
http://oshiete.goo.ne.jp/qa/7458540.html にて、
> C言語の規格上、0はNULLポインタとして扱われますが
> NULLポインタは0とは限らないので(0でない処理系もあります)。
と他の回答者が答えていたのが妙に引っかかりました。
後学のために知っておきたいのですが、
どの処理系だとNULLポインタが0ではないのかどなたかご存じないですか?
気になって、C99の仕様書らしいものを検索で見つけたのですが、
http://www.open-std.org/jtc1/sc22/wg14/www/docs/ …
これによると、6.3.2.3 Pointers の 3節 にて、
> An integer constant expression with the value 0,
> or such an expression cast to type void *, is called a null pointer constant.
とあり、7.17 Common definitions <stddef.h> の 3節 でも、
> The macros are
> NULL
> which expands to an implementation-defined null pointer constant; (略)
とあるので、NULLポインタを表す定数は0か(void*)0だと思っていたのですが。
言い換えると、
struct foo {
char* bar;
};
と構造体が定義されている時、
struct foo baz;
bzero(&baz, sizeof(baz));
すればbaz.barはNULLポインタ定数に初期化されると思っていたのですが、
これだとダメな処理系はどういう処理系で、どんな用途で使うのでしょうか?
おそらく、0でない値にした目的や歴史的経緯があるのではないかと推測するのですが。
No.5ベストアンサー
- 回答日時:
この回答への補足
ベストアンサーが一番上に表示されると思うので、ここにまとめを書きます。
Q1: どの処理系だとNULLポインタが0ではないでしょうか?
質問文の0をすべてのbitが0の値とすると、Honeywell-Bullのメインフレーム、Cyber 180シリーズなどが該当します。
(C FAQより)
Q2: bzero(&baz, sizeof(baz));すればbaz.barはNULLポインター定数に初期化されますか?
すべてのbitが0の値に初期化されるので、Q1の返答で書いたような処理系の場合にはNULLポインター定数と異なる値(すべてのビットが0の値)に初期化されます。
たとえば、06000がNULLポインター値として使われるHoneywell-Bullのメインフレームでは、bzeroされたポインター変数の値はNULLポインターの値になっていません。
Q3: NULLポインターがすべてのビットが0と同値でないアーキテクチャに向けて新しいプログラムを書く機会はありますか?
おそらく無いでしょう。しかし、絶対にないとは言えません。
いずれにせよ、0とNULLを混同しないことは大事だと思います。
本当はお三方全員をベストアンサーとしたいところではありますが、これから先に同じ疑問を持った人にも一番役に立つであろう回答はやはりC FAQの日本語版だと思いますので、wormholeさんをベストアンサーにしたいと思います。
C FAQ自体はTacosanさんの示しているところのほかに次のURLでも原典と思われるものを閲覧できるようです。
http://www.faqs.org/faqs/C-faq/faq/
http://c-faq.com/
しかしながら、C言語に関する資料としては、Tacosanさんの紹介されていたところがよくまとまっているように思います。
http://www.lysator.liu.se/c/
ここからたどって見つけましたが、ISO/IEC 9899:2011の最終原稿もここから入手できますね。
http://www.open-std.org/jtc1/sc22/wg14/www/proje …
No.10
- 回答日時:
>それにしても、80*86以外、寡聞にて聞かないのですが、(void*)0が全部のビット0となっていないアーキテクチャに向けて新しいプログラムを書く機会はそんなにあるんですか?
おそらくないと思います。
でも絶対にないともいいきれないと思います。
>80*86もデータポインターと関数ポインターでサイズが違うと、void*型を引数に取る関数を使うときに困りそうなのですがどうしているのでしょうか?
データポインタとコードポインタを同じに扱うことはほとんどないと思いますので
データ用とコード用分けたりとかするんじゃないでしょうか。
>それとも、互換性のために考慮しておく必要があることの一つということでしょうか?
「互換性のため」という機会はあまりないと思いますが0とNULLを混同しないことは大事だと思いますよ。
長年プログラマやってる人でも
char buf[100];
memset(buf, NULL, sizeof(buf));
や
buf[0] = NULL;
を平気で書く人いますし。
この回答への補足
自分のお礼にある
> もちろん、現実にこれをやる場合は、pthread_createなどと同じくvoidへのポインター型の引数をとる関数を引数にした方がよいと思いますが。
ですが、intを引数にとる関数へのポインターの方がいいですね。
pthread_createだと任意の処理をできるようにvoid *(*start_routine)(void *)をとりますが、先の例だと必ずint型の値を引数にとる関数とわかっていますから。
(void*)0と'\0'の内部表現が異なる環境の滅亡を祈ります。
> データポインタとコードポインタを同じに扱うことはほとんどないと思いますのでデータ用とコード用分けたりとかするんじゃないでしょうか。
こういうコードを書くのもどうかと思いますが、Cの規格としてはこういうコードを禁止していないと思いますので、コンパイラーを書く人も大変だなぁと。
(snip)
void
func(void *f)
{
void (*show)(int);
show = f;
show(10);
}
void
show(int x)
{
printf("value: %d\n", x);
}
int
main(void)
{
func(show);
return EXIT_SUCCESS;
}
もちろん、現実にこれをやる場合は、pthread_createなどと同じくvoidへのポインター型の引数をとる関数を引数にした方がよいと思いますが。
> 0とNULLを混同しないことは大事だと思いますよ。
その点は同意します。強く型付けされた言語をしばらく使うと、違う型への代入が気持ち悪くて仕方ありません。ただ、voidへのポインターにはC99仕様書らしきものの6.3.2.3 Pointers / 1節で許容されており、FreeBSD coding styleで返値のvoid*はキャストするなと書いているので、自分としては例外ですが。
余談ですが、このコードはコンパイラーが警告を出しませんか?
gccだと、c-typeck.cを通るときに警告が出ると思うのですが、ほかのコンパイラーではこういうチェックはしないのでしょうか。
さらに、余談ですが実用上問題ないですが、これのEXAMPLESを見ると修正したくなるかもしれません。
http://www.freebsd.org/cgi/man.cgi?query=getaddr …
No.9
- 回答日時:
>(void*)0もポインター型に代入される0もNULLポインターと同じ値ではあるけれど、それ以外の型の0が必ずしもNULLポインターと同じ値とは限らないと言うことですね。
そうではなくて、0をポインタにキャストするときにNULLポインタにコンパイラが読み替えてくれるということです。
視点の違いではないかと思います。
コンパイラから出力されるバイナリ、あるいは実行中の取り扱いについての説明だと、自分の書いた文でいいのではないかと思うのですが。
コンパイラの動きについての説明だと、コンパイラが抽象構文木を作るところで、宣言あるいは左辺値から型を決め、ポインター型だった場合はNULLポインターとして扱うのでしょうけれど。
実際の例としては、gccで-fdump-tree-allとかしてみたときに0となるか0Bとなるかでしょうか。
No.8
- 回答日時:
C言語では整数値の内部表現は規定されてないですよね。
0が内部表現オールビット0以外の処理系があるかもしれない。
でも
http://www.lysator.liu.se/c/c-faq/c-1.html
の1.10の5に「ASCIIのNULL文字はオールビット0」、6に「NULL文字('\0')」と書いてあるんですよ。
ということは
memset(sTest01,0,sizeof(sTest01));
と
memset(sTest01,'\0',sizeof(sTest01));
は必ずしも同じではないのじゃないかと。
間違ってたらごめんなさい。
総合して考えると、下記の二つは必ずしも同じでは無いと思います。
memset(sTest01,0,sizeof(sTest01));
memset(sTest01,'\0',sizeof(sTest01));
しかし、(int)0から変換されてできた(unsigned char)0と'\0'が同じ内部表現でないアーキテクチャを作ると、glibcなど仕様通りの動きをしないライブラリーが出てくるのではないかと。
No.7
- 回答日時:
少なくとも C において
memset(sTest01, 0, sizeof(sTest01));
と
memset(sTest01, '\0', sizeof(sTest01));
には「見た目」以外に違いがないはずでは>#6.
http://www.freebsd.org/cgi/man.cgi?query=memset& …
void * memset(void *b, int c, size_t len); とint型をとった上で、
(converted to an unsigned char)らしいので、そうかもしれません。
C99の仕様らしいものの6.3.1.3 Signed and unsigned integersの1節にWhen a value with integer type is converted to another integer type other than _Bool, if
the value can be represented by the new type, it is unchangedとあるので、int型の0をunsigned char型にcastした場合、unsigned char型の0になるはずで、これはlenが8以下の場合のglibcのbzeroと同じような動きになるはずです。
No.6
- 回答日時:
自分で書いておいて何ですが・・・
>memset(sTest01, 0, sizeof(sTest01)); /* bzero(sTest01, sizeof(sTest01)); と同義 */
これ嘘ですね・・・
memset(sTest01, '\0', sizeof(sTest01)); /* bzero(sTest01, sizeof(sTest01)); と同義 */
こうだ・・・
たしかに、何気なく0と'\0'が同じだと思っていましたが、'\0'はすべてのbitが0と先のC99の仕様書らしいものの5.2.1 Characters / 3節で書いてありますね。そして、これまでの話を総合すると、0がかならずしも'\0'と考えない方が良さそうですね。
POSIX.1でもそんなことが書いてますね。
http://pubs.opengroup.org/onlinepubs/007904875/f …
ちなみに、これはPOSIX.1-2004で、将来bzeroが取り下げられることが予告されており、POSIX.1-2008からは消えたようです。
http://pubs.opengroup.org/onlinepubs/9699919799/ …
一方、広く使われてそうなlibcの実装であるglibcを見ると、 http://sourceware.org/git/?p=glibc.git;a=blob;f= … では const op_t zero = 0;と((byte *) dstp)[0] = 0;となっていて、'\0'でなく、(op_t)0や(byte)0が使われているようで、これが'\0'と同じというのはどうやって保証しているのか疑問が残ります。一応、sysdeps/generic/memcopy.hに#define op_t unsigned long intとtypedef unsigned char byteがありますが。
No.4
- 回答日時:
ありがとうございます。
1.14: Seriously, have any actual machines really used nonzero null pointers, or different representations for pointers to different types?
ですね。
NULLポインターだからといって(レジスタに値として含まれている?)セグメントが0とは限らないということや、NULLポインターとして特殊な値を使っているということでしょうか。
それにしても、80*86以外、寡聞にて聞かないのですが、(void*)0が全部のビット0となっていないアーキテクチャに向けて新しいプログラムを書く機会はそんなにあるんですか?
80*86もデータポインターと関数ポインターでサイズが違うと、void*型を引数に取る関数を使うときに困りそうなのですがどうしているのでしょうか?
それとも、互換性のために考慮しておく必要があることの一つということでしょうか?
No.3
- 回答日時:
もう私が書くことはなさそうですが、
ソース上の整数値0が、NULLポインタを表すのは間違いないです。
ただしソース上の整数値0が、バイナリイメージ(メモリ上)で0とは限りません。
浮動小数点の規格であるIEEE754だとバイナリイメージで0は、数値+0なんですけどね。
ありがとうございます。
(void*)0もポインター型に代入される0もNULLポインターと同じ値ではあるけれど、それ以外の型の0が必ずしもNULLポインターと同じ値とは限らないと言うことですね。
符号ビットがあるからですよね。
No.1
- 回答日時:
特殊なCPUでポインタのビット表現としてオールゼロはあり得ないようなケースでしょうか。
例えばレジスタは32bitだけどアドレス空間は24bitで上位8ビットを別の目的で使っているとか。
なお、NULLポインタは内部表現がどうであれ、数値0と比較すると常に等しいし、ポインタに数値0を(キャストして)代入するとそのアーキテクチャのNULLポインタのビットパターンに変換して代入してくれるはずなので、#define NULL 0 はどんな場合にでも使えるはずです。
ありがとうございます。
思わずLISP処理系の実装を連想してしまいました。(あちらは下位bitを別の目的に転用するという話ですが)
プログラムの字面で0と書いてあるものが、必ずしもマシンの内部でbitがすべて0になった値となっているとは限らないということですね。ポインター型の値との演算を行っていることを検出してコンパイラーが勝手にマシン内でNULLポインターを表す値に変えたマシン語を出すということですね。
int型のポインター型にキャストされている値を+1すると、コンパイル後のマシン語では1でなく、sizeof(int)足されているようなものでしょうか。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- PostgreSQL DBFluteについて質問です。 環境:PostgreSQL java8 前提:webアプリケーショ 1 2022/07/07 00:49
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- MySQL テーブル作成です。どこかのスペルが間違っているか記号など スペースかな? 1 2022/10/01 05:08
- PHP PHPでCSVを出力するさいに、ループの中で前の行の値を変更したい 3 2022/10/27 17:44
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- 大学・短大 C言語線形リストの問題です 3 2022/12/22 00:45
- MySQL エラー 1068 (42000): 複数の主キーが定義されていますエラー 2 2022/11/17 04:36
- C言語・C++・C# スタックフレームの消滅 6 2023/05/20 12:33
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語のポインタに直接アドレス...
-
セグメントエラー
-
ポインタについて
-
Run-Time Check Failure #3とい...
-
戻り値で構造体を返すことは可...
-
ExcelVBAでのkernel32(64bit)
-
popenした子プロセスのプロセス...
-
トリプルポインタが必須!とな...
-
fopne で失敗する原因
-
LPSTR型の初期化について
-
関数ポインタの利点
-
C++で関数ポインタから関数名を...
-
C言語習得のレベル.
-
va_listを使用したfscanf()関数...
-
基本アルゴリズムの『返す』の...
-
nullポインタを逆参照とは?
-
参照送りした構造体のメンバ変...
-
構造体の中の構造体
-
アプリを32bitから64bit移行
-
ポインタを使うことのメリット...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
セグメントエラー
-
C言語のポインタに直接アドレス...
-
init関数の意味
-
Run-Time Check Failure #3とい...
-
戻り値で構造体を返すことは可...
-
ExcelVBAでのkernel32(64bit)
-
アプリを32bitから64bit移行
-
参照型で受け取った引数をポイ...
-
fopne で失敗する原因
-
PASCALとFARの意味
-
LPSTR型の初期化について
-
CWnd::EnableWindow()の扱い方
-
ポインタについて
-
プーさんのマウスポインタを教...
-
連結リスト 要素の入れ替え
-
ハンドルはポインタか
-
C++で関数ポインタから関数名を...
-
自作DLLの引数について、ポイン...
-
NULLポインタが0でない処理系と...
-
TCHAR文字列内の検索について
おすすめ情報