BrNaka 3 Posted May 12, 2018 Share Posted May 12, 2018 (edited) 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 Edited March 6, 2020 by BrNaka 1 Quote Link to post Share on other sites
gzn 39 Posted May 12, 2018 Share Posted May 12, 2018 (edited) 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); Edited May 12, 2018 by gzn +strlen 1 Quote Link to post Share on other sites
Administradores Fernando Mercês 0 Posted May 14, 2018 Administradores Share Posted May 14, 2018 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! Quote Link to post Share on other sites
BrNaka 3 Posted May 14, 2018 Author Share Posted May 14, 2018 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! ? Quote Link to post Share on other sites
Apoiador Nibble Lucas Rodrigues 4 Posted May 15, 2018 Apoiador Nibble Share Posted May 15, 2018 (edited) 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. Edited May 16, 2018 by Lucas Rodrigues 2 Quote Link to post Share on other sites
BrNaka 3 Posted May 16, 2018 Author Share Posted May 16, 2018 Obrigado pela resposta, Lucas Rodrigues! Quote Link to post Share on other sites
Joel Antonio 0 Posted November 22, 2020 Share Posted November 22, 2020 ola bruno eu sou o joel estou a estudar aqui em portugal eu acho que o codico ficou bem Quote Link to post Share on other sites
fredericopissarra 276 Posted November 22, 2020 Share Posted November 22, 2020 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 1 Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.