• ベストアンサー

ポインタに ~0を入れること

見かけたCのプログラムで、 ポインタに~0を代入するものを見ました。 そのプログラムをそのまま載せるのはわかりにくいので、 代わりに以下のプログラムを作って実行しました。 #include <stdio.h> int main(void) { char *pa[3]; int i; pa[0]=0; pa[1]=~0; pa[2]="Hello"; printf("sizeof(char*)=%d\n", sizeof(char*)); for(i=0; i<=2; i++) { if(pa[i]==NULL) printf("pa[%d] はNULLです。\n", i); if(pa[i]==(char*)0xFFFFFFFF) printf("pa[%d]は全ビット1です。\n", i); if(pa[i]==~0) printf("pa[%d]は~0です。\n", i); } return 0; } 結果 sizeof(char*)=4 pa[0] はNULLです。 pa[1]は全ビット1です。 pa[1]は~0です。 このプログラムはコンパイル時にエラーも警告も出ず、 動作も意図したとおりです。 pa[1]に入っている ~0 は、int型の定数なのでしょうか。 それならば、 pa[1]=~0; という代入や if(pa[i]==~0) という比較は 左辺はchar*型で右辺はconst int型であって型が異なりますが、 問題ないのでしょうか。 ~0は0の否定なので、全ビットは1なのでしょうけど、 int型(の定数)だと思います。 ~0というのは何か特別な値なのでしょうか。 ポインタに~0を入れるというのは、意味があるのでしょうか。 (例えば、「ポインタに0を入れるということは、ヌルポインタであって、ポインタとして無効なんですよ」のようなこと。)

質問者が選んだベストアンサー

  • ベストアンサー
noname#11476
noname#11476
回答No.3

懐かしいですね。最近はC++のおかげでポインタを駆使するプログラムは書かなくなりましたので、使うことは余りありませんけど。 先の回答者の方々が言われているように、ポインタが通常無効である場合はNULLでよいのですが、それ以外の情報を伝えたいことがあります。 そのときに、ありえないアドレスとして全ビット1の数値を使うことがあるのです。 で、なぜ ~0なのかというと、そのほうが移植性がよくなるからです。 最近ではポインタはみな32bitポインタが主流なので、そのようなプラットフォーム上では (char*)0xFFFFFFFF のような形でもよいでしょう。 しかし、たとえば 64 bit ポインタのシステム上では(char*)0xFFFFFFFFはうまく働きません。だって0xFFFFFFFFFFFFFFFFにしないと全ビット1でないから、場合によっては実アドレスである可能性もあるわけです。 そんなときに、(char *) ~0 としてあげると、全ビット1が保証されるのでやりやすいのです。 他の方法、たとえば(char *) ( (int) -1 )もうまく動くとは限りません。intのbit数とポインタのbit数は一致が保証されていませんし。 他には、char *p として p = NULL, p-- ならば保証できますが、代入で済むところをこのようなことをすると面倒です。 キャストしない場合、警告を出すコンパイラと出さないコンパイラがあるようですが、まあそれは別問題なので。特に特別なものとして扱われているわけではないと思いますよ。 あ、あと私がこの手法を使った目的は正確には思い出せませんけど、関数の戻り値として通常はポインタを返すのですが、リターンコードとしてNULL以外に情報を伝えたいときに使った記憶がありますね。 多分ポインタツリーの探索をする関数で次のItemを返すのだけど、ツリーの一番下まできたら~0を返して他の探索を要求して、探索する枝が全部なくなったとか、エラーが起きたときにNULLを返すとかですね。 では。

noname#2045
質問者

お礼

みなさんありがとうございました。 7月26日22時過ぎに締め切りました。

noname#2045
質問者

補足

丁寧にご説明いただきありがとうございます。 >char *p として p = NULL, p-- ならば保証できますが 私はこのようなことを考えもしたことなかったです。 =========================== ご回答を読んで、以下のプログラムを作ってみました。 私の環境では、コンパイル時にエラーも警告も出ないです。 (他の環境では違うんでしょうね。) #include <stdio.h> #include <limits.h> int main(void) { char *pa[3]; int i; printf("CHAR_BIT=%d\n", CHAR_BIT); printf(" sizeof(char*)=%d\n sizeof(int)=%d\n", sizeof(char*), sizeof(int) ); pa[0]=NULL; pa[1]=NULL; (pa[1])--; pa[2]=(char*)-1; for(i=0; i<=2; i++) { if(pa[i]==NULL) printf("pa[%d] はNULLです。\n", i); if(pa[i]==(char*)0xFFFFFFFF) printf("pa[%d]は全ビット1です。\n", i); if(pa[i]==~0) printf("pa[%d]は~0です。\n", i); } return 0; } 結果 CHAR_BIT=8 sizeof(char*)=4 sizeof(int)=4 pa[0] はNULLです。 pa[1]は全ビット1です。 pa[1]は~0です。 pa[2]は全ビット1です。 pa[2]は~0です。

その他の回答 (2)

回答No.2

私も特殊な状態を表すために使ってると思います。 古いソースを眺めていると0xFFFFFFFFや0xFFFFFFFEなどを特殊な状態 としてポインタに設定してたりするのを見かけます。defineされてたりね。 デバイスがらみのソースだとけっこうあるかも。 システムによって、ポインタのビット幅違うし、型変換、キャストだらけ のソースになったりするので私は好きではないです。 自分なら struct sample{ int status; char* ptr; } みたいな構造体(あるいはclassまで)作って、特殊な状態もポインタ変数1個で表すのでなく、 特殊な状態を別のメンバとして扱うほうを選択します。 単純な操作のライブラリだったら、ポインタ変数1個だけで表現できるので、 I/Oも楽にはなるでしょうけど。 状態の場合分けが少なくて、インターフェースをなるべく簡単にしたいなら、 提示されてるやり方を選ぶかもしれないけどね。

noname#2045
質問者

補足

>システムによって、ポインタのビット幅違うし、型変換、キャストだらけ >のソースになったりするので私は好きではないです。 そうですね。 ありがとうございました。

  • mojimojio
  • ベストアンサー率51% (14/27)
回答No.1

gccでコンパイルしてみましたが、9行目と22行目でちゃんとwarningが出ますね。 少なくともキャストしないと、文法上も問題があると思います。 元のプログラムでの~0の使用目的を勝手に推測してみますが、データ列のなかで~0を有り得ない値として識別用(番兵)に使っているのではないでしょうか。 例えば、任意の個数のポインタを格納する配列(ヌルポインタにも要素として意味がある)があって、その終端を識別するために~0を入れているとか。 このように単純な話なら要素数を別に持っておけば良いのですが、効率上の理由でポインタの番兵を使用したいケースなのかもしれません。 もしそうだとして、これがテクニックとして一般的なのかどうかは知りません。~0なんて使わずに、ダミーの変数を指す本物のポインタを使ったほうが問題ないとは思いますが。

noname#2045
質問者

補足

>9行目と22行目でちゃんとwarningが出ますね。 やはり出ましたか。 ありがとうございました。

関連するQ&A