C言語では、ポインタ型で表される値を、直接、int型の関数の返却値として指定できませんよね。

(具体例は最下行に表示)

なぜ、C言語はそのような仕様になっているのでしょうか?

両者を混同すると具体的にどのような不都合が生じるのでしょうか?
---------------------------------------------------------------------
例えば以下のような関数の2行目のreturnの括弧内の(int)を削除してmakeしようとすると、
コンパイラから、キャスト演算子が必要だと文句を言われますよね。

/* int cと同じ文字が*str内にあればその文字が格納されている場所を返す関数 */
int adstrchr(const char *str, int c){
while(*str++)if(*str==(char)c)return((int)str);
return(0);

回答の条件
  • URL必須
  • 1人2回まで
  • 登録:2008/12/01 18:31:47
  • 終了:2008/12/01 23:03:12

ベストアンサー

id:bellbind No.6

bellbind回答回数6ベストアンサー獲得回数12008/12/01 22:15:16

ポイント100pt

上の方々も言及しているとおり、int型とポインタ型のサイズがおなじ保障はありません。

実装的にlong型がほとんどの場合ポインタ型と同じサイズになりますが、ポインタ型の範囲を保障する整数型は、inttypes.h中のuintptr_tおよび、intptr_tとして定義されています。

このinttypes.hはISO C99仕様では標準になっています。

#include <stdio.h>
#include <inttypes.h>

int main()
{
  void * ptr = &main;
  uintptr_t ptrv = (uintptr_t) ptr;
  
  printf("main addr: %" PRIuPTR "\n", ptrv);
  return 0;
}

ポインタと整数を相互変換できるは、OSやドライバを書くようなシステムプログラミングでは便利な機能です。しかし、そうではない一般のアプリケーションプログラミングでは、相互変換する必要性はまずないでしょう。

id:Xenos

> ポインタ型の範囲を保障する整数型

があるのですね。

ご紹介ありがとうございます。

2008/12/01 22:51:44

その他の回答(5件)

id:pahoo No.1

pahoo回答回数5960ベストアンサー獲得回数6332008/12/01 18:52:24

ポイント100pt

話を簡単にするため、ポインタ char* で示されるデータは半角英数字からなる文字列、int で示される整数は16ビット符号付(範囲は-32768~+32767)という前提で回答します。


ポインタ char* で示される文字列も、int で示される整数も、CPUから見たらビットの並びに過ぎません。しかし、論理的な構造が異なります。

半角英数字1文字は8ビット、すなわち1バイトで表現されます。たとえば 1, 2, 3‥‥という文字列は、メモリ上では下記のようなデータ(16進数2桁=1バイト=1文字)が並んでいます。

31 32 33 ‥‥

これに対し、int は16ビット(2バイト)です。1, 2, 3‥‥という整数の並びは、下記のようになります。(Intel系CPUの場合)

01 00 02 00 03 00 ‥‥

"01 00" でワンセットで、整数の 1 を表します。


このようにメモリ上のデータ構造が異なることをCPUに知らせるため、C言語ではキャストという仕組みが備わっています。

文字列が日本語(2バイト)、整数が32ビット長になった場合も話は同じです。


蛇足ながら、JavaScriptやPHPなどの最近のスクリプト言語は、キャストをせずとも、プログラムの方で自動的に型判定を行ってくれます。しかし、ある条件下でキャストに失敗することがあり、これがシステムの脆弱性に繋がることがあります。なので、C言語と同様のキャストを指定することもできる仕様になっています。

個人的には、予想外のデータが入ってきたときにCPUが混乱しないよう、どんな言語であっても、キャスト指定はした方が良いと思います。


参考サイト

id:Xenos

メモリ上のデータ構造が異なるとは、その型の変数のサイズが異なる(=sizeof(型)が異なる)ということで良いのでしょうか?だとすると、質問文のソースの1行目の「const char *str」を「const int *str」にするとノーエラーでmakeできそうですね。後で試してみます。

2008/12/01 19:27:45
id:zzz_1980 No.2

zzz_1980回答回数492ベストアンサー獲得回数642008/12/01 18:52:25

ポイント33pt

1) sizeof(int) と sizeof(char *)が正しいという保証はありません。

% cat hoi.c
#include <stdio.h>
const char *adstrchar(const char *str,int c);
const char samplestrings[]="ABCDEFG";
int main(int argc,char *argv[])
{
        const char *s;
        s = adstrchar(samplestrings,(int)'C');
        if(s){
                printf("%c\n",*s);
        }else{
                printf("not found\n");
        }
        return 0;
}
const char *adstrchar(const char *str,int c)
{
        while(*str++)if(*str==(char)c) return (str);
        return (NULL);
% cc -Wall hoi.c -o hoi
% ./hoi
C
%

url はダミーです。

http://www.cs.princeton.edu/~bwk/

id:Xenos

ご回答ありがとうございます。質問文のソースを書き換えて以下のようにしてmakeを行ったところやはり、castが必要だとのエラーが返ってきました。

---------------

int adstrchr(const int *str, int c){

while(*str++)if(*str==c){return((int)str);}

return(0);}

-----

データ構造について詳しく調べたいと思います。

2008/12/01 19:27:23
id:dev_zer0 No.3

dev_zer0回答回数332ベストアンサー獲得回数252008/12/01 19:23:54

ポイント100pt

> C言語はそのような仕様になっているのでしょうか?

ポインタ型の実装を隠蔽したいからです。

# 多くの実装では単なるメモリアドレス値になってしまっていますが...


> 両者を混同すると具体的にどのような不都合が生じるのでしょうか?

今回は文字へのポインタ型ですが、他にも

・数値へのポインタ

・配列へのポインタ

・構造体型へのポインタ

とほぼポインタ型はほぼ無限に定義でき、かつ

違う型同士を比較や代入、返却することは大抵誤りであることが多いからです


int i = 100;

int *p = 100;

上下では意味が全く異なるのは理解できますよね?


あなたが作った関数で似たような関数が標準関数で存在します(strchr)が、その戻り値はchar *です。

http://www.linux.or.jp/JM/html/LDP_man-pages/man3/strchr.3.html


ドライバとかのかなり低レベルなI/Oで無い限り、ポインタ型と数値を混同するのは

百害あって一理なしです。

id:Xenos

ご回答ありがとうございます。

int i=100;はint型の変数iを整数100で初期化。

int *p = 100;は pを100番地?を指すポインタとして定義する。

ということでしょうか?int *pがちょっと自信がないですが。

> ドライバとかのかなり低レベルなI/Oで無い限り、ポインタ型と数値を混同するのは

百害あって一理なしです。

そうですね、自分にとってはポインタはややこしいので、きちんとキャストをつけようと思います。

2008/12/01 19:35:48
id:quintia No.4

quintia回答回数561ベストアンサー獲得回数702008/12/01 20:03:35

ポイント33pt

内容的には2の回答と同じなのですがちょっと補足です。

int よりも char* の方がサイズが大きいことが普通にありえます。64bit環境では、ポインタ型が64bit、int型が32bitになります。

http://www.cc.u-tokyo.ac.jp/publication/news/VOL9/No2/64bitmode....

http://docs.sun.com/app/docs/doc/806-0018/6j9dcmg4t?l=ja&a=view


この場合、 質問のソースでは「cと同じ文字が*str内にあればその文字が格納されている場所を返す関数」にはなりません。

intにキャストした時点で情報が落ちていますから、あとでchar*にキャストしても元のところを指し示す保証がないのです。この関数の型をintにするのは明確な間違いです。

id:Xenos

ご回答ありがとうございます。

64bit環境では、ポインタ型が64bit、int型が32bitになるのですね。

そうするとint型にしてはまずいですね。

後で試してみます。

2008/12/01 20:17:56
id:fuentebella No.5

fuentebella回答回数269ベストアンサー獲得回数302008/12/01 22:09:20

ポイント34pt

他の方の解答に加えて思いつくのは、Cでは、「NULLポインタ値が規定されていないので、NULLポインタの時キャストしないと0になるとは限らない」からというものあると思います。

http://www.sophia-it.com/content/%E3%83%8C%E3%83%AB%E3%83%9D%E3%...

id:Xenos

NULLは盲点でした。ありがとうございます。

2008/12/01 22:51:12
id:bellbind No.6

bellbind回答回数6ベストアンサー獲得回数12008/12/01 22:15:16ここでベストアンサー

ポイント100pt

上の方々も言及しているとおり、int型とポインタ型のサイズがおなじ保障はありません。

実装的にlong型がほとんどの場合ポインタ型と同じサイズになりますが、ポインタ型の範囲を保障する整数型は、inttypes.h中のuintptr_tおよび、intptr_tとして定義されています。

このinttypes.hはISO C99仕様では標準になっています。

#include <stdio.h>
#include <inttypes.h>

int main()
{
  void * ptr = &main;
  uintptr_t ptrv = (uintptr_t) ptr;
  
  printf("main addr: %" PRIuPTR "\n", ptrv);
  return 0;
}

ポインタと整数を相互変換できるは、OSやドライバを書くようなシステムプログラミングでは便利な機能です。しかし、そうではない一般のアプリケーションプログラミングでは、相互変換する必要性はまずないでしょう。

id:Xenos

> ポインタ型の範囲を保障する整数型

があるのですね。

ご紹介ありがとうございます。

2008/12/01 22:51:44

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません