1

次のコマンドを使用して、Ubuntu 4.6.1 および SUSE 4.6.2 で gcc を使用しています。

gcc gets_s.c

私のソースコードは

// Read and Display Lines
// gets_s.c

#include <stdio.h>

int main(void)
{

    char first_name[11]; 
    char last_name[11]; 

    printf("First Name : ");
    gets_s(first_name, 11);
    printf("Last Name  : ");
    gets_s(last_name, 11);
    puts(first_name);
    puts(last_name);

    return 0;

}

私の質問について詳しく説明します。

私にとっての主な問題は、入力された行と保存された行の間の 1 対 1 の対応です。

成功した場合、fgets と gets_s の違いは、fgets には改行ターミネータが含まれているのに対し、gets_s は改行ターミネータを null ターミネータに置き換えて、行入力と gets_s への成功した呼び出しとの間の 1 対 1 の対応を維持することです。

バッファー長をオーバーフローする入力の場合、fgets はバッファーに収まる数の文字を受け入れ、残りを次の fgets のために入力バッファーに残します。

標準 (K.3.5.4.1) では、(gets とは異なり) gets_s を使用する場合、改行、EOF、または n-1 文字以内の読み取りエラーが必要であると規定されています。したがって、オーバーフローは実行時制約違反です。実行時制約違反がある場合、バッファ内の最初の文字がヌル文字に設定され、stdin 入力バッファ内の文字が読み取られ、改行文字が読み取られるまで破棄されます。ファイルの終わりが発生します。または読み取りエラーが発生します。

したがって、成功した場合、私は次のことを期待しました。

>fgets

First Name : Chris
Last Name  : Szalwinski
Chris

Szalwinski

>

>gets_s
First Name : Chris
Last Name  : Szalwinski
Chris
Szalwinski
>

オーバーフローでは、fgets と gets_s とは異なる動作を期待していました。言い換えると、

>fgets
First Name : Christopher
Last Name  : Christophe
r


>

>gets_s
First Name : Christopher
Last Name  : Szalwinski

Szalwinski

>

gets_s が入力の最初の行の内容を完全に削除することを期待していたことに注意してください。

主な問題が入力行と保存行の間の 1 対 1 の対応である場合、これはデバッグにおいて重要ですが、独自の関数を記述する必要があります (K&R の getline と同様)。

char *gets_s(char *s, int n) 
{
    int i, c;
    for (i = 0; i < n - 1 && (c = getchar()) != EOF && c != (int)'\n'; i++)
        s[i] = c;
    s[i] = '\0';
    while (n > 1 && c != EOF && c != (int)'\n')
        c = getchar();
    return c != EOF ? s : NULL;
}

このような関数では、1 対 1 の対応が維持され、バッファが飽和し​​、実行時制約違反は発生しません。

この結論を導き出すことは正しいでしょうか。

4

2 に答える 2

3

`-std = c11'を渡してみましたか?

このページによるとgets_s、C11で紹介されました。GCCを使用していると仮定すると、 `-std =c11'オプションを使用して制限付きC11サポートを有効にできます。

于 2013-03-01T02:16:39.727 に答える
1

最初にすべきことは、コンパイラの警告を有効にすることです。たとえば、-WallGCC に渡します。それが完了すると、次のように発行されます。

warning: implicit declaration of function ‘gets_s’

gets_s()これは基本的に、コンパイラが関数が何であるかを知らないことを意味します。では、そもそもコンパイラはどのように機能するのでしょうか。C では、これは暗黙の関数宣言と呼ばれます。これは基本的に、関数の宣言がない場合、コンパイラは整数を返し、任意の数のパラメーターを受け入れる関数があると想定する必要があることを示しています。例えば:

int foo(...);

したがって、コンパイラは喜んでコードを生成しました。あなたの特定のケースでは、そこから丘を下ります。おわかりのように、実際に存在する関数 (たとえば、printf()標準ライブラリに存在する標準のようなもの) を使用した場合は、すべてがうまくいったはずです (ほとんどの場合、さらに多くの落とし穴があります)。実際には、C11以前の世界にはそのようなものはありませんgets_s()(私が間違っていなければ、MicrosoftのCライブラリにのみ表示されます)。したがって、リンカーは単純にそれを見つけることができません。そのため、プログラムをまとめてアセンブルしようとするのをあきらめ、エラー メッセージを吐き出します。つまり、使用しないでくださいgets_sfgets代わりに使用してください。それはほとんど同じことです — 唯一の違いは、それが標準であり、FILE *(あなたがそれを与えるためにstdin)。端末に入力すると、それらのドキュメントを読むことができますman fgets。または (システムにマニュアル ページがインストールされていない可能性があるため)、オンラインで見つけることもできます

ここで何人かの友人が指摘しているようにgets_s()、C11 で追加されました。残念ながら、C11 は、Ubuntu が使用している GCC (および glibc) に完全には実装されていません。これは進行中の作業であり、GCC のC11 Status Wikiでステータスを確認できます。

@Keith Thompsonが言及したように、C11 Annex Kの残りの「境界チェックインターフェース」とともに、C11実装に完全に準拠している場合でもオプションです(gccはまだです)。実装は、マクロ__STDC_LIB_EXT1__を定義し、インターフェースを提供することもあれば、提供しないこともあります。

したがって、基本的には を使用しますfgets

于 2013-03-01T02:23:47.620 に答える