NULLポインタが0でない処理系とは?

このQ&Aのポイント
  • C言語の規格上、NULLポインタは0とは限らない処理系も存在する。
  • C99の仕様書ではNULLポインタを表す定数は0か(void*)0とされているが、これに反する処理系もある。
  • 一部の処理系では、構造体のポインタメンバは明示的に初期化しないとNULLポインタにならない。
回答を見る
  • ベストアンサー

NULLポインタが0でない処理系とは?

http://okwave.jp/qa/q7458540.html にて、 > C言語の規格上、0はNULLポインタとして扱われますが > NULLポインタは0とは限らないので(0でない処理系もあります)。 と他の回答者が答えていたのが妙に引っかかりました。 後学のために知っておきたいのですが、 どの処理系だとNULLポインタが0ではないのかどなたかご存じないですか? 気になって、C99の仕様書らしいものを検索で見つけたのですが、 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf これによると、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でない値にした目的や歴史的経緯があるのではないかと推測するのですが。

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

  • ベストアンサー
  • wormhole
  • ベストアンサー率28% (1619/5653)
回答No.5

日本語訳版 http://www.kouno.jp/home/c_faq/c5.html C FAQ、最近は読まれないのかなぁ

hanabutako
質問者

お礼

ありがとうございます。 C FAQ 自分は初耳でした。

hanabutako
質問者

補足

ベストアンサーが一番上に表示されると思うので、ここにまとめを書きます。 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/projects#9899

その他の回答 (9)

  • wormhole
  • ベストアンサー率28% (1619/5653)
回答No.10

>それにしても、80*86以外、寡聞にて聞かないのですが、(void*)0が全部のビット0となっていないアーキテクチャに向けて新しいプログラムを書く機会はそんなにあるんですか? おそらくないと思います。 でも絶対にないともいいきれないと思います。 >80*86もデータポインターと関数ポインターでサイズが違うと、void*型を引数に取る関数を使うときに困りそうなのですがどうしているのでしょうか? データポインタとコードポインタを同じに扱うことはほとんどないと思いますので データ用とコード用分けたりとかするんじゃないでしょうか。 >それとも、互換性のために考慮しておく必要があることの一つということでしょうか? 「互換性のため」という機会はあまりないと思いますが0とNULLを混同しないことは大事だと思いますよ。 長年プログラマやってる人でも char buf[100]; memset(buf, NULL, sizeof(buf)); や buf[0] = NULL; を平気で書く人いますし。

hanabutako
質問者

お礼

(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=getaddrinfo&sektion=3

hanabutako
質問者

補足

自分のお礼にある > もちろん、現実にこれをやる場合は、pthread_createなどと同じくvoidへのポインター型の引数をとる関数を引数にした方がよいと思いますが。 ですが、intを引数にとる関数へのポインターの方がいいですね。 pthread_createだと任意の処理をできるようにvoid *(*start_routine)(void *)をとりますが、先の例だと必ずint型の値を引数にとる関数とわかっていますから。

  • wormhole
  • ベストアンサー率28% (1619/5653)
回答No.9

>(void*)0もポインター型に代入される0もNULLポインターと同じ値ではあるけれど、それ以外の型の0が必ずしもNULLポインターと同じ値とは限らないと言うことですね。 そうではなくて、0をポインタにキャストするときにNULLポインタにコンパイラが読み替えてくれるということです。

hanabutako
質問者

お礼

視点の違いではないかと思います。 コンパイラから出力されるバイナリ、あるいは実行中の取り扱いについての説明だと、自分の書いた文でいいのではないかと思うのですが。 コンパイラの動きについての説明だと、コンパイラが抽象構文木を作るところで、宣言あるいは左辺値から型を決め、ポインター型だった場合はNULLポインターとして扱うのでしょうけれど。 実際の例としては、gccで-fdump-tree-allとかしてみたときに0となるか0Bとなるかでしょうか。

  • wormhole
  • ベストアンサー率28% (1619/5653)
回答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)); は必ずしも同じではないのじゃないかと。 間違ってたらごめんなさい。

hanabutako
質問者

お礼

総合して考えると、下記の二つは必ずしも同じでは無いと思います。 memset(sTest01,0,sizeof(sTest01)); memset(sTest01,'\0',sizeof(sTest01)); しかし、(int)0から変換されてできた(unsigned char)0と'\0'が同じ内部表現でないアーキテクチャを作ると、glibcなど仕様通りの動きをしないライブラリーが出てくるのではないかと。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.7

少なくとも C において memset(sTest01, 0, sizeof(sTest01)); と memset(sTest01, '\0', sizeof(sTest01)); には「見た目」以外に違いがないはずでは>#6.

hanabutako
質問者

お礼

http://www.freebsd.org/cgi/man.cgi?query=memset&sektion=3 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と同じような動きになるはずです。

  • wormhole
  • ベストアンサー率28% (1619/5653)
回答No.6

自分で書いておいて何ですが・・・ >memset(sTest01, 0, sizeof(sTest01)); /* bzero(sTest01, sizeof(sTest01)); と同義 */ これ嘘ですね・・・ memset(sTest01, '\0', sizeof(sTest01)); /* bzero(sTest01, sizeof(sTest01)); と同義 */ こうだ・・・

hanabutako
質問者

お礼

たしかに、何気なく0と'\0'が同じだと思っていましたが、'\0'はすべてのbitが0と先のC99の仕様書らしいものの5.2.1 Characters / 3節で書いてありますね。そして、これまでの話を総合すると、0がかならずしも'\0'と考えない方が良さそうですね。 POSIX.1でもそんなことが書いてますね。 http://pubs.opengroup.org/onlinepubs/007904875/functions/bzero.html ちなみに、これはPOSIX.1-2004で、将来bzeroが取り下げられることが予告されており、POSIX.1-2008からは消えたようです。 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/strings.h.html 一方、広く使われてそうなlibcの実装であるglibcを見ると、 http://sourceware.org/git/?p=glibc.git;a=blob;f=string/bzero.c;h=b7a88ec9253bdf0c189c11c663301359ac6b777b;hb=HEAD では 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がありますが。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

ちろっと調べたら http://www.lysator.liu.se/c/c-faq/c-1.html が見付かった.

hanabutako
質問者

お礼

ありがとうございます。 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*型を引数に取る関数を使うときに困りそうなのですがどうしているのでしょうか? それとも、互換性のために考慮しておく必要があることの一つということでしょうか?

  • wormhole
  • ベストアンサー率28% (1619/5653)
回答No.3

もう私が書くことはなさそうですが、 ソース上の整数値0が、NULLポインタを表すのは間違いないです。 ただしソース上の整数値0が、バイナリイメージ(メモリ上)で0とは限りません。 浮動小数点の規格であるIEEE754だとバイナリイメージで0は、数値+0なんですけどね。

hanabutako
質問者

お礼

ありがとうございます。 (void*)0もポインター型に代入される0もNULLポインターと同じ値ではあるけれど、それ以外の型の0が必ずしもNULLポインターと同じ値とは限らないと言うことですね。 符号ビットがあるからですよね。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

(void *)0 がヌルポインタ定数であることは間違いありません. しかし, この規定はあくまで「ソースプログラム上そう書く」というだけであり, 「ビット表現がどうなっているか」については全く関与しません. 一般論として, 「異なる型にキャストした場合」にビット表現が保存されることは保証されていないですよね.

hanabutako
質問者

お礼

ありがとうございます。 マシンの内部表現とプログラムの字面は違うと言うことですね。 でも、「異なる型にキャストした場合」は残念なことしか起こらない予感しかしないです。

  • notnot
  • ベストアンサー率47% (4846/10257)
回答No.1

特殊なCPUでポインタのビット表現としてオールゼロはあり得ないようなケースでしょうか。 例えばレジスタは32bitだけどアドレス空間は24bitで上位8ビットを別の目的で使っているとか。 なお、NULLポインタは内部表現がどうであれ、数値0と比較すると常に等しいし、ポインタに数値0を(キャストして)代入するとそのアーキテクチャのNULLポインタのビットパターンに変換して代入してくれるはずなので、#define NULL 0 はどんな場合にでも使えるはずです。

hanabutako
質問者

お礼

ありがとうございます。 思わずLISP処理系の実装を連想してしまいました。(あちらは下位bitを別の目的に転用するという話ですが) プログラムの字面で0と書いてあるものが、必ずしもマシンの内部でbitがすべて0になった値となっているとは限らないということですね。ポインター型の値との演算を行っていることを検出してコンパイラーが勝手にマシン内でNULLポインターを表す値に変えたマシン語を出すということですね。 int型のポインター型にキャストされている値を+1すると、コンパイル後のマシン語では1でなく、sizeof(int)足されているようなものでしょうか。

関連するQ&A

  • #define NULL ((void *)0) の弊害

    よく話題にされるヌルポインタについての疑問です。 定数の0は、それがポインタと解されるべき文脈では ヌルポインタに読み替えられますが、 可変長引数のようにポインタであることがコンパイラには判断できない文脈では、 明示的にキャストしてやらなければなりません。 このとき、#define NULL 0 と定義されている処理系では、 NULLを使っても定数の0を書いたのと全く同じであり、 上のような場合におけるキャストの必要性からは逃れられません。 しかし、たまたま自分の処理系で #define NULL ((void *)0) と定義されていれば、 キャストを行わなくてもNULLを使うことによって正しく動いてしまいます。 ということは、#define NULL ((void *)0) と定義された処理系しか 使ったことの無いプログラマは、 「NULLを使うこと自体が、これはポインタだよという意志表示になる」 と錯覚してしまう危険性をはらんでいることになります。 この人の書いた「NULLを使い、必要なキャストを省略しているソース」を、 #define NULL 0 と定義された処理系でコンパイルすると 正しく動作しない可能性があります。 こういう弊害があるにもかかわらず、 ANSI Cでは #define NULL 0 のほかに #define NULL ((void *)0) も許しているのは、 一体なぜなのでしょうか。 メリットもあるのでしょうか?

  • ダブルクォートで囲まれた文字列の取り出し

    Windows環境でRuby 1.8.7を使用しております。 ----sample1.txt----- hoge"foo\"bar\"foo"hage"bar\"baz\"bar"hoge ------------------ ファイルから1行ずつテキストを読み込んで処理を行います。上のsample.txtの様な行を読み込んだ場合にダブルクォートで囲まれた部分 foo\"bar\"foo bar\"baz\"bar を取り出すのにはどの様な正規表現を用いればよろしいでしょうか? エスケープされたダブルクォートを除ける上手い方法がわかりません。 また、ダブルクォートで囲まれた部分が複数行にまたがる場合はどの様に処理をすれば良いでしょうか? -----sample2.txt----- hoge"foo \"bar\" baz" hage ---------------------- -----sample3.txt----- hoge"foo \"ba r\"baz" hage ---------------------- 最初の例のように1行の場合は File.foreach(file) do | line | …… end で良いのですが、複数行の場合には同じように単純には行きません。 ダブルクォートの数を数えて、奇数の場合は偶数になるまで次行をくっつける様な処理を考えているのですが、もっと適切な方法はありますか? 宜しくお願いします。

  • C言語のNULLについて

    C言語のNULLについて教えてください。 ・NULLは、アドレスの0番地であり、参照先が無い(どこのメモリ箇所も指していない)と  言う認識でいます。 そこで、仮に下記の処理を行った場合どうなるか試してみました。 ------------------------------------------ stddef.h (void *)0) ------------------------------------------ char *test; test = NULL; printf("[%p]\n", test); // 出力 [00000000] printf("[%s]\n", test); // 出力 [] memcpy(test, "123", 3); // メモリフォルト ------------------------------------------ 予想としては、%sの処理でメモリフォルトとなると思っていたのですが 実際は、メモリフォルトが起こらなかったです。 と言う事は参照先が何かしらあったって事なのでしょうか?

  • stddef.hって何?

    stddef.hっていうのは 処理系に依存する型とマクロの定義。 ptrdiff_t - ポインタ減算の結果の型 size_t - sizeof 演算子の結果の型 wchar_t - ワイド文字型 NULL - 空ポインタ定数マクロ offsetof - 構造体のフィールドのオフセットを算出するマクロ という事だと思うんですが(思うんですがというか引用ですが) size_tとかこのヘッダファイルインクルードしないでも使えますよね?? なんでなのでしょうか・・。 たとえばインクルードなしで void main(){ size_t i; } でコンパイルしても通ります。stddef.hをインクルードしないといけないときはどんなときなんでしょうか?

  • ポインタの扱い

    すみません、構造体へのポインタの配列の扱いに困っています。 下記ソースの struct list *hashtable[HASHSIZE]; の箇所をmain部に入れた場合の処理がうまくいきません。。 どのように修正すれば良いのでしょうか。 申し訳ありませんが教えてください。 --------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #define HASHSIZE 40 #define MAX_KW_LEN 256 #define NUM_KW 23 #define TRUE 1 #define FALSE 0 struct list { char keyword[MAX_KW_LEN]; struct list *next; }; struct list *hashtable[HASHSIZE]; static char kw[NUM_KW][MAX_KW_LEN] = { "auto", "break", "double", "enum", "char", "continue", "extern", "float", "for", "int", "long", "register", "short", "signed", "static", "struct", "typedef", "union", "unsigned", "return", "void", "volatile", "while" }; int Hash(char *key); void InitHTable(void); int FindKeyWord(char *key); void ListKeyWord(void); void FreeKeyWord(void); int main(void); int Hash(char *key) { int hashval = 0; while (*key != '\0') hashval += *key++; return (hashval % HASHSIZE); } void InitHTable(void) { int i; struct list *p, *q; int hashval; for (i = 0; i < NUM_KW; i++) { printf("%d\n",i); if ((FindKeyWord(kw[i])) == FALSE) { if ((p = (struct list *)malloc(sizeof(struct list))) == NULL) { fprintf(stderr, "メモリ不足です。\n"); exit(2); } strcpy((*p).keyword, kw[i]); hashval = Hash(kw[i]); if (hashtable[hashval] == NULL) { hashtable[hashval] = p; p->next = NULL; } else { q = hashtable[hashval]; while (q->next != NULL) { q = q->next; } q->next = p; p->next = NULL; } } } } int FindKeyWord(char *key) { struct list *p; for (p = hashtable[Hash(key)]; p != NULL; p = p->next) if (!strcmp(key, (*p).keyword)) return (TRUE); return (FALSE); } void ListKeyWord(void) { int i; struct list *p; for (i = 0; i < HASHSIZE; i++) for (p = hashtable[i]; p != NULL; p = p->next) printf("予約語:%s ハッシュ値:%d:\n", (*p).keyword, Hash((*p).keyword)); } void FreeKeyWord(void) { int i; struct list *p, *q; for (i = 0; i < HASHSIZE; i++) for (p = hashtable[i]; p != NULL; ) { q = p->next; free(p); p = q; } } int main(void) { char word[MAX_KW_LEN]; int i; InitHTable( ); ListKeyWord( ); for (i = 0; i < 4; i++) { printf("Cの予約語を入力して下さい "); fgets(word, 128, stdin); if ((FindKeyWord(word)) == TRUE) printf("%s は登録済みです。\n", word); else printf("%s は未登録です。\n", word); } FreeKeyWord( ); }

  • Javaの定数の定義について、教えてください

    Android StudioでAndroidアプリを作っています。 使用言語はJavaです。 現在コーディングしている部分では定数を定義しているんですが、 例えば以下の3つの定数があるとして、 final HOGE = 1; final FOO = 2; final BAZ = 3; あとから、2つ目と3つ目の間に filnal BAR = 3; という違う定数を入れたいとします。 そうすると、 filnal BAZ = 4; に手動で数値をずらさないといけません。 定数の定義の数が膨大になっていて、非常に面倒なのです! これを回避するのに、最適な方法を教えてください。 ちなみに、スクリプト言語のHSPでは、 #enam HOGE = 1 #enum HOO #enum BAR #enum BAZ ・・・と、列挙していくだけで、自動的にコンパイラがインクリメントしてくれるので、プログラマが悩まなくても良いようになっているんですが。 同じことって、できますか? 調べた限りでは、C言語でも、似たような事ができるみたいですけど。 Javaでも可能でしょうか?

  • ポインタと構造体

    C言語初心者です。 下のコードはリスト構造のサンプルコードを元に自分で書き直そうとしているコードです。(なので、現時点では不完全なところ(例えばfreeしてないとか)があるのと、自分で理解出来ていない箇所があります。) 実行すると、8から3までの値が一応表示されるようになったですが、その過程の仕組みが自分でもよく理解出来ていません。 (1)tra *q = NULL; 通常、構造体のポインタを使用するときはq = &___のように他の構造体のアドレスを渡して使用出来るようにすると思いますが、ここではなぜ*qに、NULLを代入する必要があるのでしょうか。 (2)そのNULL状態のポインタqを関数printingdudeに突っ込んで結果的に8、7、6、、と出力されるまでの過程がよくわかりません。簡単に解説して頂けると助かります。(ちょっと雑な質問になってしまい申し訳ありません) typedef struct transcript{ int no; struct transcript *next; } tra; void printingdude(struct transcript *m); tra* noud(int v, tra* c); int main(){ tra *q = NULL; int i; for(i = 8; i>1; i--){ printingdude(q); q = noud(i, q); } return; } void printingdude(struct transcript *m){ if(m ==NULL) return; printf("%d\n", m->no); } tra* noud(int v, tra* c){ tra *a = (tra *) malloc(sizeof(tra)); a->no = v; a->next = c; return a; }

  • C言語の自己参照型プログラムについて

    #include <stdio.h> #include <string.h> #include <stdlib.h> struct list { int key; /* キー */ char name[20]; /* 名前 */ struct list *next; /* 次のデータへのポインタ */ }; struct list *add_list(int key, char *name, struct list *head); void show_list(struct list *p); void free_list(struct list *p); int main(void) { struct list *head; /* 先頭ポインタ */ char name[20]; int key = 0; head = NULL; /* 先頭ポインタにNULLを設定 */ printf("キーと名前(MAX:19文字)を入力(終了:CTRL+Z)\n"); while (scanf("%d %s", &key, name) != EOF) { /* リストにデータを登録 */ head = add_list(key, name, head); } /* リストの表示 */ show_list(head); /* リストの開放 */ free_list(head); return 0; } /*** リストにデータを登録 ***/ struct list *add_list(int key, char *name, struct list *head) { struct list *p; /* 記憶領域の確保 */ if ((p = (struct list *) malloc(sizeof(struct list))) == NULL) { printf("malloc error\n"); exit(EXIT_FAILURE); } /* リストにデータを登録 */ p->key = key; strcpy(p->name, name); /* ポインタのつなぎ換え */ p->next = head; /* 今までの先頭ポインタを次ポインタに */ head = p; /* 新たな領域を先頭ポインタに */ return head; } /*** リストの表示 ***/ void show_list(struct list *p) { while (p != NULL) { /* 次ポインタがNULLまで処理 */ printf("%3d %s\n", p->key, p->name); p = p->next; } } /*** リストの開放 ***/ void free_list(struct list *p) { struct list *p2; while (p != NULL) { /* 次ポインタがNULLまで処理 */ p2 = p->next; free(p); p = p2; } } これを実行すると、 新しく入力された順にリストが表示されます。 そうではなく、キーの昇順に表示したいです。 どなたかそのように実行できるようにプログラムを書き換えてくれませんか? 図々しいですがよろしくお願いいたします。m(_ _)m

  • ポインタに ~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を入れるということは、ヌルポインタであって、ポインタとして無効なんですよ」のようなこと。)

  • 構造体へのポインタ

    すみません、構造体へのポインタの配列の扱いに困っています。 下記ソースの struct list *hashtable[HASHSIZE]; の箇所をmain部に入れた場合の 他の関数内での使用の仕方が全くわかりません。 どのように修正すれば良いのでしょうか。 申し訳ありませんが教えてください。 --------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #define HASHSIZE 40 #define MAX_KW_LEN 256 #define NUM_KW 23 #define TRUE 1 #define FALSE 0 struct list {  char keyword[MAX_KW_LEN];  struct list *next;   /* 次の list へのポインタ */ }; struct list *hashtable[HASHSIZE]; /* ハッシュテーブル */ /* キーワード ( Cの予約語 ) */ static char kw[NUM_KW][MAX_KW_LEN] = {   "auto", "break", "double",   "enum", "char", "continue", "extern", "float", "for", "int",   "long", "register", "short", "signed", "static",   "struct", "typedef", "union", "unsigned", "return",   "void", "volatile", "while" }; int Hash(char *key); void InitHTable(void); int FindKeyWord(char *key); void ListKeyWord(void); void FreeKeyWord(void); int main(void); int Hash(char *key) {  int hashval = 0;  while (*key != '\0')   hashval += *key++;  return (hashval % HASHSIZE); } void InitHTable(void) {  int i;  struct list *p, *q;  int hashval;  for (i = 0; i < NUM_KW; i++) {   printf("%d\n",i);   if ((FindKeyWord(kw[i])) == FALSE) { /* 登録されていなかったら */             /* メモリを割り付ける */    if ((p = (struct list *)malloc(sizeof(struct list))) == NULL) {     fprintf(stderr, "メモリ不足です。\n");     exit(2);    }    strcpy((*p).keyword, kw[i]);    hashval = Hash(kw[i]);    /* ハッシュ値を求めて */        if (hashtable[hashval] == NULL) { /* 未登録なら */     hashtable[hashval] = p;  /* p の指すアドレスを登録 */     p->next = NULL;    /* リストの末尾に NULL を追加 */    }    else {        /* 既に登録していたら */     q = hashtable[hashval];     while (q->next != NULL) {  /* データがなくなるまで */      q = q->next;    /* リストをたどる */     }     q->next = p;     /* リストの末尾に p の指すアドレスを登録 */     p->next = NULL;    /* その末尾に NULL を追加 */    }   }  } } int FindKeyWord(char *key) {  struct list *p;  for (p = hashtable[Hash(key)]; p != NULL; p = p->next)   if (!strcmp(key, (*p).keyword))  /* 登録済みなら */    return (TRUE);     /* TRUE を返す */   return (FALSE);      /* 未登録ならFALSE を返す */ } void ListKeyWord(void) {  int i;  struct list *p;  for (i = 0; i < HASHSIZE; i++)   for (p = hashtable[i]; p != NULL; p = p->next) /* p が NULL でなければ */               /* ハッシュ値とキーワードを表示 */    printf("予約語:%s ハッシュ値:%d:\n", (*p).keyword, Hash((*p).keyword)); } /* malloc( ) で割り付けたメモリを解放 */ void FreeKeyWord(void) {  int i;  struct list *p, *q;  for (i = 0; i < HASHSIZE; i++)   for (p = hashtable[i]; p != NULL; ) { /* p が NULL でなければ */    q = p->next;      /* p->next を保存 */    free(p);       /* メモリを解放 */    p = q;        /* p->next を p に代入 */   } } int main(void) {  char word[MAX_KW_LEN];  int i;  InitHTable( );  ListKeyWord( );  for (i = 0; i < 4; i++) {   printf("Cの予約語を入力して下さい ");   fgets(word, 128, stdin);   if ((FindKeyWord(word)) == TRUE)    printf("%s は登録済みです。\n", word);   else    printf("%s は未登録です。\n", word);  }  FreeKeyWord( ); }

専門家に質問してみよう