初めて質問させて頂きます。C言語初心者です。
実は講義で「ファイル中の英文を単語に分けてその出現頻度をカウントするコードを木構造を用いて出力せよ」という課題が出ました。
そこで、参考にするコードを検索しましたところ、以下のURLにあるベストアンサーのコードが近いと感じました。
http://oshiete.goo.ne.jp/qa/4155655.html
コードの内容は以下の通りになります。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct node Node;
struct node{
char *word;
int count;
Node *left,*right;
};
Node *root=NULL;
void compose(FILE *fp);
void inorder(Node *p);
void strlower(char *s);
int main(int argc, char *argv[]) {
FILE *fp;
Node *new;
fp=fopen(argv[1],"r");
if(fp==NULL){puts("ファイルを開けません");return(-1);}
compose(fp);
inorder(root);
return (0);
}
void strlower(char *s){
while(*s!=NULL){*s=tolower(*s);s++;}
}
void compose(FILE*fp){
Node **p,*new;
char buf[256];
while(1){
fscanf(fp,"%[^a-zA-Z0-9]",buf);
if(fscanf(fp,"%[a-zA-Z0-9]",buf)==EOF)break;
strlower(buf);
if(root==NULL){
new=(Node *)malloc(sizeof(Node));
new->left=NULL; new->right=NULL; new->word=strdup(buf); new->count=1;
root=new;
}else{
*p=root;
while(1){
if(strcmp(buf,(*p)->word)==0){
(*p)->count++;break;
}else if(strcmp(buf,(*p)->word)<0){
if((*p)->left==NULL){
new=(Node *)malloc(sizeof(Node));
new->left=NULL;new->right=NULL;new->word=strdup(buf);new->count=1;
(*p)->left=new;
break;
}else{
*p=(*p)->left;
}
}else{
if((*p)->right==NULL){
new=(Node *)malloc(sizeof(Node));
new->left=NULL; new->right=NULL; new->word=strdup(buf); new->count=1;
(*p)->right=new;
break;
}else{
*p=(*p)->right;
}
}
}
}
}
}
void inorder(Node*p){
if (p==NULL) return;
inorder(p->left);
printf("%s %d\n",p->word, p->count);
inorder(p->right);
}
しかし、これをそのままコンパイル・実行すると、コンパイル時に以下の注意が出ます。
warning
comparison between pointer and integer ('int' and 'char *')
while(*s!=NULL){*s=tolower(*s);s++;}
上記の注意を無視してそのまま実行すると、segmatation faultが出てしまいますorz
おそらく、sの型が*s=s[]なので、注意の中の「s++」の部分で誤作動を起こしている(s++を実行するにはsはint型でなければならない)と思うのですが、どうコード文を変えれば良いのかがよくわかりません。
どなたかお教え頂けると幸いです。どうぞよろしくお願いしますm(_ _)m
No.8ベストアンサー
- 回答日時:
> #4
#1です。ご指摘、ごもっとも。自動変数が初期化されるシステムでもダメですね。
> 質問者
warningはコンパイラからのツッコミなので文法的に是正していけばいいですが、segmentation faultや一般保護エラーは実行環境(OSとか)からのツッコミですので、実行環境であなたのプログラムが何か「悪いこと」をやらかしているわけです。
それが何かを理解する必要があるので、C言語の文法的な問題ではないのかもしれないという予想と、実行環境が何を嫌がっている(何をしたらシステムが発狂する)かを知るべきです。
ちなみにOSがないシステム(組み込み機器とか)なら誰からもツッコミが入ることなく、ただ暴走もしくはフリーズするだけですがね。
「int a;」という記述はコンパイラによって、とあるRAM領域のどこかにint型の値を記録できる領域を確保して、そのアドレスに紐付けされたaというラベルを提供するという処理に置き換えると考えることができます。そして「a=1;」は、aというラベルに紐付けされているRAM領域に1が書き込まれます。(感覚的な表現であって、アセンブリ言語レベルでは違う処理かもしれませんが。)
それに対し「int *a;」という記述は、とあるRAM領域のどこかに「int型の値を記録できる領域」のアドレスを記録できる領域を確保して、そのアドレスに紐付けされたaというラベルを提供します。そして「a=1;」は冗長に書くと「a=(int*) 1;」であって、つまりint型変数のアドレスとして「1番地」をaに紐付けされたRAM領域に書き込みます。さらに「*a=1;」は、「1番地のRAM領域」にint型の1という値を書き込みます。
WindowsやMacのようなメモリ保護前提のOSでRAMアドレスを直接指定するということはまずないと思いますが、μITRON系OSやOS無しマイコンでは絶対的なアドレスを指定して値を書き込む場面は多々あります。
で、なんで「int *a; a = 1;」がダメなのか。
「int *a;」ではaは、書き込んでも良いアドレスで初期化されないからです。「int型を記録できる領域のアドレス値」の入れ物が定義されただけで、aが持っている値は不定なわけです。それは0番地かもしれないし1番地かもしれないし、100番地かも知れないし、1000番地かもしれません。
その後の「a=1;」はどこだかわからないアドレス番地に1を書き込んでいることになります。
で、ここからが実行環境の話です。
実行環境、特にOSがある場合は、RAM領域の中でシステム専用のアドレス領域やmalloc()で取り出す元になる領域などといったように用途ごとに区画分けがされています。そして1という値は、あなたにとってはint型の1であっても、それぞれの区画では別の意味でしょう。書き込むアドレスによっては、もしかするとパソコンを爆破する命令を指す値かもしれません。
そういった不定な領域に任意の値を書き込む行為は、(あなたにその意志がなくても)実行環境にとってはテロなわけです。テロへの対抗手段として、実行環境があなたの実行したプログラムを(自衛のために)殺すのが不正終了と呼んでいる挙動、その原因である不定な領域に不正な値を書き込む行為を指すエラー名がsegmentation faultや一般保護エラーです。
ちなみに0番地に書き込むのも大多数の実行環境ではテロにあたる事が多いので、「int *a = 0;*a=1;」もsegっちゃうよ、というのが#4の指摘だと思います。(外してたら恥ずかしいな・・・)
だったらどうしたらいいのか?ですね。
int *a;
としたら、
if ((a = (int*) malloc(sizeof int)) == NULL) exit 1;
として、終了(exit)しなかったら、
*a = 1;
といった感じで、まずはaに有効なアドレスを与えてあげなければいけません。
他には、
int b;
int *a;
a = &b;
*a = 1;
でもいいし、
int c[100];
int *a;
a = c;
*a = 1;
でも(文法的には)いい。
で、ソースコードに立ち戻って、まずは「Node **p;」から「*p = root;」の間で「p=」で始まる処理を探しましょう。
もし無いなら、p(==不定なアドレス)への代入がsegの原因でしょう。
そして、じゃあどこに「p=」で始まる文を入れるべきなのかを検討しましょう。
それがいわゆるデバッグってやつです。
すべてを自分で考えてソースコードを書くならともかく、他人が別の実行環境向けに作ってテスト方法や検証結果も提供していないソースコードをせしめて使いたいなら、デバッグは必須と考えましょう。
特に今回のような、「Node **p;」のような「Node型へのポインタへのポインタ」とかいった多段の参照関係や、あるいは関数ポインタによるコールバックや抽象化は、「C言語で作ったプログラム」としてはごく普通というか、「それがしたくて未だにC言語を使っている」といった類の手法ですが、ポインタの基礎的な知識が不足している状態でそのレベルのソースコードを引っ張って来て流用するというのは、相当の覚悟と努力が必要だと思います。
まさに賞賛に値します。
この回答への補足
>warningはコンパイラからのツッコミなので文法的に是正していけばいいですが、segmentation faultや一般保護エラーは実行環境(OSとか)からのツッコミですので、実行環境であなたのプログラムが何か「悪いこと」をやらかしているわけです。
つっこんでくる相手がそもそも違っていたのですね。大変分かりやすくお教えくださりありがとうございますm(_ _)m
>WindowsやMacのようなメモリ保護前提のOSでRAMアドレスを直接指定するということはまずないと思いますが、μITRON系OSやOS無しマイコンでは絶対的なアドレスを指定して値を書き込む場面は多々あります。
>実行環境、特にOSがある場合は、RAM領域の中でシステム専用のアドレス領域やmalloc()で取り出す元になる領域などといったように用途ごとに区画分けがされています。そして1という値は、あなたにとってはint型の1であっても、それぞれの区画では別の意味でしょう。書き込むアドレスによっては、もしかするとパソコンを爆破する命令を指す値かもしれません。そういった不定な領域に任意の値を書き込む行為は、実行環境にとってはテロなわけです。テロへの対抗手段として、実行環境があなたの実行したプログラムを(自衛のために)殺すのが不正終了と呼んでいる挙動、その原因である不定な領域に不正な値を書き込む行為を指すエラー名がsegmentation faultや一般保護エラーです。
OSがsegmantationfaultを起こす背景にはこんな事情があったんですね。すごく勉強になりました。
>int *a;としたら、
int b;
int *a;
a = &b;
*a = 1;
でもいいし、
int c[100];
int *a;
a = c;
*a = 1;
でも(文法的には)いい。
「int型を記録できる領域のアドレス値の入れ物」の定義に加えて、int型の値を記録できる領域を確保して、そのアドレスに紐付けされたラベル(上記の例ですとbとcがそれにあたりますね)を代入すればいいということですね。そしてその代入にあたる行為が#4様の仰った「初期化」だというわけですね。
他の方々もご親切に詳しくお教え頂いたので全員をBAとさせて頂きたいところですが、全体的に最も丁寧に解説してくださった(C言語に関して全く無知な私に一から教えてくださった)貴殿を本質問のBAとさせて頂きます。
本当にありがとうございましたm(_ _)m
No.7
- 回答日時:
>英語力に乏しく、エラーメッセージを読み違えておりましたorz
読み間違えたというのが
>おそらく、sの型が*s=s[]なので、注意の中の「s++」の部分で誤作動を起こしている(s++を実行するにはsはint型でなければならない)と思うのですが、
のつもりなら英文全く読んでなくて見ただけでしょ(メッセージ中の単語の意味どれ一つ出てないし)。
>がダメ・・・int型なのに「*p」としてるからですか?
(以下略)
もしかしてポインタ理解できていませんか?
この回答への補足
>英文全く読んでなくて見ただけでしょ
仰る通りです。見て何となく「int型とchar*型を比較しているからおかしいんだろう」と考えただけです。
ご親切にアドバイスして頂いたのにも関わらず、嘘を言ってしまいました。申し訳ありません。
>ポインタ理解できてませんか?
お恥ずかしながら、ポインタについて理解できておりませんでしたorz
幸い、他の回答者がそれについて詳しく説明してくださり、助かりました。
No.6
- 回答日時:
int i ;
で、 int を入れるための領域が確保されます。ここでこれを 「100番地からの領域」 とします。
このとき、「100番地からの領域」に何が入っているかはわからない、というのがC言語です。
i = 1 ;
は
「100番地からの領域」に「int型の1」を格納する
という意味です。
この「100番地からの領域」というのは、ポインタそのものです。よって
*(「100番地からの領域」) = 1 ;
と同じことになります。
同様に
int *p ;
で、 int * を入れるための領域が確保されます。ここでこれを 「200番地からの領域」 とします。
このとき、「200番地からの領域」に何が入っているかはわからない、というのがC言語です。
*p = 1;
は
「pからの領域」に「int型の1」を格納する
という意味です。pは「200番地からの領域」だったので
「「200番地からの領域」からの領域」に「int型の1」を格納する
となります。
さて。「int型の1」が格納されるのは、どこでしょうか?
解決策は
(1)int *p ではなく int i を使う。
(2)int *p で、 p=「intが収めることができる領域」 と初期化して使う。
p = &i ; /* int i で宣言した変数iのアドレス */
p= malloc(sizeof(int)) ; /* intが収まる領域をmallocで確保→あとでfreeすることを忘れずに */
があります。
どちらがいいかは、プログラム次第です。
Node **p=NULL ;
では、(2)をしようとして、「使えない領域(NULL)」を設定してるのですから、動く保証はありません。
「初期化」と「NULL、0に設定する」ことは同じではありません。
今回の場合なら
Node *q ; /* Node* を収めることができる変数q を用意*/
p = &q ; /* pにqのアドレスを設定する */
か
p = malloc(sizeof(Node *)) ; /* Node*を収めることができる領域を確保 */
となります。
ですがよく見ると、そのプログラム *p の形でしか使っていません。
Node ** とする必要ってあるのでしょうか?
この回答への補足
大変分かりやすく教えてくださってありがとうございます!
>さて。「int型の1」が格納されるのは、どこでしょうか?
「「200番地からの領域」からの領域」になりますね。訳が分かりません。
これじゃ混乱してしまいますね。
>p = malloc(sizeof(Node *)) ;
これをNode **p;のすぐ下に入れたら正常に動きました!本当にありがとうございますm(_ _)m
>ですがよく見ると、そのプログラム *p の形でしか使っていません。Node ** とする必要ってあるのでしょうか?
試しに「Node *p;」としてみたら正常に動きました・・・。「*」が何故2つあったのかよくわかりませんねorz
No.5
- 回答日時:
>申し訳ありません、「char*」と「char[]」が同義だと勘違いしていました・・・。
似たように使えるというだけであって同義ではないです。
ただし関数の仮引数のchar[]のような配列表記はchar *と同義です。
void foo(char s[]); // char s[]はchar *sと同じ
void foo(char *s);
>注意の解説もしてくださり、ありがとうございます(使用コンパイラはmacの「Xcode」です)。
英文なので避けたいかもしれませんがメッセージを読めるように努力をしましょう(質問するなという意味じゃないです)。
ただし質問などされる際には、今回のようにそっくりそのまま書きましょう。
>つまり、*pに代入するrootの初期化がされていない、ということでしょうか?
いいえ。
#4でも書かれてますけど
int *p;
*p = 1;
これがダメなのと同じです。
この回答への補足
英語力に乏しく、エラーメッセージを読み違えておりましたorz
int *p;
*p=1;
がダメ・・・int型なのに「*p」としてるからですか?
int i;
i=1;
は大丈夫ですよね・・・?「int *p=1;」としなければならない、ということでしょうか。
ちなみに「void compose(FILE*fp)」のところで「Node **p=NULL,*new;」としてもsegmentation faultが発生しました・・・orz
No.4
- 回答日時:
「自動変数のスタックが強制的に初期化されるシステム」でもダメだと思うけどなぁ>#3.
例えば
int foo(void)
{
int x = 5;
int *y;
*y = x;
}
が「おかしい」ことはわかりますか? どこがどう「おかしい」か説明できますか?
No.2
- 回答日時:
>おそらく、sの型が*s=s[]なので、注意の中の「s++」の部分で誤作動を起こしている(s++を実行するにはsはint型でなければならない)と思うのですが、どうコード文を変えれば良いのかがよくわかりません。
sの型はchar *であって配列(char[])ではないです。
また
>warning
> comparison between pointer and integer ('int' and 'char *')
の警告は*s(型がcharから暗黙の型変換が行われたint)とNULL(型がchar *。使用コンパイラは何だろう・・・)を比較してることについてです。
segumentation faultを起こしてるのは
*p=root;
この回答への補足
ご回答ありがとうございますm(_ _)m
申し訳ありません、「char*」と「char[]」が同義だと勘違いしていました・・・。
注意の解説もしてくださり、ありがとうございます(使用コンパイラはmacの「Xcode」です)。
segumentation faultを起こしているのは「*p=root;」の部分で、wormhole様もそこの「初期化がされていない」とご指摘されています。
つまり、*pに代入するrootの初期化がされていない、ということでしょうか?
コードの上の方に「Node *root=NULL;」とありますがこれで初期化がされていないと・・・?
申し訳ありません、頭が悪い私にも分かりやすく説明して頂けますでしょうかorz
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
このQ&Aを見た人はこんなQ&Aも見ています
-
許せない心理テスト
私は「あなたの目の前にケーキがあります。ろうそくは何本刺さっていますか」と言われ「12本」と答えたら…
-
フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
あなたが普段思っている「これまだ誰も言ってなかったけど共感されるだろうな」というあるあるを教えてください
-
映画のエンドロール観る派?観ない派?
映画が終わった後、すぐに席を立って帰る方もちらほら見かけます。皆さんはエンドロールの最後まで観ていきますか?
-
海外旅行から帰ってきたら、まず何を食べる?
帰国して1番食べたくなるもの、食べたくなるだろうなと思うもの、皆さんはありますか?
-
天使と悪魔選手権
悪魔がこんなささやきをしていたら、天使のあなたはなんと言って止めますか?
-
C言語のポインターで詰まっている
C言語・C++・C#
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
c言語のポインタへの文字列入力...
-
newしないオブジェクトについて
-
メモリ不足になってしまう。
-
new と malloc によるメモリの...
-
MSDNがgethostbynameではなくge...
-
DLLのマルチスレッドの動作につ...
-
allocってなんですか?
-
プログラムが途中で強制終了し...
-
仮に&str[i]のアドレスを上げな...
-
LoadLibraryでAccess Violation...
-
指定したメモリアドレスの値の...
-
mallocで確保するメモリの領域...
-
x64環境で連続4GB以上のメモリ...
-
init関数の意味
-
配列を使わずに、変数名を動的...
-
VBAのプログラムで、DIAG = 1# ...
-
戻り値で構造体を返すことは可...
-
配列の要素数に変数を入れたい...
-
関数から配列を返すには?
-
VB.NETでファイル名順にファイ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
allocってなんですか?
-
newしないオブジェクトについて
-
c言語のポインタへの文字列入力...
-
ヒープメモリの解放について
-
配列の添え字の最大数とは?
-
stringの最大サイズ
-
C++で、メンバもヒープに確保さ...
-
プログラムが途中で強制終了し...
-
void*型のデータサイズ
-
malloc呼び出し時のセグメンテ...
-
スタック破壊の上手な見つけ方...
-
ビットをローテートするプログ...
-
構造体でchar name[]と*nameの...
-
mallocについて
-
GDI+におけるメモリの開放について
-
ポインタのポインタの初期化法
-
構造体を使ったファイルの読み込み
-
C言語 mallocとfreeについて
-
HEAP に関すること
-
VBからMFC-DLL呼び出し
おすすめ情報