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 amultiplier
-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 nemchar
.
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 egylong 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 egyint
-t ad vissza, tehátmy_atoi()
is. Ha elemezni akar egylong long
elemet, akkor emuláljonstrtoll()
. -
isdigit(*c)
nincs definiálva a*c
0-nál kisebb értékekhez (az EOF kivételével). Jobb awhile (isdigit((unsigned char) (*c) ))
- hiányzó sarok: Amikor az
my_atoi()
eredménynekLLONG_MIN
,value += (int) (*c-'0');
aláírt egész szám túlcsordulás (UB), amikor megpróbálja kialakítani aLLONG_MAX + 1
. - A
isdigit
egyáltalán nem stimmel, mivel nincs ‘ ehhez kapcsolódó funkciójanumeric_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_MIN
– 0
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
ésINT_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.