web-dev-qa-db-ja.com

フォーマット文字列内で定義を使用するにはどうすればよいですか?

文字配列があるとしましょう:

#define LEN 10
char arr[LEN + 1];

それに対していくつかのscanf操作を実行しましょう:

scanf("Name: %s", arr);

誰かが10文字を超える名前を入力している場合、これは危険な場合があります。だからこれをよりよく使う:

scanf("Name: %10s", arr);

さて、LENを変更すると、問題が発生します。 arrのコンテキストで10を使用したすべての行を修正するには、コード全体を調べる必要があります。だから私はこのような何かについて考えました:

scanf("Name: %LENs", arr);

ただし、これは機能しません。LENは文字列内で使用されるため、プリプロセッサによって解決されません。

フォーマット文字列内で定義を使用する方法は?

14
eDeviser

Cは隣接する文字列リテラルを結合し、プリプロセッサパラメータを_#_で文字列化できるため、次の方法でうまくいくはずです。

_#define LEN 10

// this converts to string
#define STR_(X) #X

// this makes sure the argument is expanded before converting to string
#define STR(X) STR_(X)

[...]

scanf("Name: %" STR(LEN) "s", arr);
_

マクロが必要なのは、_#LEN_だけでは、LENが10に展開され、引数に_#_を適用するマクロが1つだけの場合、結果は_"LEN"_(引数は展開されません)。

プリプロセッサ/コンパイラは、次の手順でこれを変換します。

_1. scanf("Name: %" STR_(10) "s", arr);
2. scanf("Name: %" "10" "s", arr);
3. scanf("Name: %10s", arr);
_

最後のステップでは、文字列リテラルが1つに結合されます。


ちなみに、scanf()形式の文字列では、ユーザーは文字通り入力する必要があります。

_Name: xyz
_

実際に一致します。これがあなたが望んでいたことではないかと思います。あなたはおそらくこのようなものが欲しいでしょう:

_fputs("Name: ", stdout);
fflush(stdout);
scanf("%" STR(LEN) "s", arr);
_

また、scanf()をまったく使用しないことを検討してください。例: fgets()、このプリプロセッサの魔法全体は廃止されました。 scanf()を使用すべきでない理由については、私の scanf() から離れた初心者向けガイドを参照してください。

24
user2371524