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を入力するのは本当に難しいことを私は知っていますが、あなたは決して知りません、それは起こるかもしれません!
- エラーを発生させます。入力フォーマットに問題があります。 ‘ユーザーが何を望んでいるかを推測するべきではありませんが、ユーザーの意図を間違いなく明確にする必要があります;)
- 必要なのは文字列のパスを1つだけ(2つではない)です。 。
- レビューが無関係になる可能性があるため、レビュー後にコードを編集しないでください。
回答
改善できる点
変数/初期化
-
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; }
2つのループがほぼ同じことを行う方法を確認しますか?
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)になります。 -
は、関連する関数
numeric_value
がないため、まったく間違っています。’したがって、文字セットに2つの数字の範囲(0から9、および٠から٩)がある場合、Indic番号は誤って解析されます。安全のため、'0' <= c && c <= '9'
に固執するだけです。これにより、未定義の動作がctype関数を誤って使用することも回避されます。 - ” ‘ 0 ‘ ” :そこに’ホスト文字セットがASCIIである必要があることを示すものは何もありません(0..9のみが連続しています)。そのため、’は、エンコーディング固有のコードポイント番号ではなく、
'0'
と書く理由です。
回答
[編集]
エラー時の動作を除いて、atoi()
は同等です(int)strtol(nptr, (char **)NULL, 10)
へ。 strtol()
は先頭の空白を受け入れます。 OPの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
に移動すると、「text-」の整数サイズとして常に可能であるとは限りません。 ->整数 “ルーチンが最大サイズになる場合があります。厳密に言えば、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++; }
「ポインタ」を参照解除する理由3回? 1回で十分です:
char sign = *pointer; if (sign == "-" || sign == "+") { pointer++; }
コメント
- コードレビューへようこそ、最初の回答は良さそうです、 滞在を楽しんで!生成されたコードに違いがあるのではないかと思いますが。
回答
再帰に問題がないかどうか次に、コードを以下の1つに短縮できます。
#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コンパイラの両方でサポートされています。
更新:
前述のとおり Roland Illig の実装では、不正な入力は処理されません。 atoi
semantics に厳密に従うことが必要な場合は、次のコードは 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 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; } };
コメント
- コードレビューへようこそ!別の解決策を提示しましたが、’コードを確認していません。著者や他の読者があなたの思考プロセスから学ぶことができるように、あなたの推論(あなたの解決策がどのように機能し、なぜそれが元の解決策よりも優れているのか)を説明してください。結果が検証されるまで、直接乗算が実行されます。 INT_MINより大きくなります。