BrNaka Posted May 12, 2018 at 05:01 AM Share Posted May 12, 2018 at 05:01 AM Fala, galera. Boa noite! Como trabalho da faculdade, fiz este conversor de bases em C. Queria saber a opinião de vocês sobre o código em si, se estou cometendo erros ou práticas ruins ao programar e etc. Os vídeos do Fernando ("Programação Moderna em C") me ajudaram muito mesmo! Vlw, Fernando. O código está aqui em baixo, mas também vou deixar o arquivo.c em anexo! Obrigado! Bruno. #include <stdio.h> #include <math.h> #include <stdlib.h> #define SIZE 255 #define ALG 17 // Esta função converte um número decimal para a base 2, 8 ou 16. // Recebe como parâmetro um número decimal (num_dec), e a base (base) cujo número será convertido. void decToBase(int num_dec, int base) { char resto[ALG] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}, num_conv[SIZE]; int indx = 0; // Este loop realiza as operações de divisão. Os restos são guardados no vetor 'num_conv' while(num_dec > 0) { num_conv[indx] = resto[num_dec % base]; num_dec /= base; indx++; } // Exclui o caractere especial '\0' do final da string indx -= 1; // Utilizado para identificar a saída do programa switch(base) { case 2 : printf("BINÁŔIO: "); break; case 8 : printf("OCTAL: "); break; case 16 : printf("HEXADECIMAL: "); break; } // Inverte a string contida no vetor num_conv for(indx; indx >= 0; indx--) printf("%c", num_conv[indx]); printf("\n"); } // Esta função permite converter um número na base 2, 8 ou 16 para a base decimal. // Recebe como parâmetro uma string (representação do número), e um número inteiro que // determina em qual base está o número do primeiro parâmetro. void baseToDec(char* num_base, int base) { int indx = 0, exp = 0, len = 0, dec = 0, num = 0; // Este loop determina o tamanho da string while(num_base[len] != '\0') { len++; } // Neste loop, a string é percorrida de trás para frente. Cada caractere é convertido // em um número inteiro, multiplicado pela base elevada ao expoente (0..Comprimento da string) // e somado ao valor da variável 'dec', que recebe as somas sucessivas. for(exp, indx=(len-1); exp < len; exp++, indx--) { if(base == 16) { switch(num_base[indx]) { case 'A' : num = 10; break; case 'a' : num = 10; break; case 'B' : num = 11; break; case 'b' : num = 11; break; case 'C' : num = 12; break; case 'c' : num = 12; break; case 'D' : num = 13; break; case 'd' : num = 13; break; case 'E' : num = 14; break; case 'e' : num = 14; break; case 'F' : num = 15; break; case 'f' : num = 15; break; default : num = (int)num_base[indx] - (int)'0'; } } else num = (int)num_base[indx] - (int)'0'; // Aqui avalia se o usuário digitou um número válido ou não! if(num > (base-1) || num < 0) { printf("Você informou um número inválido!\n"); exit(0); } dec += num * pow(base, exp); } // O número convertido para decimal é exibido printf("DECIMAL: %d\n", dec); } // Função que imprime o menu da calculadora void menu(void) { printf("\n===========================================================\n"); printf("\nBEM-VINDO AO CONVERSOR DE BASES NUMÉRICAS\n\n"); printf("QUAL OPERAÇÃO DESEJA REALIZAR? \n\n"); printf("[ 1 ] BINÁRIO -> DECIMAL\n"); printf("[ 2 ] DECIMAL -> BINÁRIO\n"); printf("[ 3 ] OCTAL -> DECIMAL\n"); printf("[ 4 ] DECIMAL -> OCTAL\n"); printf("[ 5 ] HEXADECIMAL -> DECIMAL\n"); printf("[ 6 ] DECIMAL -> HEXADECIMAL\n"); printf("[ 0 ] SAIR\n"); printf("\n===========================================================\n"); } // Função que chama as funções correspondentes à opção escolhida void process_calc(int opt) { int number; char str[SIZE]; switch(opt) { case 0 : printf("CALCULADORA ENCERRADA!\n"); exit(0); case 1 : printf("BINÁRIO: "); scanf("%s", str); baseToDec(str, 2); break; case 2 : printf("DECIMAL: "); scanf("%d", &number); decToBase(number, 2); break; case 3 : printf("OCTAL = "); scanf("%s", str); baseToDec(str, 8); break; case 4 : printf("DECIMAL = "); scanf("%d", &number); decToBase(number, 8); break; case 5 : printf("HEXADECIMAL = "); scanf("%s", str); baseToDec(str, 16); break; case 6 : printf("DECIMAL: "); scanf("%d", &number); decToBase(number, 16); break; default : printf("OPÇÃO NÃO PERMITIDA!\nTENTE NOVAMENTE\n"); break; } } int main(void) { int opcao; menu(); printf("QUAL OPERAÇÃO DESEJA REALIZAR? "); scanf("%d", &opcao); process_calc(opcao); return 0; } numBases_conv.c Link to comment Share on other sites More sharing options...
gzn Posted May 12, 2018 at 10:09 PM Share Posted May 12, 2018 at 10:09 PM Ficou bom o código, obrigado por compartilhar! No trecho: case 'A' : num = 10; break; case 'a' : num = 10; break; Será que daria para usar só um break? case 'A' : num = 10; case 'a' : num = 10; break; Ou então talvez usar a função tolower/toupper (ctype.h) . Talvez ficaria mais elegante, não sei rsrs Ah, também poderia ter usado funções como strlen: size_t tam = strlen(array); Link to comment Share on other sites More sharing options...
Administrators Fernando Mercês Posted May 14, 2018 at 02:38 AM Administrators Share Posted May 14, 2018 at 02:38 AM Dá pra tratar os dois casos juntos também: case 'a': case 'A': num=10; break; Sendo minimalista hehe: case 'a': case 'A': num=10; break; Mas a ideia de converter pra maiúscula ou minúscula antes de comparar é boa também. Outra ideia é, sabendo que 'a' é 97, você pode substituir todo esse switch por uma única linha: num = tolower(num_base[indx]) - 87; Se quiser pode tratar num if entre 'a' e 'f' também. Só lembrar que tudo é número. ? Abraço! Link to comment Share on other sites More sharing options...
BrNaka Posted May 14, 2018 at 10:57 PM Author Share Posted May 14, 2018 at 10:57 PM Obrigado pelas dicas, Fernando e gzn! Eu também achei que o uso desse 'switch' ficou um pouco deselegante haha Mas estava funcionando, então nem toquei kkk Abraço! ? Link to comment Share on other sites More sharing options...
Supporter - Nibble Lucas Rodrigues Posted May 15, 2018 at 10:28 PM Supporter - Nibble Share Posted May 15, 2018 at 10:28 PM Tem uma outra forma de fazer essa conversão que acho sensacional, que simplesmente usa bitwise e codifica em HEX baseado no ASCII. Essa forma é mais segura contra cache timing attack, inclusive versões similares são usados pela BoringSSL para decodificar os certificados e tal, o negativo é a performance, que é bizarramente mais lenta. Eu não sou nada bom em C, mas fiz um esforço para sair alguma coisa razoável, se tiver sugestão de melhorias eu agradeço. #include <stdio.h> #include <math.h> #include <stdlib.h> #include <stdint.h> #define SIZE 255 struct hexResult { uint64_t i; uint64_t e; }; struct hexResult hexToDec(char str[]) { struct hexResult r = {0, 0}; for (int i = 0; i < _mbstrlen(str); i++) { uint64_t b = str[i] - (uint64_t) 48; b -= (((uint64_t) 9 - b) >> (uint64_t) 8) & (uint64_t) 7; b -= (((uint64_t) 41 - b) >> (uint64_t) 8) & (uint64_t) 32; r.e |= ((b >> (uint64_t) 8) | ((uint64_t) 15 - b) >> (uint64_t) 8) & (uint64_t) 1; r.i |= b << (uint64_t) (4 * i); } return r; } int main(void) { int opcao; char str[SIZE]; printf("Insira o hexadecimal: "); scanf("%s", str); struct hexResult result = hexToDec(str); if (result.e) { printf("%s", "Caracteres invalidos foram detectados"); } printf("%lld", result.i); return 0; } Isso é só para decodificar o hexadecimal para uint64. Acredito que o resto possa ser feito da mesma forma. A ideia é bem simples, basicamente. Assumimos que "A" é 10, assim como "1" é 1. O "A" no ASCII é o 65 (em inteiro), portanto se fizermos 65-55 teremos exatamente o 10, assim como "1" que em ASCII é o 49, podemos fazer simplesmente 49-48 que daria 1, exatamente o mesmo valor que o "1" vale em hexadecimal. Para evitar usar o if, para mitigar timing-attack (e similares) o que se faz é compara se o número é menor ou maior usando bitwise. Neste trecho especifico: b = str[i] - (uint64_t) 48; Todos os caracteres serão subtraídos por 48. Dessa forma o "1" será 1, assim com o "9" será 9. Porém, o "A" será 17, porque existe uma lacuna no ASCII entre o números e letras, por isso existe a segunda linha: b -= (((uint64_t) 9 - b) >> (uint64_t) 8) & (uint64_t) 7; Então, se o número for maior que 9 ele será um número negativo. Veja que 9-10 = -1, mas 9 - 9 = 0 e 9-8 = 1. Então o que queremos é saber se ele é ou não maior do que 9. Para isso fazemos o shift para pegar o resto dos bits, após os 7 primeiros bits, já que o numero nunca irá exceder 7 bits. Se ele for negativo o bit do sinal de 1 será propagado pelo shift, ficando 11111111.... O and entre o -1 & 7 irá funcionar, assim ficará b -= 7. Porém, se for positivo, todos os bits a esquerda serão 0, então o shift também sera tudo 0, e consequentemente o and não irá funcionar, assim ficará b -= 0 & 7 que será b -= 0; A mesma lógica se aplica aos demais casos. Isso é apenas uma outra forma de resolver, sem usar if e nem tabelas. Eu não sei há outros erros, lembre-se que não estou acostumado com o C, e não uso ele. Link to comment Share on other sites More sharing options...
BrNaka Posted May 16, 2018 at 04:26 AM Author Share Posted May 16, 2018 at 04:26 AM Obrigado pela resposta, Lucas Rodrigues! Link to comment Share on other sites More sharing options...
Joel Antonio Posted November 22, 2020 at 01:59 PM Share Posted November 22, 2020 at 01:59 PM ola bruno eu sou o joel estou a estudar aqui em portugal eu acho que o codico ficou bem Link to comment Share on other sites More sharing options...
fredericopissarra Posted November 22, 2020 at 05:14 PM Share Posted November 22, 2020 at 05:14 PM Um exemplo do uso de strtoul() para conversão de bases (qualquer entre 2 e 36) para decimal: /* base.c Exemplo de uso de strtoul() para converter uma string em um valor inteiro de acordo com uma base numérica. O programa aceita apenas 2 argumentos: A string contendo o "valor" e a base desejada (em decimal). Exemplo: $ ./base 1010 2 10 */ #include <stdio.h> #include <stdlib.h> #include <libgen.h> #include <errno.h> int main ( int argc, char *argv[] ) { unsigned long base, n; char *p; if ( argc != 3 ) { fprintf ( stderr, "Usage: %s <string> <base>\n", basename ( argv[0] ) ); return EXIT_FAILURE; } errno = 0; base = strtoul ( argv[2], &p, 10 ); if ( *p || errno == ERANGE || base < 2 || base > 36 ) { fputs ( "ERRO: Base numérica precisa estar entre 2 e 36.\n", stderr ); return EXIT_FAILURE; } errno = 0; n = strtoul ( argv[1], &p, base ); if ( *p || errno == ERANGE ) { fputs ( "ERRO: Valor a ser convertido errado ou fora da faixa.\n", stderr ); return EXIT_FAILURE; } printf ( "%lu\n", n ); return EXIT_SUCCESS; } Compilando e testando: $ cc -o base base.c $ ./base Usage: base <string> <base> $ ./base 1010 2 10 $ ./base 1a 2 ERRO: Valor a ser convertido errado ou fora da faixa. $ ./base 1a 16 26 Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.