ポインタまたはアドレスを出力するための正しいフォーマット指定子?


192

変数のアドレスを出力するにはどのフォーマット指定子を使用すればよいですか?以下のロット間で混乱しています。

%u-符号なし整数

%x-16進値

%p-voidポインター

住所を印刷するのに最適な形式はどれですか?

回答:


239

異なるプラットフォーム間での形式のばらつきやバリエーションを気にしないと仮定すると、最も簡単な答えは標準%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 *特定のメモリ位置のアドレスのビット表現が同じメモリ位置の「その他のポインタ」アドレスと異なるマシンでは問題になります。これは、バイトアドレス指定されたマシンではなく、ワードアドレス指定されたマシンです。このようなマシンは一般的ではありません(おそらく利用できません)が、大学の後に私が最初に取り組んだマシンはそのようなマシンの1つ(ICL Perq)でした。

の実装定義の動作に満足できない場合は、代わりに%pC99 <inttypes.h>を使用しuintptr_tます。

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

これにより、表現を自分に合わせて微調整できます。数字が均一に同じ高さになるように16進数字を大文字にすることを選択しました。これにより、開始時に特徴的なディップが0xA1B2CDEF表示されます。これは0xa1b2cdef、数値に沿って上下にディップするのとは異なります。ただし、非常に広い範囲で選択できます。(uintptr_t)それはコンパイル時にフォーマット文字列を読み取ることができたときにキャストが明確にGCCで推奨されています。キャストをリクエストするのは正しいと思いますが、ほとんどの場合、警告を無視してそれを回避する人もいると思います。


ケレックはコメントで尋ねています:

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

私は、すべてのオブジェクトポインターは同じサイズでなければならず、異なるサイズであっvoid *int *はならないというC標準の幻想の下にありました。しかし、私がC99標準の関連セクションだと思うのはそれほど強調されていません(私が提案したことが真である実装は実際には偽ですが)。

§6.2.5タイプ

¶26voidへのポインタは、文字型へのポインタと同じ表現および配置要件を持たなければならない。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 *、表現は変更されません。そのvoid *ような変換の結果の値は、情報を失うことなく、明示的なキャストを使用して、元の関数ポインター型に戻すことができます。

注:ISO C標準ではこれは必須ではありませんが、POSIX準拠には必須です。

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


3
標準的なプロモーションと可変引数について少し混乱しています。すべてのポインターは標準に昇格されvoid*ますか?それ以外の場合、int*たとえば、2バイトでvoid*4バイトだった場合、引数から4バイトを読み取るのは明らかにエラーでしょう。
Kerrek SB、2012

POSIX(POSIX 2013)への更新によりセクション2.12.3が削除され、dlsym()代わりにほとんどの要件が関数に移動したことに注意してください。ある日、変更について書こうと思いますが、「1日」は「今日」ではありません。
ジョナサンレフラー2014年

この回答は関数へのポインタにも適用されますか?それらを変換できますvoid *か?うーん、ここにコメントが表示されます。one-wat変換のみが必要なため(への関数ポインターvoid *)、それは機能しますか?
chux-モニカを2015年

@chux:厳密には、答えは「いいえ」ですが、実際には答えは「はい」です。C標準では、関数ポインタをaに変換したりvoid *、情報を失わずに変換したりできることを保証していません。実用的には、関数ポインターのサイズがオブジェクトポインターのサイズと同じではないマシンはほとんどありません。変換が問題となるマシンで関数ポインタを出力する方法は、この標準では提供されていないと思います。
Jonathan Leffler、2015年

「情報を失うことなく戻す」は印刷には関係ありません。それは役に立ちますか?
chux-モニカを2015年

50

pポインターを出力するための変換指定子です。これを使って。

int a = 42;

printf("%p\n", (void *) &a);

キャストの省略は未定義の動作であり、p変換指定子を使用した印刷は実装定義の方法で行われることに注意してください。


2
失礼、キャストを省略するのが「未定義の動作」なのはなぜですか?値ではなくアドレスだけが必要な場合、これはどの変数のアドレスでもかまいませんか?
valdo 2012年

9
@valdoは、Cがそれを言っているためです(C99、7.19.6.1p8)「p引数はvoidへのポインタでなければなりません。」
ouah 2012年

12
@valdo:すべてのポインターが同じサイズ/表現であるとは限りません。
ca

32

使用%p「ポインタ」のため、および*何かを使用しないでください。ポインタを特定のタイプの整数のように扱うことが許可されていることは、標準では保証されていないため、実際には、整数形式で未定義の動作が発生します。(例えば、%u期待しunsigned intている場合、しかし、何void*よりも、異なるサイズまたは位置合わせ要件を有していますunsigned int?)

*)[Jonathanの細かい答えを参照してください!]の代わりに%p、C99で追加されたからのポインター固有のマクロを使用できます<inttypes.h>

すべてのオブジェクトポインターはvoid*Cで暗黙的に変換可能ですが、可変引数としてポインターを渡すには、明示的にキャストする必要があります(任意のオブジェクトポインターは変換可能で、voidポインター同一ではないため)。

printf("x lives at %p.\n", (void*)&x);

2
すべてのオブジェクトポインターはに変換できますvoid *printf()可変関数であるため、技術的には明示的なキャストが必要です)。関数ポインタは必ずしもに変換できるとは限りませんvoid *
ca

@caf:ああ、私は可変引数について知りませんでした-修正しました!ありがとう!
Kerrek SB、2012

2
標準Cではvoid *、関数ポインターを損失せずに関数ポインターに変換したり、関数ポインターに戻したりする必要はありません。幸いにも、POSIXはそれを明示的に要求します(標準Cの一部ではないことに注意してください)。したがって、実際にはそれを回避できます(に変換void (*function)(void)void *てに戻しますvoid (*function)(void))が、厳密にはC標準では必須ではありません。
ジョナサンレフラー、2012年

2
ジョナサンとR .:これはすべて非常に興味深いですが、ここでは関数ポインタを出力しようとしているわけではないので、これについて議論するのに適切な場所ではないかもしれません。私はむしろ私が主張しないためのサポートをここにいくつか見たいと思います%u
Kerrek SB、2012

2
%u%luに間違っているすべてのマシンではなく、いくつかのマシン。の仕様はprintf、渡されたタイプがフォーマット指定子で必要なタイプと一致しない場合の動作が定義されていないことを明確にしています。タイプのサイズが一致するかどうか(マシンに応じてtrueまたはfalseになる可能性があります)は関係ありません。それは一致しなければならないタイプであり、決して一致しません。
R .. GitHub ICE HELPING ICEの停止2012

9

他の(非常に良い)答えの代わりに、あなたはにキャストできuintptr_tたりintptr_t(からstdint.h/ inttypes.h)と対応する整数変換指定子を使用します。これにより、ポインタのフォーマット方法の柔軟性が高まりますが、厳密に言えば、これらのtypedefを提供するための実装は必要ありません。


検討し#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); } 、それが使用して変数のアドレスを印刷する未定義の動作である%u書式指定子が?ほとんどの場合、変数のアドレスは正なので、%u代わりに使用でき%pますか?
デストラクタ

1
@Destructor:いいえ、型の%u形式であり、unsigned intへのポインター引数と共に使用することはできませんprintf
R .. GitHub ICE HELPING ICEを停止

-1

%xまたは%Xまたはを使用できます%p。それらはすべて正しいです。

  • を使用する%x場合、アドレスは小文字で指定されます。次に例を示します。a3bfbc4
  • を使用する%X場合、アドレスは大文字で指定されます。次に例を示します。A3BFBC4

これらは両方とも正しいです。

を使用している場合、%xまたは%Xアドレスの6つの位置を考慮している場合、および使用し%pている場合、アドレスの8つの位置を考慮しています。例えば:


1
SOへようこそ。他の回答を確認するために、少し時間をかけてください。それらは、あなたが見落としている多くの詳細を明確に説明しています。
AntoineL
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.