2012-01-29 17 views
19

可能性の重複:
Correct format specifier to print pointer (address)?printf関数とポインタ

printfを使ってポインタを印刷する場合、それはvoid *へのポインタをキャストする必要のですか?言い換えれば、

#include <stdio.h> 
int main() { 
    int a; 
    printf("address of a = %p\n", &a); 
} 

のようなコードでは、引数は実際には(void *) &aであるべきですか? gcc明示的なキャストが行われていないときに警告を出すようには思われません。

+0

なぜprintfに何かを受け付け、%pで指定されたデータを取得するパラメータを指定すると警告が出ます。 – TigOldBitties

+0

'printf'は型保証されていません - どうしてその変数がジャミードジャーを与えるべきなのでしょうか?それはあなた次第です。 OOPに歓迎。 –

+0

@TigOldBitties:gccは 'printf'形式の文字列に基づいて静的解析を行うためです。 –

答えて

13

はい、キャストはvoid*になります。

int a; 
printf("address of a = %p\n", &a); 

&aint*です。 printfの"%p"形式には、void*の引数が必要です。は、であり、printfの宣言では、最初のもの(書式文字列)以外のパラメータの型情報は提供されないため、void*に暗黙的に変換されます。書式文字列の後のすべての引数には、デフォルト引数のプロモーションが適用されます。これらのプロモーションはint*void*に変換しません。

可能性の高い結果がprintfがタイプint*のは本当にだと、それはタイプvoid*であったかのように解釈引数を見ているということです。これは変換ではなく型打ちであり、未定義の動作をします。 int*void*が同じ表現をした場合にはうまくいく可能性がありますが、言語標準はそれを含意によっても保証するものではありません。そして、私が記述した型打ちはただ一つの可能​​な振る舞いにすぎません。標準は文字通り何が起こるかについて何も言わない。

(あなたが目に見えるプロトタイプと非可変引数の関数で同じことを行う場合は、コンパイラは、パラメータの型void*であること、コールの時点で知っているので、それは暗黙的に行うためのコードを生成しますint* -変換。これは当てはまりません。)

2

キャストする必要がありますかもしれないと思いますが必要です。ポインタのサイズが常に同じであることは確かですか?私はstruct*のサイズ(または多分ちょうど整列?)がunion*のものと異なることができることを最近stackoverflowで読むと確信しています。これは、一方または両方がvoid*のサイズと異なる可能性があることを示唆しています。

の値がであっても、変換に大きな変化がない場合や、ポインタ自体のサイズが正しいことを保証するためにキャストが必要なことがあります。

printの場合、%pにはvoid*が必要なので、明示的にキャストする必要があります。そうしないと、幸運であれば、ポインタのサイズとポインタの表現がその日を節約するかもしれません。しかし、明示的にキャストして特定する必要があります。他のものは技術的に未定義の動作です。

+1

あなたの答えは、実装で失敗が発生する可能性のあるメカニズムを扱っていますが、重要なポイントは、あなたが正しいタイプを 'printf'に渡す必要があり、この要求に従わないとUBになりません。 –

+0

@ H2CO3、私はこの質問でどこでもダウン投票をしていません。少なくとも1人の他の人が代替理論を前進させていて、おそらく潜んでいて下降している多くの人がいるでしょう。 –

+0

私はdownvoteをしませんでしたが、私は "私は確かではありませんが、このようになるかもしれません..."という形式の答えがどのように見えるかは、他の人がdownvotesの価値があると見なすことができました。いずれにせよ、悲しいことに、これはこれまでのところ最高の答えであり、完全に間違っていない唯一の答えです。 –

4

これはCかC++の質問ですか? C++の場合、5.2.2 [expr.call]パラグラフ7によれば、void*への暗黙の変換はないようです。 C99の6.5.2.2パラグラフ6も、ポインタ型の明示的な宣伝を意味するものではないようです。これは、ポインタ型のサイズが(少なくともC++では)異なるため、void*への明示的なキャストが必要であることを意味します。異なるポインタ型のレイアウトが同じでないと、定義されていない動作になります。可変引数リストを使用するときにポインタが適切なサイズで渡されることが保証されている場所を指摘できますか?

もちろん、C++プログラマであることはそれほど大きな問題ではありません。可変数の引数を持つ関数を使用しないでください。しかし、それはCの実行可能なアプローチではありません。

+0

サイズの問題ではないので、すべてのオブジェクトポインタは同じサイズです(標準ではラウンドトリップが必要なので)。しかし、その表現は必ずしも同じではありません。 –

+2

@BenVoigt:いいえ、すべてのオブジェクトポインタが同じサイズである必要はありません。 'void *'は、情報を失うことなくオブジェクトポインタの変換値を保持するのに十分な大きさでなければならないが、 'char *'を 'int *'に変換して元の値に戻す必要はない。往復の唯一の条件は、「何もしない」から「無効にする」、そしてまた戻ることです。 ( 'char *'は 'void *'と同じ表現でなければならないので、これもまたカバーされています) –

+0

@KeithThompson:近くの読み方では、配置に依存します。 'int'が' char'よりも強い整列条件を持たない場合、元の値を返すためには 'char *'を 'int *'に戻してください。 5.2.10p7「 型のprvalue」ポインタを「T1」へのポインタをタイプ「T2へのポインタ」(ここで、「T1」および「T2」はオブジェクト型であり、「T2」の整列要件は「なし」である) 'T1'のものよりも厳しい)、元の型に戻って元のポインタ値を返します。 –