3

次のコードは私が作成したものではありません。検索していて、他の方からの質問で見つけました。

#include <stdio.h>
#define NAME_MAX    80
#define NAME_MAX_S "80"

int main(void)
{
    static char name[NAME_MAX + 1]; // + 1 because of null
    if(scanf("%" NAME_MAX_S "[^\n]", name) != 1) // This line
    {
        fputs("io error or premature end of line\n", stderr);
        return 1;
    }

    printf("Hello %s. Nice to meet you.\n", name);
}

マークされた行が何をするのか教えていただけますか?

4

5 に答える 5

7

文字列連結です。文字列を書くと、コンパイル時に連結されるようにすることができます。

 "%" NAME_MAX_S "[^\n]",

最終的には次のようになります。

 "%80[^\n]"

scanfnameは、ではない 80 文字という名前の変数に読み込みますnewline

于 2012-12-16T18:05:48.833 に答える
3

プリプロセッサの置換後は、次のようになります。

if(scanf("%" "80"_MAX_S "[^\n]", name) != 1) // <-- This line

これは次と同等です:

if(scanf("%80[^\n]", name) != 1) // <-- This line

最大 80 文字または改行文字を読み取ります。

のサイズnameは 81 です。したがって、80 文字 + nul ターミネータを収容できます。これは通常、入力を読み取る際のバッファ オーバーフローを回避するために行われます。

于 2012-12-16T18:05:45.730 に答える
3

2 つのややあいまいな言語機能が同時に使用されています。

  1. 弦貼り。前処理後、行は

    if(scanf("%" "80" "[^\n]", name) != 1)
    

    次に、隣接する文字列リテラルが一緒に貼り付けられるため、コンパイラの後半の部分では、... scanf("%80[^\n]", name) ...

  2. あまり一般的ではないscanf変換。 "%[...]"と完全に異なるわけではない"%s"ので、これは"%80s"変換にかなり似ています。マニュアルページまたは別のリファレンス[で scanf 変換指定子として調べることができると確信しています。scanf(3)

于 2012-12-16T18:08:42.463 に答える
2

stdin変数から80 文字以下 (改行以外) を読み取りnameます。

改行に遭遇すると、スキャンを停止します。

少しあいまいで、あまりよく知られていない C の機能、つまり隣接する定数文字列を単一の文字列に折りたたむ機能を使用しているため、奇妙に見えます。

(これは実際には非常に便利な機能です。長い文字列を複数の行にまたがってラップすることができ、マクロが定数文字列を使用して便利なことを実行できるようになります)

...基本的に、この行が次のようになっていると想像してください。

if(scanf("%80[^\n]", name) != 1)

次に、 scanf のドキュメントに従って、何が行われているかを理解し[^\n]てください。

ちなみに...定数の使用は、cpp stringificationNAME_MAX_Sを使用して完全に回避できます :

if(scanf("%" #NAME_MAX "[^\n]", name) != 1)
于 2012-12-16T18:05:44.997 に答える
2
if (scanf("%" NAME_MAX_S "[^\n]", name) != 1) // This line

それは書き方です:

if (scanf("%80[^\n]", name) != 1)

あなたを混乱させる可能性のある複数の機能があります。

  1. この"%" NAME_MAX_S "[^\n]"表記法では、文字列の連結を使用して、断片から単一の文字列を作成します。
  2. 変換指定では、否定された%80[^\n]「スキャン セット」を使用して、読み取り文字列が最大 80 個の非改行であることを指定します。
  3. またはでscanf()et al に長さを指定する場合、ヌルバイトを除いた文字数を指定します (旧設計)。これは、文字列変数の定義されたサイズと変換仕様で指定された長さの間の 1 つずつずれを処理する必要があることを意味します。%s%[]
  4. 全体的な条件は、文字列が正常に読み取られたことを正しくチェックします。変換の失敗 (入力の最初の文字が改行であるため戻り値 0) と EOF を検出します。唯一の考えられる問題は、2 つを区別するために戻り値を保持しないことですが、 and を使用feof()ferror()てそうすることができます。これが意図されていることです (何かが失敗した後のエラーを区別するため)。

このコードNAME_MAX_Sは、文字列があることを確認するために使用します。使用できた可能性があります:

#define STR_EVALUATE(x) #x
#define STRINGIFY(x) STR_EVALUATE(x)

if (scanf("%" STRINGIFY(NAME_MAX) "[^\n]", name) != 1)

これにより、保持する行数が 1 に減ります。ただし、単純な数値に対してのみ機能します。式がある場合#define NAME_MAX (2*LEN_NAME_COMPONENT+LEN_MIDDLE_INITIALS)、文字列形成のプロセス全体が機能しません。次に、次のことを行う必要があります。

char format[16];
sprintf(format, "%%%d[^\n]", NAME_MAX);

if (scanf(format, name) != 1)
于 2012-12-16T18:10:25.123 に答える