atoi()
함수를 구현했습니다! 내 코드는 다음과 같습니다.
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; }
내 기능을 개선 할 수있는 방법이 있는지 궁금합니다. 내 기능에 문제가 있다는 것을 알고 있습니다. 사용자가 char*
에서 int
문자열 : “232-19″로 변환하려는 경우 어떻게해야합니까? 그러면 어떻게해야합니까? 모든 조언이 정말 도움이 될 것입니다!
댓글
- int에 대한 ” 문자열의 문제 : 232-19 ” 코드와 연결되어 있습니까?
- 문자열에서 정수 -255로 변환하고 실수로 ” 8-255 “. 그런 다음 내 알고리즘에 따라 숫자 8255가 반환됩니다. 알아요 ‘는 이러한 것들에 대해 걱정하는 것은 꽤 어리석은 일이지만 사용자가 극도로 멍청하다면 어떨까요? 또한 누군가가 -255 대신에 8-255를 입력하는 것이 정말 어렵다는 것을 알고 있지만 여러분은 결코 알지 못합니다!
- 오류가 발생합니다. 입력 형식이 잘못되었습니다. ‘ 사용자가 원하는 것이 무엇인지 추측해서는 안되지만 사용자의 의도를 분명하게 밝혀야합니다.)
- 문자열을 한 번만 전달하면됩니다 (두 번이 아님). .
- 리뷰가 관련성이 없게 될 수 있도록 코드를 검토 한 후에 수정하지 마십시오.
답변
개선 할 수있는 사항
변수 / 초기화
-
어디에
multiplier
? 메서드 내에서 선언되지 않았기 때문에 전역 변수로 선언되었다고 가정합니다. 전역 변수를 피하십시오.전역 변수의 문제는 모든 함수가 이러한 변수에 액세스 할 수 있기 때문에 실제로 이러한 변수를 읽고 쓰는 함수를 파악하기가 점점 더 어려워진다는 것입니다.
어플리케이션이 어떻게 작동하는지 이해하려면 전역 상태를 수정하는 모든 기능을 고려해야합니다. 그렇게 할 수는 있지만 응용 프로그램이 커짐에 따라 사실상 불가능 (또는 최소한 시간 낭비) 할 정도로 어려워집니다.
글로벌 변수에 의존하지 않는 경우 필요에 따라 서로 다른 함수간에 상태를 전달할 수 있습니다. 이렇게하면 전역 상태를 고려할 필요가 없기 때문에 각 함수가 수행하는 작업을 훨씬 더 잘 이해할 수 있습니다.
따라서 사용하는 대신 전역 변수,
main()
에서 변수를 초기화하고 필요한 경우 함수에 인수로 전달합니다. 이 경우 함수 외부에서multiplier
를 사용할 필요가 전혀 없으므로 함수 내에서 선언 된 상태로 유지하면됩니다. -
sign
는char
가 아닌int
여야합니다. .
알고리즘
-
지금은 문자를 숫자로 변환하는 복잡하고 따르기 어려운 방법을 구현하고 있습니다. 쉬운 방법은
isdigit()
가 어려운 작업을 수행하도록하는 것입니다. 이렇게하면 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; }
두 개의 루프가 거의 동일한 작업을 수행하는 방법을 확인하세요. 다음은
isdigit()
를 사용하여 모든 것을 단순화 한 방법입니다.while (isdigit(*c)) { value *= 10; value += (int) (*c - "0"); c++; }
문자열의 문자가 숫자 인 경우 반복합니다. 각각에 대해 카운터에 추가합니다. 당신은 유지하고 있습니다-추가 할 값은 문자의 정수 값입니다. 이는 해당 숫자의 ASCII 값에서
"0"
의 ASCII 값을 빼서 수행됩니다. -
이 코드는 그렇지 않습니다. “오버플로를 처리하지 않습니다.”89384798719061231 “(
int
에 맞지 않음)을 전달하면 결과가 정의되지 않습니다. 수정은 간단합니다.long long int
를 사용하여이를 완화하세요. 매우 긴 숫자에 대해서는 여전히 문제가 있지만 함수가 의도 한대로 작동하도록 수정하는 것은 조금 더 복잡합니다.
문서
-
모든 댓글은 어디로 갔나 요? 신참 개발자는 코드 일부를 훑어 볼뿐입니다.
result = result + ( (*pointer%48) * multiplier);
주석은 다른 사람이 귀하의 코드를 이해하는 데 큰 도움이 될 수 있습니다. 주석을 너무 많이 사용하지 마십시오. 프로그램에 추가 할 수 있습니다.
구문 / 스타일링
-
오타처럼 보입니다.
if(*pointer == "-") sign =- 1;
명확성을 위해 공백을 추가합니다.
if(*pointer == "-") sign = -1;
-
그러면 안 함수의 매개 변수로 허용하는
char*
를 수정합니다. 따라서 매개 변수를 상수로 선언하세요.int my_atoi(const char* pointer)
-
더 많은 속기 연산자를 사용하세요.
pointer++; // same as pointer = pointer+1; multiplier /= 10; // same as multiplier = multiplier / 10; multiplier *= 10; // same as multiplier = multiplier * 10;
최종 코드
#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 }
댓글
- ‘ 반환 유형을 임의로 변경해서는 안됩니다.
atoi()
는 일반적으로int
를 반환하므로my_atoi()
도 반환해야합니다.long long
를 파싱하려면strtoll()
를 에뮬레이션합니다. -
isdigit(*c)
는 0보다 작은*c
값 (EOF 제외)에 대해 정의되지 않습니다.while (isdigit((unsigned char) (*c) ))
- 누락 된 모서리 :
my_atoi()
결과가LLONG_MIN
,value += (int) (*c-'0');
는LLONG_MAX + 1
를 형성하려고 할 때 부호있는 정수 오버플로 (UB)입니다. -
isdigit
는 관련 함수가 없기 때문에 ‘numeric_value
전혀 잘못되었습니다. 따라서 문자 집합에 두 개의 숫자 범위 (0 ~ 9 및 ٠ ~ ٩)가있는 경우 인도 숫자가 잘못 구문 분석됩니다. 안전을 위해'0' <= c && c <= '9'
를 고수하십시오. 또한 정의되지 않은 동작이 ctype 함수를 잘못 사용하는 것을 방지합니다. - iv id =의 ” ASCII 값을 작성할 때 중요한 점을 놓쳤습니다. “c9258faf4a”>
가 더 좋습니다.
0 ‘ ” : 저기 ‘ s 호스트 문자 집합이 ASCII 여야한다는 내용은 없습니다 (0..9 만 연속적 임). 이것이 ‘ 인코딩 별 코드 포인트 번호가 아닌'0'
를 작성하는 이유입니다.
답변
[편집]
오류 동작을 제외하고는 atoi()
는 동일합니다. (int)strtol(nptr, (char **)NULL, 10)
로. strtol()
는 선행 공백을 허용합니다. OP s my_atoi(char* pointer)
는 그렇지 않습니다. 해결 방법 :
int my_atoi(const char* pointer) { while (isspace((unsigned char) *pointer)) { pointer++; } ...
아래는 INT_MIN
.
OTOH, [INT_MIN...INT_MAX]
외부의 값을 전달하는 것은 C 사양에 의해 정의되지 않으므로 일부 단순화는 다음과 같습니다. 아래를 참조하십시오.
문자열이 INT_MIN
를 나타내는 경우 (32 비트 int
) (예 : "-2147483648"
), 코드가 int
오버플로로 실행되어 2147483648
. 이를 해결하는 간단한 방법은 양의 값을 찾은 다음이를 부정하는 것보다 부정적인 측면 을 수용하는 것입니다. INT_MIN
에서 0
범위에서 수학의 가장 큰 부분을 차지함으로써 UB를 피할 수 있습니다. 단점 : 일부는이 접근 방식을 따르기가 더 어렵다고 생각합니다.
더 넓은 정수 또는 unsigned
“텍스트-“의 정수 크기로 항상 가능하지는 않습니다. -> 정수 “루틴이 최대 크기 일 수 있습니다. 엄밀히 말하면 unsigned
가 항상 int
보다 더 넓은 양의 범위를 갖는 것은 아닙니다. 어쨌든 모든 수학은 다른 유형에 의존하지 않고 원하는 부호있는 정수 크기로 처리 할 수 있습니다.
#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; }
참고 :
pointer%48
는 혼란 스럽습니다. 48의 특별한 점은 무엇입니까? "0"
를 의미하는 경우 pointer % "0"
를 사용합니다.
“string :”232-19 “. 어떻게해야합니까? 그럼? ” 232에서 전환을 중지하고 232 값을 반환하는 것이 좋습니다. errno
를 설정할 수 있지만 일반적인 atoi()
함수는 오류 처리를 너무 많이하지 않습니다.
오버플로시 errno
설정이 발생할 수 있지만 다시 한 번 일반적인 atoi()
함수는 오류 처리를 너무 많이하지 않습니다. 간단한 반환 INT_MAX
또는 INT_MIN
를 제안합니다.
더 나은 오류 처리를 원하면 다음과 같이 변경하고 오류 상태를 설정합니다.
int my_atoi(const char *s, int *ErrorCode);
또는 위치 일이 끝난 곳. 이것이 좋은 경우 "\0"
에서 끝났습니다.
int my_atoi(const char *s, const char **endptr);
[편집] 단순화 : 제거됨 C 스펙이 허용하는 범위를 벗어난 감지. “결과 값을 표현할 수 없으면 동작이 정의되지 않은 것입니다.
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; }
댓글
-
INT_MIN/10
및INT_MIN%10
에는 C99 동작이 필요합니다.
답변
char sign = *pointer; if (*pointer == "-" || *pointer == "+") { pointer++; }
“포인터”를 역 참조하는 이유 세 번? 한 번이면 충분합니다.
char sign = *pointer; if (sign == "-" || sign == "+") { pointer++; }
댓글
- 코드 검토에 오신 것을 환영합니다. 첫 번째 답변이 좋아 보입니다. , 체류를 즐길 수! 생성 된 코드에 차이가 있는지 궁금합니다.
답변
재귀에 문제가없는 경우 그런 다음 코드를 아래로 줄일 수 있습니다.
#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;
스택 고갈은 -foptimize-sibling-calls
컴파일러 플래그로 완화 할 수 있습니다. GCC 및 Clang 컴파일러 모두에서 지원됩니다.
업데이트 :
설명 된대로 by Roland Illig 구현은 잘못된 입력을 처리하지 않습니다. atoi
의미 를 자세히 따르고 싶다면 다음 코드는 fine 댓글에서 Compile Options
를 1로 설정하는 것을 잊지 마세요 .
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); }
이것은 루프가 재귀로 대체되는 여전히 chux “의 코드입니다.
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")) }
댓글
- 테스트 케이스 :
buf = malloc(65536); buf[0] = '\0'; my_atoi(buf)
가 충돌 할 수 있습니다. - 테스트 케이스 :
bufsize = 1 << 20; buf = malloc(bufsize); memset(buf, '0', bufsize); buf[bufsize - 1] = '\0'; my_atoi(buf)
매우 시간이 오래 걸립니다.
답변
leetcode , 다음 impl 작성 : atoi cpp 코드
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; } };
댓글
- 코드 검토에 오신 것을 환영합니다! 대체 솔루션을 제시했지만 ‘ 코드를 검토하지 않았습니다. 작성자와 다른 독자가 사고 과정에서 배울 수 있도록 추론 (솔루션의 작동 방식과 원본보다 나은 이유)을 설명해주십시오.
- 코드는 방법을 사용합니다. 여기서 checkMin은 그렇지 않습니다. 결과가 검증 될 때까지 직접 곱셈이 수행됩니다. INT_MIN보다 커야합니다.