1. ホーム
  2. c

[解決済み】ポインタまたはアドレスを印刷するための正しいフォーマット指定子?

2022-04-18 21:34:03

質問

変数のアドレスを表示するには、どの書式指定子を使えばよいのでしょうか?以下のロットで迷っています。

<ブロッククオート

u - 符号なし整数

x - 16進数値

p - void ポインタ

アドレスを印刷するのに最適な形式はどれでしょうか?

どのように解決するのですか?

最もシンプルな答えは、異なるプラットフォーム間のフォーマットのばらつきや差異を気にしないと仮定すれば、標準的な %p という表記があります。

C99規格(ISO/IEC 9899:1999)では、§7.19.6.1 ¶8でこう言っています。

p へのポインタを引数とする。 void . ポインタの値は は,実装の定めるところにより,一連の印刷文字に変換される。 の方法で行う。

(C11 - ISO/IEC 9899:2011 - では §7.21.6.1 ¶8 に記載されています)。

一部のプラットフォームでは、先頭の 0x また、文字は小文字でも大文字でもよく、C標準では16進数で出力するように定義されていませんが、そうでない実装を私は知りません。

で明示的にポインタを変換するべきかどうかは、議論の余地があります。 (void *) を鋳造します。 これは明示的であり、通常は良いことです(だから私はそうしています)。 void '. ほとんどのマシンでは、明示的なキャストを省略することができます。 しかし、ビット表現で char * のアドレスは、与えられたメモリ位置の' その他のポインタ のアドレスは、同じメモリ位置に対するものです。 これは、バイトアドレスではなく、ワードアドレスのマシンとなります。 このようなマシンは最近ではあまり見かけなくなりましたが、私が大学卒業後に最初に携わったマシン(ICL Perq)がそのようなものでした。

の実装で定義された挙動に満足できない場合は、以下のようにします。 %p の場合、C99を使用します。 <inttypes.h>uintptr_t の代わりに

printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);

これによって、表現を自分に合ったものに微調整することができます。 私は、16進数の数字を大文字にすることで、数字の高さが一様に同じになるようにし、冒頭の特徴的なディップは 0xA1B2CDEF のようなものではなく、このように表示されます。 0xa1b2cdef も数字に沿って上下に凹む。 しかし、非常に広い範囲内で、あなたの選択です。 その (uintptr_t) のキャストは、GCCがコンパイル時にフォーマット文字列を読み取ることができる場合、明確に推奨されています。 警告を無視して、たいていの場合逃げ出す人もいると思いますが、キャストを要求するのは正しいことだと思います。


ケレクさんがコメントで質問しています。

標準のプロモーションと変種引数について少し混乱しています。すべてのポインターはvoid*に標準プロモートされるのでしょうか?そうでなければ、もし int* が例えば2バイトで void* が4バイトだった場合、引数から4バイトを読み取るのは明らかにエラーですよね?

C言語規格では、オブジェクトのポインタはすべて同じサイズでなければならないとされているように錯覚していましたので void *int * を異なるサイズにすることはできません。 しかし、私がC99規格の関連セクションと考えるものは、それほど強調されていません(ただし、私が提案したことが実際に間違っている実装を私は知りません)。

§6.2.5 タイプ

¶26 voidへのポインタは、文字型へのポインタと同じ表現とアラインメントの要件を持つものとする。 39) 同様に、互換型の修飾版または非修飾版へのポインタも、同じ表現と配置の要件を持つものとする。構造体型へのすべてのポインタは,互いに同じ表現及び整列の要求をもたなければならない。和集合型へのすべてのポインタは,互いに同じ表現及び整列の要求をもたなければならない。他の型へのポインタは,同じ表現又は整列の要求事項をもつ必要はない。

39) 同じ表現と配置の要件は、関数の引数、関数の戻り値、組合のメンバーとして交換可能であることを意味する。

(C11では§6.2.5, ¶28, 脚注48で全く同じことを言っている)。

したがって、構造体へのポインタはすべて互いに同じサイズでなければならず、ポインタが指す構造体のアライメント要件が異なっていても、同じアライメント要件を共有しなければなりません。 ユニオンの場合も同様です。 文字ポインタとvoidポインタは、同じサイズとアライメント要件でなければなりません。 のバリエーションへのポインターは int (意味 unsigned intsigned int ) は、互いに同じサイズとアライメントの要件を持たなければなりません。他の型についても同様です。 しかし、C言語規格では、正式に sizeof(int *) == sizeof(void *) . そうか、SOは自分の思い込みを点検させるのに適しているんだ。

C言語規格では、関数ポインタとオブジェクトポインタが同じサイズであることを決定的に要求していません。 これは、DOSのようなシステムで異なるメモリモデルを壊さないようにするために必要なことでした。 16ビットのデータポインタがあっても32ビットの関数ポインタがあったり、その逆もあり得るのです。 このため、C言語規格では、関数ポインタをオブジェクトポインタに変換したり、その逆を行うことは義務づけていません。

POSIXをターゲットにしているプログラマにとっては)幸いなことに、POSIXがこの問題に踏み込んで、関数ポインタとデータポインタを同じサイズにすることを義務付けています。

<ブロッククオート

§2.12.3 ポインターの種類

すべての関数ポインタの型は,voidへの型ポインタと同じ表現を持たなければならない。関数ポインタの void * は,その表現を変更してはならない。A void * このような変換によって得られた値は、情報を失うことなく、明示的なキャストを用いて元の関数ポインタ型に戻すことができます。

注 ISO C 標準では要求されていませんが、POSIX 準拠のためには必要です。

ということで、どうやら、明示的なキャストで void * のような可変個体関数へのポインタを渡す場合、コードの信頼性を最大にするために強く推奨されます。 printf() . POSIX システムでは、関数ポインタを void ポインタにキャストして印刷することは安全です。 他のシステムでは,必ずしもそうすることが安全とは限りませんし,ポインタを void * をキャストせずに使用します。