※ ChatGPTを利用し、要約された質問です(原文:C言語に関する質問)
C言語初心者のためのファイル中の英文の単語出現頻度をカウントするコードの修正方法
このQ&Aのポイント
C言語初心者の方が「ファイル中の英文を単語に分けてその出現頻度をカウントするコードを木構造を用いて出力せよ」という課題に取り組んでいます。参考にしたコードのURLと注意事項を紹介し、警告メッセージが出た場合の修正方法を質問しています。
コードの内容は、ファイルを読み込んで単語に分割し、その出現頻度をカウントするための木構造を作成するものです。しかし、コンパイル時にポインタと整数の比較に関する警告が出ており、そのまま実行するとセグメンテーションフォルトが発生します。
質問者は、sの型が*s=s[]なので、注意の中の「s++」の部分で誤作動を起こしている(s++を実行するにはsはint型でなければならない)と考えています。しかし、正しい修正方法を知りたいと質問しています。
初めて質問させて頂きます。C言語初心者です。
実は講義で「ファイル中の英文を単語に分けてその出現頻度をカウントするコードを木構造を用いて出力せよ」という課題が出ました。
そこで、参考にするコードを検索しましたところ、以下のURLにあるベストアンサーのコードが近いと感じました。
http://okwave.jp/qa/q4155655.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
補足
>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