%n
Cでのフォーマット指定子の使用は何ですか? 誰かが例で説明できますか?
11 に答える
これらの回答のほとんどは何をするのか(何も印刷%n
せず、これまでに印刷された文字数を変数に書き込むこと)を説明していますが、これまでのところ、それがどのように使用int
されているかの例を実際に示した人は誰もいません。これが1つです:
int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");
印刷されます:
hello: Foo
Bar
FooとBarを揃えます。(この特定の例を使用せずにそれを行うのは簡単%n
であり、一般に、最初のprintf
呼び出しを常に分割することができます:
int n = printf("%s: ", "hello");
printf("Foo\n");
printf("%*sBar\n", n, "");
わずかに追加された利便性が、%n
(そしておそらくエラーを導入する)ような難解なものを使用する価値があるかどうかは議論の余地があります。)
何も印刷されません。引数は、これまでに書き込まれた文字数が格納されているsignedintへのポインタである必要があります。
#include <stdio.h>
int main()
{
int val;
printf("blah %n blah\n", &val);
printf("val = %d\n", val);
return 0;
}
前のコードは次のように出力します。
blah blah
val = 5
指定子が実際に実際に使用されている例はあまり見たことがありませんが、かなり前にフォーマット文字列攻撃による%n
古い学校の printf 脆弱性で使用されたことを覚えています。
このようになったもの
void authorizeUser( char * username, char * password){
...code here setting authorized to false...
printf(username);
if ( authorized ) {
giveControl(username);
}
}
悪意のあるユーザーは、ユーザー名パラメーターがフォーマット文字列として printf に渡されることを利用して%d
、%c
または w/e の組み合わせを使用してコール スタックを調べ、認証された変数を真の値に変更する可能性があります。
ええ、これは難解な使い方ですが、セキュリティ ホールを回避するためにデーモンを作成するときに知っておくと便利ですか? :D
ここから、これまでに印刷された文字数が格納されていることがわかります。
n
引数は、関数の1つへのこの呼び出しによってこれまでに出力に書き込まれたバイト数が書き込まれる整数へのポインタでなければなりませんfprintf()
。引数は変換されません。
使用例は次のとおりです。
int n_chars = 0;
printf("Hello, World%n", &n_chars);
n_chars
その場合、値は。になり12
ます。
に関連付けられた引数%n
はとして扱われ、のint*
その時点で印刷された合計文字数で埋められprintf
ます。
これまでのところ、すべての答えはそれについてです%n
が、そもそもなぜ誰もがそれを望んでいるのかというわけではありません。格納されている値は結果の文字列への配列インデックスであるため、後で結果の文字列を分割または変更する必要がある場合は、sprintf
/を使用すると便利です。ただし、このアプリケーションは、特にファミリ内の関数が処理された文字数ではなくフィールド数を返すためsnprintf
、はるかに便利です。sscanf
scanf
もう1つの本当にハックな使用法は、別の操作の一部として数値を出力すると同時に、疑似log10を無料で取得することです。
先日、私は自分%n
の問題をうまく解決できる状況にいることに気づきました。以前の回答とは異なり、この場合、適切な代替案を考案することはできません。
指定したテキストを表示する GUI コントロールがあります。このコントロールは、そのテキストの一部を太字 (または斜体、下線など) で表示できます。また、開始文字インデックスと終了文字インデックスを指定して、その部分を指定できます。
私の場合、コントロールへのテキストを で生成していますがsnprintf
、置換の 1 つを太字にしたいと考えています。この置換の開始インデックスと終了インデックスを見つけることは、次の理由から簡単ではありません。
文字列には複数の置換が含まれており、置換の 1 つは任意のユーザー指定のテキストです。これは、気になる置換をテキストで検索すると、あいまいになる可能性があることを意味します。
フォーマット文字列はローカライズされている可能性があり、
$
位置フォーマット指定子に POSIX 拡張を使用している可能性があります。したがって、フォーマット指定子自体の元のフォーマット文字列を検索することは簡単ではありません。ローカリゼーションの側面は、フォーマット文字列を複数の への呼び出しに簡単に分割できないことも意味します
snprintf
。
したがって、特定の置換の周りのインデックスを見つける最も簡単な方法は、次のようにすることです。
char buf[256];
int start;
int end;
snprintf(buf, sizeof buf,
"blah blah %s %f yada yada %n%s%n yakety yak",
someUserSpecifiedString,
someFloat,
&start, boldString, &end);
control->set_text(buf);
control->set_bold(start, end);
何も印刷されません。%n
これは、フォーマット文字列に表示される前に印刷された文字数を把握し、それを提供されたintに出力するために使用されます。
#include <stdio.h>
int main(int argc, char* argv[])
{
int resultOfNSpecifier = 0;
_set_printf_count_output(1); /* Required in visual studio */
printf("Some format string%n\n", &resultOfNSpecifier);
printf("Count of chars before the %%n: %d\n", resultOfNSpecifier);
return 0;
}
その関数でこれまでに印刷された文字数の値を格納しprintf()
ます。
例:
int a;
printf("Hello World %n \n", &a);
printf("Characters printed so far = %d",a);
このプログラムの出力は次のようになります。
Hello World
Characters printed so far = 12
%n は C99 です。VC++ では動作しません。