ショッピングモール  


【掲示板ご利用上の注意】

 ※題名は具体的に!
 ※学校の課題の丸投げ禁止!
 ※ソースの添付は「HTML変換ツール」で字下げ!
 ※返信の引用は最小限に!
 ※環境(OSとコンパイラ)や症状は具体的に詳しく!
 ※マルチポスト(多重投稿)は謹んで!

 詳しくはこちら



 本当はこんなに大きく書きたくはないのですが、なかなか守っていただけなくて…。
 守ってくださいね。お願いします。(by管理人)

C言語ソース⇒HTML形式ツール   掲示板1こちら


管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧

No.3032

printf でポインタを表示するときに(void*)キャストが必要か?
投稿者---RiSK(2004/11/24 15:25:33)


環境:WinXP Pro/VCTK2003

No.3026 Re:ディレクトリ配下のファイルを読み込む
ソースを書いていて疑問に思ったことを質問します。
printf でポインタを表示するときに(void*)でキャストする必要はあるのでしょうか?


私は↑のリンク先で
> ポインタを表示するときは %p
> また void へのポインタで無ければならないようなので(void*)でキャストします。
って書きましたが

> C言語の場合、(void *)で受ける場合、キャストは不要ですが、

とのレスがありました。そこで代入の場合の実験:
int main(void) {
    int i, *pi;
    void * pv = &i;
    return 0;
}
確かに不要っぽいです。
# ちなみに void* への代入は C++ でもキャスト不要でした
# pi = pv;
# の時は,C は OK. C++ ではエラー(=キャスト必要)でしたよ。>RAPT さん<勘違いかも (^^


しかし,C FAQ 15.3を見ていると
> 関数が可変数個の引数を取るとき、プロトタイプは可変数個の引数の 数やそのデータ型についての情報を与えない(そもそも出来ない)。よっ て通常の保護の仕組みは、可変数個の引数リストの可変長の部分には あてはまらない。コンパイラは暗黙の変換も(通常の)データ型の不 一致も警告もできない。

とあります。やっぱりキャストは必要なのではと思い,次のようなソースで実験してみました。
#include <stdio.h>
int main(void) {
    FILE *fp = fopen("test.c", "r");  // オープン成功と仮定
    printf("       fp == %p\n", fp);
    printf("(void*)fp == %p\n", (void*)fp);
    fclose(fp);
    return 0;
}
実行結果:
C:\hoge>test
       fp == 004080D8
(void*)fp == 004080D8
…どちらでもいいように見えます。これはキャストの必要無しと考えてよいのでしょうか?

キャストは必要? それとも不要?
結局,可変長引数の型変換についての質問になるのかな?
現在の規格ではどのようになっているのでしょうか?


この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:printf でポインタを表示するときに(void*)キャストが必要か? 3035 tetrapod 2004/11/24 16:38:42
<子記事> Re:printf でポインタを表示するときに(void*)キャストが必要か? 3037 通行人 2004/11/24 17:45:16


No.3035

Re:printf でポインタを表示するときに(void*)キャストが必要か?
投稿者---tetrapod(2004/11/24 16:38:42)


厳密には「必要」でしょう。

今手元に C の規格書がない (C++ の規格書はこの際役に立たない) ので記憶だけで書きます。

・非関数ポインタは、内部表現を失うことなく void* に暗黙のうちに変換できる
・非関数ポインタ各種の内部表現について規格書は述べていない
 =例えば char* と int* とでポインタそのもののサイズが異なっていてもよい。
 (http://www.kouno.jp/home/c_faq/c5.html#17)
・可変個引数の可変個部分については暗黙の昇格以外には型変換は行われない

この件に絡むのは以上3点。
3番目から、
printf の %p は void* を要求するので、そこに char* や int* を渡しても
例:int *p=&x; printf("%p", p);
勝手な型変換は行われず char* や int* のままである。
2番目から
int* と void* とで内部表現(特にsizeof)が異なる場合 printf は void* を正しく取り出せない。
すなわち誤動作する。
1番目から
int* を void* に明示変換しておけば精度落ちなく正しい変換がされる。

と言えそうです。

ポインタの内部表現が異なるマシンはレアなので、切り捨てちゃうならキャスト不要。
厳密規格合致なプログラムにするならキャスト必要。
ただし、ポインタの内部表現は違う可能性があるので実行結果は可搬ではない。
ということでしょう。

C++ の operator << (const void*) の場合は話が逆。
これひとつ用意すれば任意の T t; に対して os << &t; が可能となります。



この投稿にコメントする

削除パスワード

No.3054

Re:printf でポインタを表示するときに(void*)キャストが必要か?
投稿者---RiSK(2004/11/25 15:51:02)


詳細な説明をありがとうございます。
まだ,気になるところがあるので質問を続けます。

>ポインタの内部表現が異なるマシンはレアなので、切り捨てちゃうならキャスト不要。
>厳密規格合致なプログラムにするならキャスト必要。

だということは理解しましたが,

>ただし、ポインタの内部表現は違う可能性があるので実行結果は可搬ではない。

のはキャストした場合でしょうか,しなかった場合でしょうか?
(あるいはいずれにせよ?)

前半部分の説明を見る限りキャストをしなかった場合の説明だとは思うのですが,「ただし」というのが何に対するものなのかハッキリ分かりませんでした。


>C++ の operator << (const void*) の場合は話が逆。
>これひとつ用意すれば任意の T t; に対して os << &t; が可能となります。

将来 C++ の勉強を再会したときのために記憶にとどめておきます。
ありがとうございます。


この投稿にコメントする

削除パスワード

No.3057

Re:printf でポインタを表示するときに(void*)キャストが必要か?
投稿者---tetrapod(2004/11/25 17:07:16)


%p で何がどのように表示されるかは処理系定義?未規定?です。つまり
0x12345678 のような表示がされるかもしれないし
FEDC:BA98 のような表示がされるかもしれないし
FFEDCB のような表示がされるかもしれないし
あるいは私がまったく想像しなかったような表示かもしれません。

厳密規格合致なソースコードを違うマシン/コンパイラに持って行ったとしても、
この場合は得られる結果が同じとはならない、ということです。



この投稿にコメントする

削除パスワード

No.3063

Re:printf でポインタを表示するときに(void*)キャストが必要か?
投稿者---RiSK(2004/11/25 22:36:00)


>ただし、ポインタの内部表現は違う可能性があるので実行結果は可搬ではない。

>%p で何がどのように表示されるかは処理系定義?未規定?です。つまり
>0x12345678 のような表示がされるかもしれないし
>FEDC:BA98 のような表示がされるかもしれないし
>FFEDCB のような表示がされるかもしれないし

ああ,なるほど。この例で言わんとしていることが分かりました。

>厳密規格合致なソースコードを違うマシン/コンパイラに持って行ったとしても、
>この場合は得られる結果が同じとはならない、ということです。

そうですよね。ポインタそのものの内部表現が確か規定されていないはずですから,それの表現方法も処理系によるでしょうね。
私の勘違いにも答えてくださり,ありがとうございます。


まとめ:
printf でポインタを表示するときに(void*)キャストが必要か?

厳密規格合致なプログラムにするならキャスト必要。
    printf("%p\n", (void*)fp);
ただし,処理系定義の方法で表示される。


この投稿にコメントする

削除パスワード

No.3073

Re:printf でポインタを表示するときに(void*)キャストが必要か?
投稿者---tetrapod(2004/11/26 13:02:08)


あうー。大きなミスを犯していたので訂正します。

「厳密規格合致プログラム」とは、以下のようなプログラムを言います。
・「未定義な動作」が無い正しいプログラムであって、
・「処理系定義な動作」「未規定な動作」のすべてに依存しないもの

%p を使った時点で「処理系定義な動作」に依存してしまうため、
そーいうプログラムは「厳密規格合致」には決してなりません。

なのでこの場合「規格に合致させるためには (void*) キャストが必要」と呼ぶべきです。
厳密という言葉を取っちゃってください。
# 「規格に、より合致させるには」と言うべきかな?



この投稿にコメントする

削除パスワード

No.3037

Re:printf でポインタを表示するときに(void*)キャストが必要か?
投稿者---通行人(2004/11/24 17:45:16)


> 現在の規格ではどのようになっているのでしょうか?

下記は、古い JIS C (JIS X 3010-1993 7.9.6.1 fprintf関数) より、 p 変換指定子説明箇所を引用したものです。

>実引数は、voidへのポインタでなければならない。そのポインタ値を処理系定義の方法で表示可能文字の列に変換する。

下記は、新しい JIS C (JIS X 3010:2003 7.19.6.1 fprintf関数) より、 p 変換指定子説明箇所を引用したものです。

>実引数は、voidへのポインタでなければならない。そのポインタ値を処理系定義の方法で表示可能文字の並びに変換する。

どちらの規格も、 voidへのポインタでなければならないようです。



この投稿にコメントする

削除パスワード

No.3055

Re:printf でポインタを表示するときに(void*)キャストが必要か?
投稿者---RiSK(2004/11/25 15:55:54)


>どちらの規格も、 voidへのポインタでなければならないようです。

新旧両方の情報ありがとうございます。
BohYoh.com【C言語講座】標準ライブラリ関数 fprintf
↑にも同様のことが書かれていますね。

難しいことを考えずに(void*)キャストしてしまえばいいのかもしれませんが,する必要がないところまでキャストはしたくないですね。
まだスッキリしないので自分でも調査継続中です。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    記事検索    ログ    タグ一覧




掲示板提供:Real Integrity