Implementação de atoi ()

Eu implementei a função atoi()! Aqui está meu código:

int my_atoi(char* pointer) { int result = 0; char* pointer1; multiplier = 1; char sign = 1; if(*pointer == "-") sign =- 1; pointer1 = pointer; while(*pointer != "\0") { if(*pointer >= "0" && *pointer <= "9") multiplier = multiplier * 10; pointer = pointer + 1; } pointer = pointer1; while(*pointer != "\0") { if(*pointer >= "0" && *pointer <= "9") { result = result + ( (*pointer%48) * multiplier); multiplier = multiplier / 10; } pointer = pointer+1; } return (result * sign) / 10; } 

Eu me pergunto se há alguma maneira de melhorar minha função. Eu sei que há um problema com minha função. E se o usuário quiser converter char* em int esta string: “232-19”. O que devo fazer então? Qualquer conselho seria realmente útil!

Comentários

  • como está o problema ” string para int: 232-19 ” conectado com o código em mãos?
  • Bem, e se eu quiser converter de string para int o número -255 e por acidente eu digitar ” 8-255 “. Então, de acordo com meu algoritmo, o número 8255 será retornado. Eu sei que ‘ é muito estúpido se preocupar com essas coisas, mas e se o usuário for extremamente burro? Além disso, eu sei que é muito difícil para alguém digitar 8-255 em vez de -255, mas nunca se sabe, pode acontecer!
  • gerar um erro. o formato de entrada está com defeito. você não deve ‘ adivinhar o que o usuário queria, mas fazê-lo deixar sua intenção inequivocamente clara;)
  • Você só precisa de uma passagem da string (não duas) .
  • Por favor, não edite seu código depois de ter sido revisado para que possa tornar qualquer revisão irrelevante.

Resposta

Coisas que você pode melhorar

Variáveis / Inicialização

  • Onde você declara multiplier? Presumo que, uma vez que não é declarado no método, é declarado como uma variável global. Tente evitar variáveis globais.

    O problema com variáveis globais é que, uma vez que todas as funções têm acesso a elas, fica cada vez mais difícil descobrir quais funções realmente leem e escrevem essas variáveis.

    Para entender como o aplicativo funciona, você deve levar em consideração todas as funções que modificam o estado global. Isso pode ser feito, mas conforme o aplicativo cresce, fica mais difícil a ponto de ser virtualmente impossível (ou pelo menos uma completa perda de tempo).

    Se você não depende de variáveis globais, você pode passar o estado entre funções diferentes, conforme necessário. Dessa forma, você tem uma chance muito melhor de entender o que cada função faz, já que não precisa levar em conta o estado global.

    Então, em vez de usar variáveis globais, inicialize as variáveis em main() e passe-as como argumentos para funções, se necessário. Nesse caso, não vejo necessidade de multiplier ser usado fora da função, então simplesmente mantenha-o declarado dentro da função.

  • sign deve ser um int, e não um char .

Algoritmo

  • Neste momento, você está implementando um método complicado e difícil de seguir para converter um caractere em um número. A maneira fácil é fazer com que isdigit() faça o trabalho árduo para você. Isso também ajudará você a implementar o Princípio DRY .

    while(*pointer != "\0") { if(*pointer >= "0" && *pointer <= "9") multiplier = multiplier * 10; pointer = pointer + 1; } pointer = pointer1; while(*pointer != "\0") { if(*pointer >= "0" && *pointer <= "9") { result = result + ( (*pointer%48) * multiplier); multiplier = multiplier / 10; } pointer = pointer+1; } 

    Vê como você tem dois loops fazendo coisas quase idênticas? Veja como simplifiquei tudo isso usando isdigit().

    while (isdigit(*c)) { value *= 10; value += (int) (*c - "0"); c++; } 

    Você percorre os caracteres na string, desde que sejam dígitos. Para cada um, adicione ao contador você está mantendo – o valor a adicionar é o valor inteiro do caractere. Isso é feito subtraindo o valor ASCII de "0" do valor ascii do dígito em questão.

  • Observe que este código não “para lidar com o estouro. Se você transmitir” 89384798719061231 “(que não” caberá em um int), o resultado será indefinido. A correção é bastante simples, basta usar um long long int para mitigar isso. Ainda teremos problemas para números extremamente longos, mas consertar isso para que a função funcione conforme o esperado é um pouco mais complicado.

Documentação

  • Para onde foram todos os seus comentários? Um desenvolvedor mais recente simplesmente olharia boquiaberto para parte do seu código.

    result = result + ( (*pointer%48) * multiplier); 

    Os comentários podem realmente ajudar outras pessoas a entender seu código. Não exagere, porém, você terá que equilibrar quanto de para colocá-los em seu programa.

Sintaxe / estilo

  • Parece um erro de digitação.

    if(*pointer == "-") sign =- 1; 

    Adicione um espaço para maior clareza.

    if(*pointer == "-") sign = -1; 
  • Você deve não modifique o char* que você aceita como um parâmetro na função. Portanto, declare o parâmetro como constante.

    int my_atoi(const char* pointer) 
  • Use mais operadores abreviados.

    pointer++; // same as pointer = pointer+1; multiplier /= 10; // same as multiplier = multiplier / 10; multiplier *= 10; // same as multiplier = multiplier * 10; 

Código final

#include <stdio.h> #include <assert.h> #include <ctype.h> long long int my_atoi(const char *c) { long long int value = 0; int sign = 1; if( *c == "+" || *c == "-" ) { if( *c == "-" ) sign = -1; c++; } while (isdigit(*c)) { value *= 10; value += (int) (*c-"0"); c++; } return (value * sign); } int main(void) { assert(5 == my_atoi("5")); assert(-2 == my_atoi("-2")); assert(-1098273980709871235 == my_atoi("-1098273980709871235")); puts("All good."); // I reach this statement on my system } 

Comentários

  • Você não deve ‘ alterar os tipos de retorno arbitrariamente. atoi() tradicionalmente retorna um int, então my_atoi() também deveria. Se você deseja analisar um long long, emule strtoll().
  • isdigit(*c) não é definido para *c valores menores que 0 (diferente de EOF). Melhor para while (isdigit((unsigned char) (*c) ))
  • Canto perdido: quando my_atoi() o resultado deve ser LLONG_MIN, value += (int) (*c-'0'); é um estouro de número inteiro assinado (UB) ao tentar formar LLONG_MAX + 1.
  • Usando isdigit está totalmente errado, uma vez que não ‘ tem uma função relacionada numeric_value. Portanto, se o seu conjunto de caracteres tem dois intervalos de dígitos (0 a 9 e ٠ a ٩), os números índicos serão analisados de forma errada. Basta seguir '0' <= c && c <= '9' para estar seguro. Isso também evita o comportamento indefinido de usar a função ctype incorretamente.
  • Você perdeu um ponto importante quando escreveu ” valor ASCII de ‘ 0 ‘ ” : há ‘ s nada que diga que o conjunto de caracteres do host precisa ser ASCII (apenas que 0..9 são contíguos). É ‘ por isso que você escreve '0' em vez de um número de ponto de código específico da codificação.

Resposta

[Editar]

Exceto pelo comportamento em caso de erro, atoi() é equivalente para (int)strtol(nptr, (char **)NULL, 10). strtol() aceita espaços em branco à esquerda. OP “s my_atoi(char* pointer) não. Para remediar:

int my_atoi(const char* pointer) { while (isspace((unsigned char) *pointer)) { pointer++; } ... 

O que se segue descreve uma boa forma de lidar com INT_MIN.

OTOH, entregar valores fora de [INT_MIN...INT_MAX] não é definido pela especificação C, portanto, algumas simplificações podem ser teve. Veja mais abaixo.


Quando uma string representa INT_MIN, (vamos supor que 32 bits int) como "-2147483648", o código é executado em int estouro tentando calcular 2147483648. Uma maneira simples de resolver isso é, em vez de encontrar o valor positivo e negá-lo, abraçar o lado negativo das coisas. Ao fazer a maior parte da matemática na faixa de INT_MIN a 0, evitamos o UB. Lado negativo: alguns acham esta abordagem mais desafiadora de seguir.

Indo para um número inteiro mais amplo ou unsigned nem sempre é possível, pois o tamanho inteiro de “text- -> inteiro “a rotina pode ter o tamanho máximo. Estritamente falando, unsigned nem sempre tem um intervalo positivo mais amplo do que int. Em qualquer caso, toda a matemática pode ser tratada no tamanho inteiro assinado desejado sem recorrer a outros tipos.

#include <ctype.h> #include <limits.h> int my_atoi(const char* pointer) { // good idea to make the `const` int result = 0; while (isspace((unsigned char) *pointer)) { pointer++; } char sign = *pointer; if (*pointer == "-" || *pointer == "+") { // text could lead with a "+" pointer++; } int ch; // isdigit() expects an unsigned char or EOF, not char while ((ch = (unsigned char)(*pointer)) != 0) { if (!isdigit(ch)) break; ch -= "0"; // Will overflow occur? if ((result < INT_MIN/10) || (result == INT_MIN/10 && ch > -(INT_MIN%10))) Handle_Overflow(); result *= 10; result -= ch; // - , not + pointer++; } if (sign != "-") { if (result < -INT_MAX) Handle_Overflow(); result = -result; } return result; } 

Notas:

pointer%48 é confuso. O que há de especial em 48? Se você quer dizer "0", use pointer % "0".

“string:” 232-19 “. O que devo fazer então? ” Recomende interromper a conversão em “232” e retornar o valor 232. Pode definir errno, mas o típico atoi() função não faz muito tratamento de erros.

Em estouro, configuração errno, pode acontecer, mas, novamente, típico atoi() function não faz muito tratamento de erros. Sugerir o retorno simples de INT_MAX ou INT_MIN.

Se quiser um melhor tratamento de erros, mude para algo como o seguinte e definir um status de erro.

int my_atoi(const char *s, int *ErrorCode); 

ou localização onde as coisas terminaram. Se isso for bom, eles terminaram em "\0".

int my_atoi(const char *s, const char **endptr); 

[Editar] Simplificado: Removido detecção fora do intervalo, conforme a especificação C permite. “Se o valor do resultado não puder ser representado, o comportamento é indefinido.

int my_atoi(const char* pointer) { int result = 0; while (isspace((unsigned char) *pointer)) { pointer++; } char sign = *pointer; if (*pointer == "-" || *pointer == "+") { pointer++; } while (isdigit((unsigned char)*pointer)) { result = result*10 - (*pointer++ - "0"); } if (sign != "-") { result = -result; } return result; } 

Comentários

  • INT_MIN/10 e INT_MIN%10 exigem comportamento C99.

Resposta

 char sign = *pointer; if (*pointer == "-" || *pointer == "+") { pointer++; } 

Por que remover a referência de “ponteiro” três vezes? Uma vez é o suficiente:

 char sign = *pointer; if (sign == "-" || sign == "+") { pointer++; } 

Comentários

  • Bem-vindo à revisão do código, sua primeira resposta parece boa , Aproveite sua estadia! Embora eu me pergunte se isso faz alguma diferença no código gerado.

Resposta

se você está ok com a recursão então o código pode ser reduzido para um abaixo

#include <string.h> #include <math.h> #include <stdbool.h> int natural_number(const char* string) { int index = strlen(string) - 1; int number = pow(10, index) * (*string - "0"); return (index == 0) ? number : number + natural_number(string + 1); } int my_atoi(const char* string) { int sign = (*string == "-") ? -1 : 1; int offset = (*string == "-") ? 1 : 0; return sign * natural_number(string + offset); } /* test cases */ my_atoi("-100") == -100; my_atoi("0") == 0; my_atoi("100") == 100; 

O esgotamento da pilha pode ser mitigado por -foptimize-sibling-calls sinalizador do compilador, sendo compatível com os compiladores GCC e Clang.

Atualização:

Conforme observado pela implementação de Roland Illig não lida com entrada malformada. Se for desejado seguir de perto a atoi semântica , o próximo código deve ser ótimo não se esqueça de definir Compile Options como um nos comentários .

int digit(char symbol) { return symbol - "0"; } /* tail call optimized */ int natural_number_tc(const char* string, int number) { return !isdigit(*string) ? number : natural_number_tc(string + 1, 10 * number + digit(*string)); } int natural_number(const char* string) { return natural_number_tc(string, 0); } const char* left_trim_tc(const char* string, const char* symbol) { return !isspace(*string) ? symbol : left_trim_tc(string + 1, symbol + 1); } const char* left_trim(const char* string) { return left_trim_tc(string, string); } int my_atoi(const char* string) { const char* symbol = left_trim(string); int sign = (*symbol == "-") ? -1 : 1; size_t offset = (*symbol == "-" || *symbol == "+") ? 1 : 0; return sign * natural_number(symbol + offset); } 

Este ainda é o código do chux “onde os loops são substituídos por recursão

int result = 0; while (isdigit((unsigned char)*pointer)) { result = 10 * result + (*pointer - "0"); pointer++; } // VS int loop(const char* pointer, int result) { return !isdigit((unsigned char)*pointer) ? result : loop(pointer + 1, 10 * result + (*pointer - "0")) } 

Comentários

  • Caso de teste: buf = malloc(65536); buf[0] = '\0'; my_atoi(buf) provavelmente travará.
  • Caso de teste: bufsize = 1 << 20; buf = malloc(bufsize); memset(buf, '0', bufsize); buf[bufsize - 1] = '\0'; my_atoi(buf) levará muito muito tempo.

Resposta

Para um exercício em leetcode , escreveu o seguinte impl: atoi cpp code

 class Solution { private: bool checkMin(int a, int b=10, int c=0, int min_val=INT_MIN) { /* accepts a*b + c, min a>min; b>min; c>min check a*b+c > min or not b>0; a<0 -ive; c<0 a!=0 */ min_val = min_val -c; //std::cout<<"new min input: "<<a <<" , "<< c<<" iter: "<<b << " "<<min_val <<std::endl; //compare with a now if(a<min_val) return false; int cur_prod = 0; if(a==0) return true; for(;b>1;b--) { cur_prod += a; int curr_diff = min_val-cur_prod; /* subtraction possible because min_val<prod, min_val-prod<prod-prod min_val-prod<0 ---1 prod<0 -prod>0 min_val+(-prod )> min_val+0 [x+ (+ive quantity)>x ] min_val-prod>min_val --2 from 1, 2 min_val< min_val-prod < 0 ---3 from 3, min_val-prod can be expressed in integer check if curr_diff still can hold a deduction of a which means: curr_diff<a should hold, for a further a deduction in prod -5, -6 for ex of min_val = 59, a = -6 at b = 2 (9th iteration) prod = -54 you can"t add -6 now, since it will cross definable limit only b-1 iterations because at i-1 th iteration, ith product formation is checked */ //std::cout<<"check function for input: "<<a <<" , "<< c<<" iter: "<<b << " prod now = " //<< cur_prod << " diff = " <<curr_diff<<" is curr_dif<a "<<(curr_diff<a)<<std::endl; if(curr_diff>a) { //std::cout<<" not possible"<<std::endl; return false; } } return true; } bool checkMax(int a, int b=10, int c=0, int max_val=INT_MAX) { /* accepts a*b + c, min a<max; b<max; c<max check a*b+c < max or not b>0; a>0, c>0 */ max_val = max_val -c; //std::cout<<"new max input: "<<a <<" , "<< c<<" iter: "<<b << " "<<max_val <<std::endl; //compare with a now if(a>max_val) return false; int cur_prod = 0; if(a==0) return true; for(;b>1;b--) { cur_prod += a; int curr_diff = max_val-cur_prod; /* subtraction possible because max_val>prod, max_val-prod>prod-prod max_val-prod>0 ---1 prod>0 -prod<0 max_val+(-prod )< max_val+0 [x+ (-ive quantity)<x ] max_val-prod<max_val --2 from 1, 2 0< max_val-prod < max_val ---3 from 3, max_val-prod can be expressed in integer check if curr_diff still can hold a increment of a which means: curr_diff>a should hold, for a further a deduction in prod 5>6 fails for ex of max_val = 59, a = 6 at b = 2 (9th iteration) prod = 54 you can"t add 6 now, since it will cross definable limit only b-1 iterations because at i-1 th iteration, ith product formation is checked */ //std::cout<<"check function for input: "<<a <<" , "<< c<<" iter: "<<b << " prod now = " // << cur_prod << " diff = " <<curr_diff<<" is curr_dif<a "<<(curr_diff>a)<<std::endl; if(curr_diff<a) { //std::cout<<" not possible"<<std::endl; return false; } } return true; } public: int myAtoi(string str) { //code to trim string int i =0, end=str.length()-1; //std::cout<<i<<" "<<end<<std::endl; while(i<end && str.at(i)==" ") {i++;continue;} while(end>-1 && str.at(end)==" ") {end--;continue;} if(end<i) return 0; int sign=1; if(str.at(i)=="-") {sign = -1; i++;} else if(str.at(i)=="+") {i++;} string tr_str = str.substr(i, end-i+1); int num = 0; for(char& digit : tr_str) { if(digit<"0" || digit>"9") return num; // not convertable character - exit int c= digit-"0"; if(sign==-1) { //std::cout<<"Evaluating "<<c<<std::endl; //number cannot be lower than INT_MIN // do a check of num * 10 - c //num<0 already if(checkMin(num, 10, -c, INT_MIN)) num = num*10 -c; else { num = INT_MIN; break; } //std::cout<<"number is"<<num<<std::endl; } else { if(checkMax(num, 10, c, INT_MAX)) num = num*10 +c; else { num = INT_MAX; break; } //std::cout<<"number is"<<num<<std::endl; } } return num; } }; 

Comentários

  • Bem-vindo à revisão do código! Você apresentou uma solução alternativa, mas não ‘ t revisou o código. Explique seu raciocínio (como sua solução funciona e por que é melhor do que o original) para que o autor e outros leitores possam aprender com seu processo de pensamento.
  • o código usa um método, onde checkMin, onde não multiplicação direta é executada até que o resultado seja validado. ser maior que INT_MIN.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *