Az atoi ()

megvalósítása a atoi() függvényt! Itt van a kódom:

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; } 

Kíváncsi vagyok, lehet-e valamilyen módon javítani a funkciómon. Tudom, hogy probléma van a funkciómmal. Mi van akkor, ha a felhasználó char* -ről int ezt a karakterláncot szeretné átalakítani: “232-19”. Mit tegyek akkor? Minden tanács nagyon hasznos lenne!

Megjegyzések

  • hogyan áll a probléma ” karakterlánc az int-hez: 232-19 ” kapcsolódik a kéznél lévő kódhoz?
  • Nos, mi van, ha konvertálni akarok string-ről -255 int számra, és véletlenül beírom a ” 8-255 “. Ezután algoritmusom szerint a 8255 szám kerül visszaadásra. Tudom, hogy ‘ nagyon hülye aggódni ezekért a dolgokért, de mi van, ha a felhasználó rendkívül buta? Továbbá tudom, hogy valakinek nagyon nehéz a -255 helyett a 8-255-et beírni, de soha nem lehet tudni, ez előfordulhat!
  • hibát vet fel. a beviteli formátum hibás. nem szabad ‘ kitalálni, hogy mit akar a felhasználó, hanem tegye félreérthetetlenül egyértelművé szándékát;)
  • A karakterláncból csak egy lépés szükséges (nem kettő) .
  • Kérjük, ne módosítsa a kódot, miután felülvizsgálták, hogy minden felülvizsgálata lényegtelenné válhasson.

Válasz

Amit javíthatna

Változók / Inicializálás

  • Hol nyilatkozik multiplier? Feltételezem, hogy mivel a módszeren belül nincs deklarálva, globális változóként van deklarálva. Próbáljon kerülni a globális változókat.

    A globális változók problémája az, hogy mivel minden funkció hozzáfér ezekhez, egyre nehezebb kideríteni, hogy mely függvények olvassák és írják ezeket a változókat.

    Az alkalmazás működésének megértéséhez nagyjából figyelembe kell venni minden olyan funkciót, amely módosítja a globális állapotot. Ez megtehető, de az alkalmazás növekedésével egyre nehezebbé válik, hogy gyakorlatilag lehetetlenné váljon (vagy legalábbis teljes időpazarlás).

    Ha nem támaszkodik globális változókra, akkor szükség szerint átadhatja az állapotot a különböző funkciók között. Így sokkal nagyobb esélye van megérteni az egyes funkciók működését, mivel nem kell figyelembe vennie a globális állapotot.

    Tehát ahelyett, hogy használná globális változók, inicializálja a változókat az main() fájlban, és szükség esetén adja át argumentumként a függvényeknek. Ebben az esetben nem látom annak szükségességét, hogy a multiplier -t egyáltalán a függvényen kívül használják, ezért egyszerűen tartsa deklarálva a függvényen belül.

  • sign int legyen, és nem char .

Algoritmus

  • Jelenleg egy bonyolult és nehezen követhető módszert vezet be, amellyel egy karaktert számgá alakíthat. A legegyszerűbb módja az, hogy isdigit() elvégzi a kemény munkát az Ön számára. Ez is segít a SZÁRÍTÁS elve .

    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; } 

    Látja, hogy van két hurok, amelyek szinte azonos dolgokat csinálnak? Így egyszerűsítettem mindezt a isdigit() használatával.

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

    Végiglapozhatja a karakterlánc karaktereit, amíg azok számjegyek. Mindegyikhez adja hozzá a számlálóhoz megtartja – a hozzáadandó érték a karakter egész értéke. Ez úgy történik, hogy kivonja az "0" ASCII értékét a kérdéses számjegy ascii értékéből.

  • Ne feledje, hogy ez a kód nem “t kezelni a túlcsordulást. Ha átadja a” 89384798719061231 “szöveget (amely” nem fér bele egy int fájlba), az eredmény nincs meghatározva. A javítás elég egyszerű, csak enyhítésként használjon egy long long int t. Még mindig vannak problémáink rendkívül hosszú számokkal kapcsolatban, de egy kicsit bonyolultabb annak kijavítása, hogy a függvény rendeltetésszerűen működjön.

Dokumentáció

  • Hová lettek az összes megjegyzésed? Egy újabb fejlesztő egyszerűen megnézi néhány kódodat.

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

    A megjegyzések valóban sokat segíthetnek abban, hogy más is megértse a kódodat. De ne lépd túl őket velük, ki kell egyensúlyoznod, hogy őket a programjába.

Szintaxis / stílus

  • Ez elírási hibának tűnik.

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

    Helyezzen el egy helyet az érthetőség érdekében.

    if(*pointer == "-") sign = -1; 
  • nem módosítsa a függvény paramétereként elfogadott char* fájlját. Ezért nyilvánítsa a paramétert állandónak.

    int my_atoi(const char* pointer) 
  • Használjon több gyorsírás-operátort.

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

Végleges kódex

#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 } 

Megjegyzések

  • Nem szabad ‘ megváltoztatni a visszatérési típusokat önkényesen. A atoi() hagyományosan egy int -t ad vissza, tehát my_atoi() is. Ha elemezni akar egy long long elemet, akkor emuláljon strtoll().
  • isdigit(*c) nincs definiálva a *c 0-nál kisebb értékekhez (az EOF kivételével). Jobb a while (isdigit((unsigned char) (*c) ))
  • hiányzó sarok: Amikor az my_atoi() eredménynek LLONG_MIN, value += (int) (*c-'0'); aláírt egész szám túlcsordulás (UB), amikor megpróbálja kialakítani a LLONG_MAX + 1.
  • A isdigit egyáltalán nem stimmel, mivel nincs ‘ ehhez kapcsolódó funkciója numeric_value. Ezért, ha a karakterkészletnek két számjegytartománya van (0 és 9, valamint ٠ és ٩ között), akkor az indikus számokat rosszul elemzik. Csak biztonságban maradjon a '0' <= c && c <= '9' oldalon. Ezzel elkerülhető a meghatározatlan viselkedés a ctype függvény helytelen használata esetén is.
  • Egy fontos pontot elmulasztott, amikor a iv id = ” ASCII értéket írta. “c9258faf4a”>

0 ‘ ” : ott ‘ s semmi, ami azt mondja, hogy a gazdagép-karakterkészletnek ASCII-nek kell lennie (csak az, hogy a 0..9 összefüggő). Ez az, amiért ‘ ezért írsz'0', mint kódolás-specifikus kódpont-számot.

Válasz

[Szerkeszt]

A hibás viselkedés kivételével a atoi() egyenértékű hogy (int)strtol(nptr, (char **)NULL, 10). strtol() elfogadja a vezető fehér helyet. Az OP “s my_atoi(char* pointer) nem. Javítás:

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

Az alábbiakban leírjuk a INT_MIN.

OTOH, a [INT_MIN...INT_MAX] kívüli értékek átadását a C specifikáció nem határozza meg, ezért néhány egyszerűsítés lehetséges Lásd messze alább.


Amikor egy karakterlánc a INT_MIN -t ábrázolja, (tegyük fel, hogy 32 bites int), például "-2147483648", a kód int túlcsordulásba ütközik, és megpróbálja kiszámolni a 2147483648. Ennek megoldásának egyszerű módja az, hogy nem a pozitív értéket találja meg, majd pedig tagadja, a negatív oldal felkarolása. Ha a matematika oroszlánrészét a INT_MIN0 tartományban végezzük, elkerüljük az UB-t. Lefelé: egyesek ezt a megközelítést nehezebben követik.

Szélesebb egész számra vagy unsigned áttérve ez nem mindig lehetséges a “text- -> integer “rutin lehet a maximális méret. Szigorúan véve a unsigned nem mindig szélesebb pozitív tartományú, mint az int. Mindenesetre az összes matematika a kívánt aláírt egész számmérettel kezelhető anélkül, hogy más típusokat kellene igénybe venni.

#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; } 

Megjegyzések:

A pointer%48 zavaró. Mi a különleges a 48-ban? Ha "0" -re gondol, akkor használja a pointer % "0" -et.

“string:” 232-19 “. Mit tegyek? akkor csináld? ” Javasoljuk, hogy állítsa le a konverziót a “232” ponton, és adja vissza a 232 értéket. Beállíthat errno, de a tipikus atoi() A függvény nem végez túl sok hibakezelést.

Túlcsorduláskor előfordulhat, hogy a errno beállítást megadjuk, de megint tipikus atoi() függvény nem végez túl sok hibakezelést. Javasoljon egyszerű visszatérést: INT_MAX vagy INT_MIN.

Ha jobb hibakezelést szeretne, váltson a következőkre: állítson be hibaállapotot.

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

vagy hely hol végződtek a dolgok. Ha ez jó, akkor a "\0" oldalon végződött.

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

[Szerkesztés] Egyszerűsítve: Eltávolítva tartományon kívüli észlelés, mivel a C spec ezt megengedi. “Ha az eredmény értéke nem ábrázolható, a viselkedés nincs meghatározva.

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; } 

Megjegyzések

  • INT_MIN/10 és INT_MIN%10 C99 viselkedést igényel.

Válasz

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

Miért hivatkozás visszavonása a “mutatóra” háromszor? Egy idő elegendő:

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

megjegyzések

  • Üdvözöljük a Code Review szolgáltatásban, első válasza jól néz ki , kellemes időtöltést! Bár kíváncsi vagyok, vajon jelent-e különbséget a létrehozott kódban.

Válasz

ha rendben van a rekurzió akkor a kódot lerövidítheti egy alulra.

#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; 

A verem kimerülését a -foptimize-sibling-calls fordító jelzővel lehet enyhíteni. a GCC és a Clang fordítók is támogatják.

Frissítés:

Mint megjegyeztük a Roland Illig által végrehajtás nem kezeli a hibásan bevitt bemeneteket. Ha kívánatos, kövesse az atoi szemantikát , akkor a következő kódnak finom ne felejtsd el a Compile Options beállítást egy megjegyzésben .

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); } 

Ez továbbra is a chux kódja, ahol a hurkok rekurzióval vannak helyettesítve

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")) } 

Megjegyzések

  • Teszteset: buf = malloc(65536); buf[0] = '\0'; my_atoi(buf) valószínűleg összeomlik.
  • Teszteset: bufsize = 1 << 20; buf = malloc(bufsize); memset(buf, '0', bufsize); buf[bufsize - 1] = '\0'; my_atoi(buf) nagyon hosszú időbe telik.

Válasz

Egy leetcode , a következő impl-t írta: atoi cpp kód

 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; } }; 

Megjegyzések

  • Üdvözöljük a Code Review oldalán! Ön bemutatott egy alternatív megoldást, de még ‘ nem tekintette át a kódot. Kérjük, magyarázza el az érvelését (hogyan működik a megoldása és miért jobb az eredetinél), hogy a szerző és más olvasók tanulhassanak a gondolkodási folyamatából.
  • a kód egy módszert használ, ahol a checkMin, ahol nem közvetlen szorzást végeznek az eredmény érvényesítéséig. hogy nagyobb legyen, mint az INT_MIN.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük