C言語関係掲示板

過去ログ

No.1018 NULLのキャスト

[戻る] [ホームページ]
No.1317

NULLについて
投稿者---duke(2004/02/17 09:56:46)


初めておじゃまさせていただきます。
Dukeとでもよんでくださいまし。

私も一応C言語を喋ってご飯を食べる身です。

「うわぁ、がんばって作ってるな〜」
と思いつつどうも気になるので書かせていただきます。

NULLの扱いについてですが、各所においてキャストをしていないのが
非常に気になります。

どういったお考えでこのように記述しているかお教えいただければ幸いです。


No.1318

Re:NULLについて
投稿者---YuO(2004/02/17 11:07:46)


>NULLの扱いについてですが、各所においてキャストをしていないのが
>非常に気になります。

空ポインタ定数をキャスト?
無駄かつ無意味なキャストですな……。
何のためにキャストをするのか,非常に気になります。

No.1320

Re:NULLについて
投稿者---duke(2004/02/17 11:49:21)


>>NULLの扱いについてですが、各所においてキャストをしていないのが
>>非常に気になります。
>
>空ポインタ定数をキャスト?
>無駄かつ無意味なキャストですな……。
>何のためにキャストをするのか,非常に気になります。

他のかたからレスをいただいたので私の考えを先にお話しておきます。
まず、暗黙キャストの位置付けです。
C言語は型を大切にする言語ですので、暗黙キャストは認めない
ことが大切と考えます。(c++は積極的に使うがルール)

これとはまた別の理由としてNULLの定義にあります。
vc++などの製品はvoid *でNULLを定義していますが、組み込み系ではこれを
単純にint型の0としている場合もあります。
このような数値を単純に目的のポインタ変数と比較すると当然のことならが
型が違うという警告がでるはずです。

ですから
NULLは万能でありそのまま使えるは誤り
NULLは必ず目的の型にキャストして使う
がC言語でのお作法と考えます。

逆に暗黙キャストを認めるところは
putchar関数などに代表されるChar型のint拡張や

void *pa;
pa = (void *)((char *)pa + 1);

のようなVoid*型へのキャストの部分のみと考えたほうがいいでしょう。

特に初学者に対して型を意識させ、正しいC言語を認識させるには非常に
重要なポイントと考えます。





No.1322

Re:NULLについて
投稿者---YuO(2004/02/17 13:41:03)


>まず、暗黙キャストの位置付けです。
>C言語は型を大切にする言語ですので、暗黙キャストは認めない
>ことが大切と考えます。(c++は積極的に使うがルール)

そもそも,暗黙キャストとは何ですか?

型変換はキャストではないですし,変換を使わないというのであれば,
・関数指示子は全てポインタにキャストしてから呼び出す。
・shortやcharはintにして計算する。さらに,shortやcharへの代入は行わない。
などを当然行うんですよね。
#これらは全てJIS X 3010-1993の6.2 型変換に書かれている事柄。

また,
・式文は全てvoidにキャストする。
も当然する必要があるのですよね。
#JIS X 3010-1993 6.6.3 式文及び空文


>これとはまた別の理由としてNULLの定義にあります。
>vc++などの製品はvoid *でNULLを定義していますが、組み込み系ではこれを
>単純にint型の0としている場合もあります。
>このような数値を単純に目的のポインタ変数と比較すると当然のことならが
>型が違うという警告がでるはずです。

標準に準拠している限り,定数0をポインタに代入したところで警告は出ないです。
#出すような処理系に対しては,標準に対する準拠度を疑うべき。

空ポインタ定数の定義は,
「値0をもつ汎整数定数式又はその定数式をvoid *にキャストした式」
であり,空ポインタ定数をポインタに代入又はポインタと等値比較する場合には,
対象となる型に型変換されることが標準に記述されています。
#JIS X 3010-1993 6.2.2.3 ポインタ

そして,NULLは空ポインタ定数に展開されるオブジェクト形式マクロです。

ちなみに,C++では,汎整数定数式0が空ポインタ定数です。
そして,ISO/IEC 14882:1998だと4.10/4.11において,
任意のポインタに代入できることが定められています。

ちなみに,void *だから代入可能,ということではないです。
関数へのポインタとvoid *は変換不可能ですから,


>NULLは万能でありそのまま使えるは誤り
>NULLは必ず目的の型にキャストして使う
>がC言語でのお作法と考えます。

NULLをキャストして使うなんて言うことは,Cがわかっていない人がやることです。


>逆に暗黙キャストを認めるところは
>putchar関数などに代表されるChar型のint拡張や
>void *pa;
>pa = (void *)((char *)pa + 1);
>のようなVoid*型へのキャストの部分のみと考えたほうがいいでしょう。

なぜそれが特別なのですか?
変換であることにおいて,何が違うのかを説明して下さい。


>特に初学者に対して型を意識させ、正しいC言語を認識させるには非常に
>重要なポイントと考えます。

初学者に対してキャストを教えること自体が大の問題だと思いますが。
そもそも,「空ポインタ定数」という特殊な物に対して,
特殊性を考えずに一般的なことをぶつけるだけ無駄です。


No.1323

Re:NULLについて
投稿者---duke(2004/02/17 20:39:38)


>>まず、暗黙キャストの位置付けです。
>>C言語は型を大切にする言語ですので、暗黙キャストは認めない
>>ことが大切と考えます。(c++は積極的に使うがルール)
>
>そもそも,暗黙キャストとは何ですか?
>
>型変換はキャストではないですし,変換を使わないというのであれば,
>・関数指示子は全てポインタにキャストしてから呼び出す。
>・shortやcharはintにして計算する。さらに,shortやcharへの代入は行わない。
>などを当然行うんですよね。
>#これらは全てJIS X 3010-1993の6.2 型変換に書かれている事柄。
>
>また,
>・式文は全てvoidにキャストする。
>も当然する必要があるのですよね。
>#JIS X 3010-1993 6.6.3 式文及び空文
>
>
>>これとはまた別の理由としてNULLの定義にあります。
>>vc++などの製品はvoid *でNULLを定義していますが、組み込み系ではこれを
>>単純にint型の0としている場合もあります。
>>このような数値を単純に目的のポインタ変数と比較すると当然のことならが
>>型が違うという警告がでるはずです。
>
>標準に準拠している限り,定数0をポインタに代入したところで警告は出ないです。
>#出すような処理系に対しては,標準に対する準拠度を疑うべき。
>
>空ポインタ定数の定義は,
>「値0をもつ汎整数定数式又はその定数式をvoid *にキャストした式」
>であり,空ポインタ定数をポインタに代入又はポインタと等値比較する場合には,
>対象となる型に型変換されることが標準に記述されています。
>#JIS X 3010-1993 6.2.2.3 ポインタ
>
>そして,NULLは空ポインタ定数に展開されるオブジェクト形式マクロです。
>
>ちなみに,C++では,汎整数定数式0が空ポインタ定数です。
>そして,ISO/IEC 14882:1998だと4.10/4.11において,
>任意のポインタに代入できることが定められています。
>
>ちなみに,void *だから代入可能,ということではないです。
>関数へのポインタとvoid *は変換不可能ですから,
>
>
>>NULLは万能でありそのまま使えるは誤り
>>NULLは必ず目的の型にキャストして使う
>>がC言語でのお作法と考えます。
>
>NULLをキャストして使うなんて言うことは,Cがわかっていない人がやることです。
>
>
>>逆に暗黙キャストを認めるところは
>>putchar関数などに代表されるChar型のint拡張や
>>void *pa;
>>pa = (void *)((char *)pa + 1);
>>のようなVoid*型へのキャストの部分のみと考えたほうがいいでしょう。
>
>なぜそれが特別なのですか?
>変換であることにおいて,何が違うのかを説明して下さい。
>
>
>>特に初学者に対して型を意識させ、正しいC言語を認識させるには非常に
>>重要なポイントと考えます。
>
>初学者に対してキャストを教えること自体が大の問題だと思いますが。
>そもそも,「空ポインタ定数」という特殊な物に対して,
>特殊性を考えずに一般的なことをぶつけるだけ無駄です。

まず、修正させてください。
暗黙キャストではなく、暗黙の型変換ですね。
そう読み変えていただけるとありがたいです。

型変換やキャストが悪いというのではなく、それに対して
明示性を高くするべきと考えます。

また規約についてはおっしゃる通りですね。
6.3.16.1も書かないことの根拠になると思います。

では、お手数ついでに教えていただけますか?
どうして書いてはいけないのですか?


No.1325

Re:NULLについて
投稿者---NULL(2004/02/17 22:26:26)


横から失礼させていただきます。

duke さんの「初学者に対して正しいCを認識させる」という考えには
賛成です。

ですが、正しいCを認識させようと思ったならば、ヌルポインタのキャ
ストを教えるのは良くはないのではないかと思います。

というのも、C では「ポインターを書くべきところに現れた定数0は、
コンパイル時に適切なデータ型のヌルポインタに変換される」というの
が決まっているからです(ISO Sec. 6.2.2.3)。
つまり、

char *p = 0;
if (p != 0) {
....
}

は正しいコードであることを理解させる必要があると思います。
そして、「ポインターを書くべきところに現れた定数0」を意味する
マクロ定義 NULL も同様に考えなくてはいけないと思います。

つまり、ヌルポインタは通常のポインタと違い特別視しなくては
いけないものであり、それに通常のポインタの考えを持ってくる
のはよくないのではないかと思います。

この当たりのことは下記の FAQ に詳しく載っています。

「comp.lang.c FAQ」1.2
http://www.lysator.liu.se/c/c-faq/c-1.html
(日本語)「Language C FAQ (Japanese)」5.2
http://www.catnet.ne.jp/kouno/c_faq/c5.html#9


No.1326

Re:NULLについて
投稿者---NULL(2004/02/17 23:44:51)


ちょっと追加です。

私自身、ヌルポインタのキャストを書いてはいけないと思ってはいませ
んが、

int i = 1;
double d = (double)i;

といったキャストがあるコードを見たときと同じ感想を持ってしまい
ます。
こういったコードを見たとき、「明示性を高くしている」とは思わず、
「int は double に暗黙的型変換できることを知らないのか?」と思って
しまいます。

No.1327

Re:NULLについて
投稿者---duke(2004/02/18 00:02:31)


>ちょっと追加です。
>
> 私自身、ヌルポインタのキャストを書いてはいけないと思ってはいませ
>んが、
>
>int i = 1;
>double d = (double)i;
>
> といったキャストがあるコードを見たときと同じ感想を持ってしまい
>ます。
> こういったコードを見たとき、「明示性を高くしている」とは思わず、
>「int は double に暗黙的型変換できることを知らないのか?」と思って
>しまいます。

なるほど、確かに書いてしまえば冗長という考え方もありますね。
では、本当に暗黙的に型変換をしている状態と
実際には間違えている状態をどのように区別をつけるのでしょう。

意図を的確に伝えるためにも必要な行為と考えるのはいかがてしょうか?

No.1330

Re:NULLについて
投稿者---YuO(2004/02/18 00:37:04)


>なるほど、確かに書いてしまえば冗長という考え方もありますね。
>では、本当に暗黙的に型変換をしている状態と
>実際には間違えている状態をどのように区別をつけるのでしょう。

区別できないでしょうね。
しかし,それはキャストをしたところで同じことです。

コンパイラの警告を消すための誤ったキャストと,
何らかの意図を持ってキャストをしたことの間でどのように区別できるのでしょうか。


>意図を的確に伝えるためにも必要な行為と考えるのはいかがてしょうか?

0なりNULLなりをポインタに代入するなり等値比較するなりする行為は,
「そのオブジェクトを空ポインタに設定する」なり
「そのオブジェクトが空ポインタか否かを調べる」という意図が明確にわかります。
#最低でもちゃんとCを学んだ人間であれば。

しかし,キャストを付けたところで,もともと無意味な物ですから,
そのキャストを付けた意図は不明瞭です。

「わかりきったこと」に対して,無意味な装飾を加えることは,
逆にわかりにくくなるのです。


No.1331

Re:NULLについて
投稿者---NULL(2004/02/18 01:31:09)


>> YuO さん
そうですね。
私も YuO さんと同じ考えです。

私は、キャストというのは強力すぎる武器だと思っています。
キャストをすることで、コンパイラが親切に警告をしてくれたことを
ことごとく黙らせることが出来るからです。
ですから、極力使わず、本当に必要なときに使うのがよいと思ってい
ます。
これによって、YuO さんの仰る「何らかの意図を持ってキャストをした
こと」が明確になり、それが明示性を高くすることに繋がると思います。

あまりにたくさんのキャストを使っていると、これがその中に埋もれて
しまい見えにくくなると思います。


No.1328

Re:NULLについて
投稿者---NykR(2004/02/18 00:03:31)


>>>C言語は型を大切にする言語ですので、暗黙キャストは認めない
>>>ことが大切と考えます。(c++は積極的に使うがルール)

C言語は型の検査が甘い言語です。スカラ型であれば、キャスト演算子を使って、
どんな型にでも好きに型変換することができます。
ということは、ソースコードを読む側に、キャストが正しく行われているかをチェックする手間をかけさせることになります。

また、余計なものを書くと、重要な部分が見えにくくなり、可読性が下がります。

よって、不要なキャストは書くべきではありません。

#キャストに限らずいえることですが


初学者に型を意識させることを考えるのなら、
どの型とどの型に互換性があるのか、演算によってどのように(暗黙の)型変換がなされるのか、ということを教えるべきで、
キャストのような *型付けを曖昧にする* 手法を、十分な準備もなしに教えるのはどうかと思います。

No.1329

Re:NULLについて
投稿者---YuO(2004/02/18 00:36:56)


【掲示板ご利用上の注意】を読んでいないようですが……
全文引用はやめましょう。


>>なぜそれが特別なのですか?
>>変換であることにおいて,何が違うのかを説明して下さい。

これについては説明してくれないのですね……。


>では、お手数ついでに教えていただけますか?
>どうして書いてはいけないのですか?

本質的に必要なキャストが埋もれるからです。

まぁ,Cにおいて本当にキャストが必要な場合なんてそうそう無いはずですが。


No.1342

Re:NULLについて
投稿者---duke(2004/02/19 23:20:52)


>【掲示板ご利用上の注意】を読んでいないようですが……
>全文引用はやめましょう。
>
この件に関してはまったくもって申し訳ないです。

解答の件ですが私のスタイルと思ってください。
根拠としてはみなさんがあげておられる部分とまったく同じです。

これでいってしまえはNULLに関してのキャストはなくてもいいと
いうことになります。

確かにキャストは皆さんがいう通り強力でいくらでもコンパイラを
だますことができます。
しかしならが、逆もまたいえるわけです。
割と複雑なポインタに対して*をつけるとどのような型になるか
うまくイメージできない人たちへのトレーニングとしては非常に
有効であると考えます。

また、キャストを用いてコンパイラをだますことができるという
使い方そこ改める存在であり、そもそもそんな使い方をしてる人間は
c言語のプログラマとしては問題ありです。


そういうことができてしまうから
使わないほうが良いではなく、正しい型認識のトレーニングと
して使うという考え方はいかがでしょうか。

本当に重要なキャストが埋もれてしまうという指摘ですが、
これは周りにコンパイラをだますようなキャスト書いている
プログラマがいるかもという疑念が晴れば考える必要はないの
ではないですか。(重要ならコメント入れてもいいでしょうし)

コード自体にドキュメント性を持たせる上でも有効とかんがえますが
みなさんはどうでしょう?


No.1344

Re:NULLについて
投稿者---YuO(2004/02/19 23:49:55)


>根拠としてはみなさんがあげておられる部分とまったく同じです。
>しかしならが、逆もまたいえるわけです。

同じ?逆?

どの部分が同じで,どの部分の逆を言えると言いたいのですか?
そういうことを示すために引用を行うのです。


>割と複雑なポインタに対して*をつけるとどのような型になるか
>うまくイメージできない人たちへのトレーニングとしては非常に
>有効であると考えます。

どういう風に書くと,どのようなトレーニングになるのでしょうか。
そもそも,正しく*演算子を適用しておけば,
キャスト自体がいらないのですから。


>そういうことができてしまうから
>使わないほうが良いではなく、正しい型認識のトレーニングと
>して使うという考え方はいかがでしょうか。

「正しい型認識のトレーニング」という方法にキャストは使えませんよ。

キャストは型を強引に変換してしまうため,
型が正しくても正しくなくても通ってしまいます。


>本当に重要なキャストが埋もれてしまうという指摘ですが、
>これは周りにコンパイラをだますようなキャスト書いている
>プログラマがいるかもという疑念が晴れば考える必要はないの
>ではないですか。(重要ならコメント入れてもいいでしょうし)

そもそも,キャスト,特にC++でいうreinterpret_castに相当するキャストを使うなら,
全てコメントを入れるべきだと思います。

コンパイラの静的型システムによる保護を捨て去るのですから,
「何故そういうことをするのか」の説明は必要だと考えます。

Microsoft Windowsでプログラムを書くと,reinterpret_cast相当のキャストはいくらでも出てきますけどね。


>コード自体にドキュメント性を持たせる上でも有効とかんがえますが
>みなさんはどうでしょう?

ドキュメント性を持たせるためには,無意味な行動は行うべきではないです。

NULLのキャストは,コードのドキュメント性を下げます。
つまり,空ポインタ定数は特殊であるということをきちんと認識している人間にとって,
キャストがあると「何のためのキャストか」を考える必要が出てきます。

Cプログラマなら当然知っておくべきことを知らない人間に対して,
ドキュメント性を持たせてもしょうがないでしょう。
そのような事をやるなら,
i++; /* iの値に1を足してiに格納する */

という,馬鹿馬鹿しいことをしないといけませんから。