• ベストアンサー

ポインタに ~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#2045
noname#2045

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

  • ベストアンサー
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

  • ポインタのポインタ

    こんにちは。 C言語の「ポインタのポインタ」の学習中なのですが、以下のプラグラムがエラーが出てしまします。 if文が間違っていると思うのですが、具体的に何がどう間違っているのがわかりません。 ご教示お願いいたします! #include<stdio.h> int main(void) { int date = 300; int *pdate = NULL; int **ppdate = NULL; printf("dateの値は%d<\n", date); pdate = &date; printf("*pdateの値は%d<\n", *pdate); if(**ppdate = NULL){ printf("**ppdateには何も与えられていません。\n"); } else{ return 0; } ppdate = &pdate; if(**ppdate == 300){ printf("**ppdateの値は%dになりました。正常です。\n", **ppdate); } /*強制終了を避けるためのプログラム*/ int i; scanf("%d", &i); return 0; }

  • ポインタのsizeofについて

    C初心者です。 ポインタ宣言させた変数をsizeof()で値を取得させて 表示させてみました。 char *cp; short int *sp; int *ip; i = sizeof(cp); printf("%d\n",i); i = sizeof(sp); printf("%d\n",i); i = sizeof(ip); printf("%d\n",i); 結果は全て4となりました。 これはなぜですか? (ただの変数として宣言すれば1、2、4となります。この理由も理解できています。)

  • ポインタの文法

    現在自分が勉強している中で、ポインタを宣言値を出力するときの文法は「pa」がポインタだとすると、 int *pa , printf("%p\n",pa) , printf("%d\n",*pa) などですが、sizeof(pa*)は後ろに「*」がついており後ろに「*」がつくときの意味が分かりません。 是非ご教示願います。

  • ポインタと配列

    次のソースで、結果表示でポインタを使いたいのですが、うまくいきません。1件しか表示されないのです。 ポインタの扱いがおかしいのだと思いますが、どうしたらよいでしょうか? #include <stdio.h> #include <string.h> int search(char key[256],FILE *fp,char *result[256][256]); main(void) { FILE *fp; int rep,n,i; char x[256],key[256],*result[256][256]; printf("検索キーワードを入力してください。\n" "キーワード>"); gets(key); if((fp=fopen("personal.txt","r"))==NULL) { printf("ファイルをオープンできません\n"); exit(1); } printf("=====検索結果=====\n"); n=search(key,fp,result); for(i=0;i<n;i++) { printf("%s\n",result[i]); } printf("検索結果:%d件です。\n",n); fclose(fp); } int search(char key[256],FILE *fp,char *result[256][256]) { int n=0; char *p,word[256],*name; while((p=fgets(word,256,fp))!=NULL) { if(strstr(word,key)!=NULL) { name=strtok(p," "); strcpy(result[n],name); n++; } } return n; } 実行すると、下の警告がでます。 illegal pointer combination(param)

  • ポインタ配列の動的確保

    ポインタの配列の動的確保について教えてください。 入力した数値をポインタ配列に入れるプログラムです。 下記のように書いてみました。(見づらくてごめんなさい) #include<stdio.h> #include<stdlib.h> #define kensu 3 main() { char abc[kensu+1]={'A','B','C','\0'}; char *ptr[kensu]; int i; printf("3つの整数を入力して下さい。\n"); for(i=0;i<kensu;i++){ ptr[i]=(char*)malloc(sizeof(char)*10); if(ptr[i]==NULL){ printf("メモリの取得に失敗しました"); exit(1); } printf("整数%c:",abc[i]); fgets(ptr[i],10,stdin); if(ptr[i][strlen(ptr[i])-1]=='\n') ptr[i][strlen(ptr[i])-1]='\0'; } for(i=0;i<kensu;i++) free(ptr[i]); } ちゃんと動いているようです。 しかし、ポインタ配列の動的確保をネットで調べてみると、ポインタのポインタ(?)を使って、下記のように2度mallocしています。 #include <stdio.h> #include <stdlib.h> #define N 3 int main(void) { char** arr; int i,j; arr = (char**)malloc(N * sizeof(char*)); /* ポインタ配列を確保 */ /* 配列の要素それぞれにつき、メモリ領域を確保 */ for(i=0;i<N;i++) arr[i] = (char*)malloc(N * sizeof(char));   ・・・ ポインタの配列を宣言して、配列の各要素に動的確保するのと ポインタのポインタを宣言し、ポインタ配列を動的確保して、再度配列の要素に動的確保するのとでは、何か違いがあるのでしょうか? ポインタのポインタを宣言し、ポインタ配列を確保する必要性が良く分かっていないのです。 ネット等で調べて見たのですが、理解力がないのかよく分かりませんでした。 どうか教えてください。

  • ポインタ勉強中です。しかも実行するとおかしいです。

    <本に載ってたソース> #include<stdio.h> #include<string.h> int main() { char msg[20]; char *str=NULL; int i; int cnt; str=&msg[0]; printf("文字を入力してください"); scanf("%s",&str); cnt=strlen(msg); str=msg+cnt; for(i=cnt;i>=0;i--){ printf("%c",*(str--)); } printf("\n"); return 0; } char *str=NULL;は、ポインタstrを空にするということでしょうか? いつもながらstrlenとsizeofが混じります。 sizeofがバイトの大きさで、strlenが、文字数でしたっけ?

  • char型+char型ってint型? if(char型==int型)?

    C言語の「汎整数拡張(インテグラルプロモーション)」というものに関するものだと思います。 char型とchar型を加えた結果は、char型でしょうか。それともint型でしょうか。 (下のプログラムの printf("sizeof(a[0]+a[1])は%d\n", sizeof(a[0]+a[1])); /* char型+char型 */ という部分の結果は4なので、int型と考えるべきなのかな。) 私は、char型とint型の加算の結果はint型だと思っていましたが、 char型とchar型の加算の結果はやはりchar型だと思っていました。 (それが間違えているのでしょうか。) if(a[0]==i) /* char型とint型の比較(?) */ の部分では、左辺はchar型、右辺はint型ですが、このように型の違う変数を比較しても文法上構わないのでしょうか。 (私は、「比較は必ず型の同じもの同士でしかできない」と思っていました。) 左辺はchar型のように見えて、じつはint型ですか。 #include <stdio.h> int main(void) { char a[4]; int i=77; printf("sizeof(int)は%d\n", sizeof(int)); printf("sizeof(char)は%d\n", sizeof(char)); printf("sizeof('M')は%d\n", sizeof('M')); printf("sizeof(a[0])は%d\n", sizeof(a[0])); a[0]='M'; a[1]=7+6; a[2]=a[0]+a[1]; printf("sizeof(a[0]+a[1])は%d\n", sizeof(a[0]+a[1])); /* char型+char型 */ printf("sizeof(+a[0])=%d\n", sizeof(+a[0])); if(a[0]==i) /* char型とint型の比較(?) */ puts("a[0]==i"); else puts("a[0]!=i"); return(0); } ちなみにワーニングもエラーもなんにもでません。

  • ポインタへの代入

    ポインタにアドレスを代入するには、0xは必要なのでしょうか? 手持ちの参考書には以下のようなプログラムがあります。 int *pa = 0; pa++; printf("pa...%p\n",pa); 最初の行は int *pa = 0x0; にすべきだと思うのですが。またprintfの,以降のpaになぜ&がいらないのでしょうか?

  • ポインタについて

    今初めてポインタというものを勉強しております。 よろしくお願いします。 ◎1---------------------------------- #include<stdio.h> int main(void) { int mydt=1234; int *pt; pt=&mydt; printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } --------------------------------------- ◎1のようにmydtのアドレスをポインタptに代入すれば、このプログラムは正常に動きました。 ◎2----------------------------------- #include<stdio.h> int main(void) { int mydt=1234; int *pt=&mydt; printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } ---------------------------------------- ◎2で「int *pt=&mydt;」があまりどういう意味かはわかりませんが、これも正常に動きました。 ◎3------------------------------------ #include<stdio.h> int main(void) { int mydt=1234; int *pt; *pt=&mydt printf("*pt=%d\n",*pt); printf("&mydt=%p\n",&mydt); return 0; } -------------------------------------- ◎3のように◎2と違って「*pt=&mydt」の代入を後から行うと、「'=' : 'int *__w64 ' から 'int' に変換できません。」といったようなエラーが起きてしまいます。 ◎1と◎2の違い、後何故◎3はダメなのかがわかりません。 教えていただけると嬉しいです。 後補足として、配列とポインタについてですが、 ◎4------------------------------ char ss[10]="ABCDE"; char *ssp=ss; --------------------------------- ◎5---------------------------- char ss[10]="ABCDE"; char *ssp; ssp=ss; -------------------------------- ◎4と◎5も同じような事だとは思いますが違いを教えていただけると嬉しいです。 よろしくお願いします。

  • 構造体内のポインタのポインタについて

    ポインタを理解するために以下のようなテストプログラムを作りました。 test.h --- typedef struct i_info{ int i_id; char i_name[64]; } I_INFO; typedef struct j_info{ int j_id; char j_name[64]; } J_INFO; typedef struct k_info{ int k_id; char k_name[64]; } K_INFO; typedef struct info{ int id; char name[64]; I_INFO iinfo; J_INFO *jinfo; K_INFO **kinfo; } INFO; --- test.c --- 1 #include <stdlib.h> 2 #include <stdio.h> 3 #include "./test.h" 4 5 int main(int argc, char **argv) 6 { 7 INFO info; 8 J_INFO j; 9 K_INFO k; 10 K_INFO *pk=NULL; 11 12 memset (&info,NULL,sizeof(info)); 13 memset (&j,NULL,sizeof(j)); 14 memset (&k,NULL,sizeof(k)); 15 16 info.id = 1; 17 memcpy(info.name,"***",3); 18 19 info.iinfo.i_id = 2; 20 memcpy(info.iinfo.i_name,"*i*",3); 21 22 info.jinfo = &j; 23 j.j_id = 3; 24 memcpy(j.j_name,"*j*",3); 25 26 info.kinfo = &pk; 27 pk= &k; 28 k.k_id = 4; 29 memcpy(k.k_name,"*k*",3); 30 31 printf( "%d\n",info.id); 32 printf( "%s\n",info.name); 33 printf( "%d\n",info.iinfo.i_id); 34 printf( "%s\n",info.iinfo.i_name); 35 printf( "%d\n",info.jinfo->j_id); 36 printf( "%s\n",info.jinfo->j_name); 37 /* 38 printf( "%d\n",info.kinfo->k_id); 39 printf( "%s\n",info.kinfo->k_name); 40 */ 41 } --- 38,39行目をコメントアウトするとコンパイルは通るのですが、 そのままだとコンパイルエラーになります。 なぜいけないのでしょうか?理由を教えてください。

専門家に質問してみよう