好きなおでんの具材ドラフト会議しましょう

こんにちは、独学でC言語を勉強しているゆみころです。
とある例文(This is a pen. )などを入力し(制限:256字まで Ctl+Zで入力終了)、そのアルファベットの出現頻度求めるプログラムを作っています。
(アウトプットとして A=0回、B=2回、C=1回などでてくるようです)

またもや2日考えましたが、解決が見つからず困っております。
すみませんがどなたかお助け下さいませんか?
よろしくお願いします。

質問(1)
コメントで/*このソースの意味がわからない*/ といる行の説明をお願いできますでしょうか?
質問(2)
エラーで沢山アポストロディーがIllegalだとでてきました。私の使用しているアポストロフィーは使えないのでしょうか?
ノートパッドで作成し、その後Borlandでコンパイルしています。

お礼コメントは絶対します!しかし、On timeにできない場合があります、すみませんがよろしくお願いします。

★ゆみころ★

*********勉強している本からの例をそのままコンパイルしようとしました**************

#include<stdio.h>
#include<ctype.h>
#include<string.h>

#define MAX 256 /*Max input data*/
#define ALPHA 26/*Alpha nubmer a - z*/

void main(void)
{
char str[MAX];
int alpha[ALPHA];
int i;

memset(&alpha[0],'\0',sizeof alpha);/*アルファの0配列からalphaの配列分だけ0で埋めてくれる->0で初期化*/
for(;scanf("%s",&str[0]!=EOF;) /*continue till EOF(Ctl+z) */
{
for(i=0;str[i]!='0';i++)
{
if(isalpha(str[i]))/*アルファベットか?*/
{
str[i]=(char)toupper(str[i]);/*このソースの意味がわからない。Capitalへ conversionできるのは理解できましたが、なぜ(char)がいるのですか?*/
(alpha[str[i]-'A'])++;/*このソースの意味がわからない 該当文字出現回数を更新しているそうです*/
}
}
}

for(i=0;i<ALPHA;i++)/*alphabet 26回分繰り返す*/
{
if((i%5)==0)/*横に5こづずならべる*/
{
putchar('\n');
}
printf(" %c=%5d",i+'A',alpha[i]);
}
putchar('/n');
}


*********エラーメッセージ*************************
C:\Practice>bcc32 alph2.cpp
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
alph2.cpp:
Error E2206 alph2.cpp 10: Illegal character ' ' (0x8140) in function main()
Error E2206 alph2.cpp 14: Illegal character '[' (0x816d) in function main()
Error E2121 alph2.cpp 14: Function call missing ) in function main()
Error E2206 alph2.cpp 14: Illegal character ']' (0x816e) in function main()
Error E2034 alph2.cpp 15: Cannot convert 'int' to 'char *' in function main()
Error E2121 alph2.cpp 15: Function call missing ) in function main()
*** 6 errors in Compile ***

A 回答 (3件)

なんか質問者が理解できてないのかTacosanさんの回答を右から左に流しているので,


実際のコードで見せてみます。

// memset も微妙だけど (初期化すればいいのに)
// -> = {0}で宣言時に初期化。
int alpha[ALPHA] = {0};
int c;
// ・なんで scanf を使ってるんだろう. 1文字ずつ読み込むようにすればバッファオーバーランなど起こりようもないのに.
// -> getcharを使ったプログラムに変更。1文字ずつ処理してバッファーに入れるという動作をしないので、バッファオーバーランは起きない。strの代わりにcというint型の変数を使うことにする。
// ・条件式しかない for って, あんまり普通じゃないと思う. while はお嫌い?
// -> 条件だけ取り出して、while文に書き換え
while ((c = getchar()) != EOF) {
if(isalpha(c)) {
c = toupper(c);
alpha[c - 'A']++;
}
}

以下、落穂拾い的に残りのコメントについて解説します。
> #1 への指摘を先にすると, 15行目は「Cannot convert 'int' to 'char *'」だから「EOF が変換できていない」んだと思います. また, 「for()の閉じ括弧がありません」じゃなくて「scanf の呼び出しの閉じ括弧がない」です (おそらくセミコロンのところでエラーになってる).

質問者もこれには気づいているようなので特にコメントをすることはありません。

ただ、getcharに変更して、scanfにはご退場いただいたのでこのエラーはなくなりました。
ちなみに、scanfは初心者向けの本に必ず書いてあるものの、使える場面が限られている上に、扱いが非常に難しいので自分は殆ど使わないです。代わりにfgets、fgetc、freadを使うことが多いです。

> ・そもそもアルファベットがきちんと並んでいるという保証はどこにもない. Windows なら ASCII がベースなので問題ないけど, 一般論としてはアウト.

文字をコンピュータ上でどう表すかというのを文字コードといいますが、文字コードには様々な種類があります。例えば、ASCIIだとAからZまで文字コードが順に並んでいるものの、EBCDICなどそれが保証されていないこともあって、C言語ではどの文字コードを想定するか決まりがありません。

例えば、ASCIIだと'J' - 'I' は1ですが、EBCDICだと、'J' - 'I'は1ではありません。
だから、大文字 - 'A'が26までの範囲に収まることは必ずしも保証されていません。
ただ、Windowsなど一般に普及している環境ではASCIIを採用していることがほとんどなので、大文字 - 'A'が26未満と想定しても問題になることはほぼないでしょう。

あえてやるならこうやって求めるとかでしょうか。
const char alphabet_sample[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int get_index(int c) {
char* p = index(alphabet_sample, c);
if (p != NULL)
return p - alphabet_sample;
else
return -1;
}
もっとマシな書き方はありそうですが。

> ・str[i]!='0'
これは、誤植したんでしょうね。str[i] != '\0'とすべき。
このコードもscanfからgetcharに変更したことでなくなりました。

文字と数値では意味が全然違います。
str[i] != 0だと0という数値との比較ですが、str[i] != '0'だと0という文字との比較になります。
'0'の文字コードはASCIIだと48なので、str[i] != '0'はstr[i] != 48と書いてるのと同じ動作をします。
一方str[i] != '\0'だと'\0'はNULL文字で、数値の0に中身はなっているはずなので、str[i] != 0と同じ動作をします。
ただ、文字として扱っているということを意識すると、str[i] != '\0'が正しいです。

> ・putchar('/n');
これも誤植ですね。
正しくはputchar('\n')でしょう。

以上の修正をして、FreeBSD上でコンパイルして動かしてみるとこんなかんじです。
>./a.out
Hello World!
^D
A= 0 B= 0 C= 0 D= 1 E= 1
F= 0 G= 0 H= 1 I= 0 J= 0
K= 0 L= 3 M= 0 N= 0 O= 2
P= 0 Q= 0 R= 1 S= 0 T= 0
U= 0 V= 0 W= 1 X= 0 Y= 0
Z= 0

>cat /COPYRIGHT | ./a.out

A= 284 B= 93 C= 180 D= 220 E= 556
F= 140 G= 84 H= 157 I= 473 J= 3
K= 18 L= 177 M= 110 N= 351 O= 396
P= 102 Q= 5 R= 359 S= 337 T= 471
U= 142 V= 48 W= 68 X= 13 Y= 82
Z= 1

というわけで、頑張って。
    • good
    • 0
この回答へのお礼

hanabutako先生、

ご親切なご回答をどうもありがとうございました。

実際のコードありがとうございました。
Tacosan先生の言っている意味がわかりました。

たぶん本では memsetという変数を紹介したかったんだと思います。
今までのPageではなく初登場でしたのであえて紹介したんだと思います。
たしかに使い勝手が悪いので 普通の初期化の方がいいと思いました。

この本は全てscanfを使ってます、基礎編だからでしょうか?
getcharはたまにしか出てきません、きっと応用編にいったらgetcharを使うように書いてあるのかもしれません。
whileでも書けるんですね!
見やすいしわかりやすいです!
ありがとうございました。

>>str[i] != '\0'とすべき。
>>正しくはputchar('\n')でしょう。
ご指摘ありがとうございました。
間違っていました。ほんとうに私はよく間違えて気付かないので情けないです。
(何回も見直し考えましたが 気づきませんでした涙)


すべてを直したら無事にプログラムは動き、結果も正しく得られました。
詳しい解説のおかげで 私が理解できなかった1行も理解することができました。
長々とお付き合い頂きhanabutako先生はじめ
皆さま、どうもありがとうございました。

私が理解できていないのは、頭が悪いのと、C言語を学校へ行かず一人で勉強しようと無茶をしていることと
日本語が得意ではないからです。
何度もこちらで親切で物知りな先生たちに助けてもらいただただ感謝です。
すみませんが、今後ともよろしくお願いします。


★ゆみころ★

お礼日時:2012/10/11 16:54

#1 への指摘を先にすると, 15行目は「Cannot convert 'int' to 'char *'」だから「EOF が変換できていない」んだと思います. また, 「for()の閉じ括弧がありません」じゃなくて「scanf の呼び出しの閉じ括弧がない」です (おそらくセミコロンのところでエラーになってる).



ただ, このプログラムはいろいろと突っ込みどころが....
・なんで scanf を使ってるんだろう. 1文字ずつ読み込むようにすればバッファオーバーランなど起こりようもないのに.
・条件式しかない for って, あんまり普通じゃないと思う. while はお嫌い?
・そもそもアルファベットがきちんと並んでいるという保証はどこにもない. Windows なら ASCII がベースなので問題ないけど, 一般論としてはアウト.
・str[i]!='0'
・putchar('/n');

memset も微妙だけど (初期化すればいいのに)
    • good
    • 0
この回答へのお礼

Tacosan先生、
ご指摘どうもありがとうございました。

scanfのカッコはお陰さまで修正済みです、ありがとうございました。

いろいろ突っ込むところはあると思いますが
私はまだ自分で作れるレベルまで達しておらず、本に書いてあるものをそのまま真似して作って
本当に動くかどうかとなぜそのソースで結果が得られるのかを理解するのが精一杯です。
もっと便利な変数があったり、きれいな書き方もあると思いますがどのように修正していくかも今はわかりません。(今回指摘頂いたこともいずれわかるようになりたいです)

分かる人から見たら 気になって仕方ないと思いますが
どうぞご了承くださいませ。
今後ともご指導をよろしくお願いします。

★ゆみころ★

お礼日時:2012/10/10 16:24

>str[i]=(char)toupper(str[i]);/*このソースの意味がわからない。

Capitalへ conversionできるのは理解できましたが、なぜ(char)がいるのですか?*/

toupper()のマニュアルを見ましょう。
戻り値の型はint型になっているかと。

小さい型への代入になりますので、明示的にキャストしているのでしょう。

>(alpha[str[i]-'A'])++;/*このソースの意味がわからない 該当文字出現回数を更新しているそうです*/

この中のどの部分が不明…なんでしょうか?

str[i]の値は直前の判定で半角アルファベットの大文字であることが確定しています。
つまり'A'から'Z'です。
そこから'A'の文字コードを引いた値はいくつになると思いますか?

>for(;scanf("%s",&str[0]!=EOF;) /*continue till EOF(Ctl+z) */

閉じ括弧が1つ足りないようですが?

また、この方法でも256字以上入力するとバッファオーバーランになります。
http://www.kijineko.co.jp/tech/superstitions/buf …
scanf("%255s",&str[0])
または
scanf("%255s",str)
でしょうかね。

>Error E2206 alph2.cpp 10: Illegal character ' ' (0x8140) in function main()
ソースコードの10行目に全角空白が紛れ込んでいるようです。
「char str[MAX];」の間違いではありませんか?
# charとstr[MAX];の間の空白が全角空白ではありませんか? 多バイト文字は変数名などには使用できないでしょう。

>Error E2206 alph2.cpp 14: Illegal character '[' (0x816d) in function main()
>Error E2121 alph2.cpp 14: Function call missing ) in function main()
>Error E2206 alph2.cpp 14: Illegal character ']' (0x816e) in function main()

&alpha[0]の括弧が全角になっていませんか?

>Error E2034 alph2.cpp 15: Cannot convert 'int' to 'char *' in function main()
>Error E2121 alph2.cpp 15: Function call missing ) in function main()

scanf()の閉じ括弧が無い為、第2引数が「&str[0]!=EOF」になっています。
&str[0]がchar *で、EOFとの比較の為にint型に変換できていません。(「Cannot convert~」のエラー)
で、for()の閉じ括弧がありません。(「Function call missing )~」のエラー)
    • good
    • 0
この回答へのお礼

Wr5先生、

今回もご丁寧なご回答どうもありがとうございました。

(char)toupperについてキャストの概念を忘れていました。
理解できました、ありがとうございます。

>(alpha[str[i]-'A'])++;/*このソースの意味がわからない 該当文字出現回数を更新しているそうです*/

この中のどの部分が不明…なんでしょうか?

str[i]の値は直前の判定で半角アルファベットの大文字であることが確定しています。
つまり'A'から'Z'です。
そこから'A'の文字コードを引いた値はいくつになると思いますか?

→同じ解説が本にも書いてありましたがまだ私には理解ができません。
前ラインで入力した文字を全て大文字変換しそれをstr[i]へ格収したのは理解できましたがなぜこの一行で出現回数を更新できるのかが謎です。
馬鹿ですみません。


>for(;scanf("%s",&str[0]!=EOF;) /*continue till EOF(Ctl+z) */

→カッコが足りない件のご指摘どうもありがとうございました。
エラーが消えました。
バッファオーバーランのAdditional Inforありがとうございました。
私にはまだ難しくて理解できませんが、わかるようになったら参考にさせて頂きます。

空白や全角ミスのご指摘ありがとうございました。
全てなおし、無事コンパイルはできました。

しかし、実行しても結果がでてきません。涙
まだ何か間違っているのでしょうか?

よろしくお願いします。

★ゆみころ★


*******修正後*********
#include<stdio.h>
#include<ctype.h>
#include<string.h>

#define MAX 256 /*Max input data*/
#define ALPHA 26/*Alpha nubmer a - z*/

void main(void)
{
char str[MAX];
int alpha[ALPHA];
int i;

memset(&alpha[0],'\0',sizeof alpha);/*アルファの0配列からalphaの配列分だけ0で埋めてくれる->0で初期化*/
for(;scanf("%s",&str[0])!=EOF;) /*continue till EOF(Ctl+z) */
{
for(i=0;str[i]!='0';i++)
{
if(isalpha(str[i]))/*アルファベットか?*/
{
str[i]=(char)toupper(str[i]);/*Capital conversion*/
(alpha[str[i]-'A'])++;/*該当文字出現回数を更新*/
}
}
}

for(i=0;i<ALPHA;i++)/*alphabet 26回分繰り返す*/
{
if((i%5)==0)/*横に5こづずならべる*/
{
putchar('\n');
}
printf(" %c=%5d",i+'A',alpha[i]);
}
putchar('/n');
}

*********実行結果***************
C:\Practice>alph2
this is a pen. ^Z

C:\Practice>


何もでてきませんでした涙

お礼日時:2012/10/10 16:16

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報