web-dev-qa-db-ja.com

Cで%n形式指定子を使用するのは何ですか?

Cの%n形式指定子の使用は何ですか?誰かが例を使って説明できますか?

116
josh

何も印刷されません。引数は、これまでに書き込まれた文字数が格納される符号付き整数へのポインターである必要があります。

#include <stdio.h>

int main()
{
  int val;

  printf("blah %n blah\n", &val);

  printf("val = %d\n", val);

  return 0;

}

前のコードは次を印刷します。

blah  blah
val = 5
142
Starkey

これらの答えのほとんどは、%ndoes(何も印刷せず、これまでに印刷した文字数をint変数に書き込むこと)を説明していますが、これまでのところ誰も実際にseの例を示しました。以下がその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のような難解なものを使用する価値があるかどうか(そしておそらくエラーを引き起こす可能性があるかどうか)は議論の余地があります。)

177
jamesdlin

%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

17
Xzhsh

here から、これまでに印刷された文字数が格納されていることがわかります。

n引数は、fprintf()関数の1つに対するこの呼び出しによって、これまでに出力に書き込まれたバイト数が書き込まれる整数へのポインターでなければなりません。引数は変換されません。

使用例は次のとおりです。

int n_chars = 0;
printf("Hello, World%n", &n_chars);

n_charsの値は12になります。

13
KLee1

%nに関連付けられた引数はint *として扱われ、printfのその時点で印刷された合計文字数で埋められます。

9
Evan

これまでのところ、すべての答えは%nが行うことに関するものですが、そもそも誰もそれが欲しい理由ではありません。格納された値は結果の文字列への配列インデックスであるため、後で結果の文字列を分割または変更する必要がある場合、sprintf/snprintfを使用すると多少便利です。ただし、sscanfファミリの関数は、処理される文字数ではなくフィールド数を返すため、このアプリケーションはscanfを使用するとさらに便利です。

別の非常にハッキング的な使用法は、別の操作の一部として数字を出力しながら、同時に無料で擬似log10を取得することです。

8
R..

先日、%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);
6
jamesdlin

何も印刷しません。 %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;
}

_set_printf_count_outputのドキュメント

printf()関数でこれまでに印刷された文字数の値を保存します。

例:

int a;
printf("Hello World %n \n", &a);
printf("Characters printed so far = %d",a);

このプログラムの出力は次のようになります

Hello World
Characters printed so far = 12
1