私はCを使用しているクラスにいて、インストラクターはを持っています残念ながらサンプルコードでgets()
を使用しました。
これは明らかに凶悪な見落としであり、未定義の動作やその他のさまざまな問題を引き起こす可能性があります(ほんの少しだけ) sarcasm)、 gets_s()
を実装することにしました。これは楽しい演習であり、時には価値がないためです。 fgets()
で完全なエラーチェックを実行し、予期しない長い行を切り捨てたいだけです。
これが-これは、バッファをオーバーランしないgets()
のドロップイン置換であると想定されています。
ただし、非常に重要なのは、この関数が実際にアドバタイズすることを実行することです。つまり、安全で、バッファをオーバーランしません。
これは私のfiです。初めてCで作業するとき(私は通常 java または kotlin を使用します)、すべてに感謝しますヒント。ただし、このコードの安全性について少なくともいくつか言及したいと思います。また、(現在のコンパイラへの)移植性にも関心があります。
gets_s.h
#include <stdio.h> #include <string.h> #define GETS_S_OK 0 #define GETS_S_ERROR 1 #define GETS_S_OVERRUN 2 static inline int gets_s( char str[], int n ) { char *str_end, *fgets_return; int temp; fgets_return = fgets( str, n, stdin ); /* If fgets fails, it returns NULL. This includes the case where stdin is exhausted. */ if ( fgets_return == NULL ) { str[0] = "\0"; return GETS_S_ERROR; } str_end = str + strlen(str) - 1; if ( str_end == "\n" ) { *str_end = "\0"; return GETS_S_OK; } temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK; do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" ); return GETS_S_OVERRUN; }
および小さなテストファイル:
gets_s.c
#include "gets_s.h" #include <stdio.h> int main() { char buffer[10]; int gets_s_return; printf("Enter up to %d characters safely.\n", sizeof(buffer) - 1); gets_s_return = gets_s( buffer, sizeof(buffer) ); printf("buffer = %s", buffer); printf("gets_s return = %d", gets_s_return); return 0; }
gcc -Wall -Wextra -Wpedantic -Werror gets_s.c
で正常にコンパイルされるため、「saplus。
Answer
まず、署名と動作が微妙な方法とそれほど微妙ではない方法で異なるため、gets_s
とは呼ばないでください。混乱と欲求不満につながります。とにかく、本当にはgets_s
-契約を望まないでしょう。
getline_truncated
。
n <= 0
が実装のUBであることをご存知ですか?
if (temp == EOF || temp = "\n") ^
コンパイラが上記のタイプミスについて警告していると確信しています。
または、すべての警告を要求しませんか(-Wall -Wextra -std=...
)?
お勧めしますより良い結果テストを可能にするために、リターンコードを再定義して名前を変更します:
#define GETS_S_TRUNCATED 1 // Because truncation is not neccessarily an error #define GETS_S_OK 0 #define GETS_S_ERROR EOF // Because we already have an appropriate negative constant
この方法で、または>=0
は、それぞれ「成功」であると判断した場合に応じて、それぞれ!=0
または<0
「失敗」の場合。
コメント
回答
-
コードの読み取りが多すぎる可能性があります:
temp = fgetc( stdin ); if (temp == EOF || temp = "\n") return GETS_S_OK;
temp
がEOF
でも\n
でもない場合、キャラクターが失われます。ungetc()
それをお勧めします。 -
コードはマンデートを超えています。ループ
do temp = fgetc( stdin ); while ( temp != EOF && temp != "\n" );
呼び出し元に空の文字列が表示されないことを保証します。空の文字列は意味的に意味がある場合があります。
-
man fgets
:fgets()関数とgets()関数はファイルの終わりとエラーを区別しないため、呼び出し元はfeof( 3)とferror(3)を使用して、どちらが発生したかを判別します。
これを実行するのに最適な位置にあります。盲目的に
GETS_S_ERROR
、何が起こったかを判断し、それに応じて戻ります。例:#define GETS_S_EOF 3
。
コメント
- I 'は
gets()
を模倣しています'行の最後まで読んでいます。'は、が望んでいます。 stop-at-n-minus-one-characters動作が必要な場合は、fgets()
を使用してください。または少なくとも、それがその行動を選んだ私の理由でした。 (私が'を読みすぎていると言いたい場合は、その動作を明示的に選択する理由を説明してください。私はgets()
もしそれが'バッファを超えて書き込みをしなかった場合。) - @ CAD97あなたの反対意見が私の最初の箇条書きに対処していることを理解しています。あなたのコードは確かに
gets
を模倣しており、実際に'はバッファをオーバーフローしません。私のポイントは、gets
が複数の方法で壊れているということです。getline
多かれ少なかれそれらすべてを解決します。
temp = "\n"
はこのタイプミスです。投稿のみ。元々は別のマシンでコードを記述し、質問のために再入力しました。'コードの一部であったと確信しています。 uldでエラーが発生しました(-Wall -Wextra -Wpedantic -Werror
でコンパイルしたため。==
を=
に入力すると、はるかに明確なエラーが発生します