web-dev-qa-db-ja.com

%pでNULLポインターを印刷すると未定義の動作になりますか?

%p変換指定子を使用してNULLポインターを出力することは、未定義の動作ですか?

#include <stdio.h>

int main(void) {
    void *p = NULL;

    printf("%p", p);

    return 0;
}

この質問は、Cの実装ではなくCの標準に適用されます。

92
Dror K.

短い答え

はい。 _%p_変換指定子を使用してNULLポインターを印刷すると、未定義の動作が発生します。そうは言っても、私は既存の適合実装が誤動作することを知りません。

答えは、C標準(C89/C99/C11)のいずれにも適用されます。


長い答え

_%p_変換指定子は、ポインター型の引数がvoidであることを想定しています。印刷可能な文字へのポインターの変換は実装定義です。 nullポインターが期待されているとは述べていません。

標準ライブラリ関数の概要では、特に明記されていない限り、(標準ライブラリ)関数への引数としてのNULLポインタは無効な値と見なされます。

_C99_/_C11_ _§7.1.4 p1_

[...]関数の引数に無効な値がある場合([...] nullポインター、[...]など)、動作は未定義です。

NULLポインターを有効な引数として期待する(標準ライブラリ)関数の例:

  • fflush()は、「適用される」「すべてのストリーム」をフラッシュするためにNULLポインターを使用します。
  • freopen()は、nullポインターを使用して、ストリームに「現在関連付けられている」ファイルを示します。
  • snprintf()では、「n」がゼロの場合にヌルポインターを渡すことができます。
  • realloc()は、新しいオブジェクトの割り当てにNULLポインターを使用します。
  • free()は、nullポインターを渡すことができます。
  • strtok()は、後続の呼び出しにNULLポインターを使用します。

snprintf()の場合、 'n'がゼロのときにNULLポインターを渡すことができますが、同様のゼロ 'nを許可する他の(標準ライブラリ)関数の場合はそうではありません'。例:memcpy()memmove()strncpy()memset()memcmp()

これは、標準ライブラリの概要で指定されているだけでなく、これらの関数の概要でも指定されています。

_C99 §7.21.1 p2_/_C11 §7.24.1 p2_

_size_t_ nとして宣言された引数が関数の配列の長さを指定する場合、nはその関数の呼び出しで値ゼロを持つことができます。この節の特定の関数の説明で特に明記されていない限り、7.1.4。


意図的ですか?

Nullポインターを含む_%p_のUBが実際に意図的かどうかはわかりませんが、標準では明示的にnullポインターは標準ライブラリ関数の引数として無効な値と見なされると明記されているため、 nullポインターが有効な引数(snprintf、freeなど)である場合、その後、引数がゼロの「n」の場合(memcpymemmovememset)、C標準化委員会がそのようなことを未定義にすることにあまり関心がないと仮定するのは合理的だと思います。

20
Dror K.