プロが教える店舗&オフィスのセキュリティ対策術

char型の変数の扱いで悩んでいます。
具体的には以下の二つのプログラムの差異についてです。

----------------------
char c;

scanf("%c", &c);
printf("%c\n", c);

-----------------------
char c;

scanf("%s", &c);
printf("%s\n", &c);

-----------------------


上のプログラムは正しいと思うのですが、下のプログラムが正しいのかどうか、わかる方に教えていただきたいと思い質問させていただきました。

どちらのプログラムも問題なく動作します。
僕自身は 下のプログラムの printf 関数については間違った使い方なのではないかと思っています。

scanf("%s", &c) は入力された文字のうち、終端文字の手前までの文字を引数のポインタが示すオブジェクトへ順に格納していく関数だと理解しているので、入力された文字が一文字だった場合、&cの示すオブジェクトに文字が代入されると考えたからです。

逆に printf("%s", &c) は、&cの示すオブジェクトから”ヌル文字”の手前までの文字列を順に表示する関数だと理解しているので、問題なく動作しているのは&cで示されるオブジェクトの後ろの領域が偶然'\0'だったからではないかと考えたからです。

何かの本で、未使用の領域は0である確率が高いという記述をみたことがあり、'\0'は0と同じだということなので問題なく動作する率が高いのではないかと思っています。


僕の考え方がどの程度正しくて、正確にはどうなのかを教えて欲しいです。


ちなみに、

-----------------------
char c;
char str[100];

scanf("%s", str);
scanf("%c", &c);
------------------------

と書くと c には改行文字が代入されてしまいます。
scanf("%s", str);
において"aasssdd "と最後に空白を入れると
c には空白文字が代入されます。

しかし、
--------------------------
char str1[100];
char str2[100];

scanf("%s", str1);
scanf("%s", str2);
--------------------------
においては、
scanf("%s", str1);
で "asdfg "と最後に空白を入れても次のstr2が空白で始まることはありません。


この辺りの処理がどのような法則で実行されているのかが分かりづらくて悩んでいます。
おそらく、
scanf("%s", str);
の場合には最初の文字が空白や改行文字でも、その次に有効な文字があればそれらの改行や空白を無視するのではないかと思っています。


分かる方がいましたら回答をよろしくお願いします。

A 回答 (5件)

> char c;


> scanf("%s", &c);
char c では1文字分の領域しか確保されていないので、
1文字以上の文字列を無理矢理格納すると
他のデータが存在しているかもしれない領域を書き換えてしまいます。(メモリ破壊)

なお、'\0'はscanfが(本来書き込んではいけない領域に対してですが)書き込んでいます。


> と書くと c には改行文字が代入されてしまいます。
最初のscanfが改行文字を読み込んでいないからです。
以下のページに書かれている内容と本質的には同じ。
http://www9.plala.or.jp/sgwr-t/c/sec05.html#s5-

> 最初の文字が空白や改行文字でも、その次に有効な文字があればそれらの改行や空白を無視するのではないかと思っています。
そう考えて問題ないと思います。
    • good
    • 3
この回答へのお礼

回答ありがとうございます。
記載していただいたURLも非常に参考になりました。

お礼日時:2007/05/26 09:55

最初の方のコードは、scanf()使用の是非はともかくとして、


間違っているわけではありません。

char型は「int型やlong型よりも狭い範囲の整数」を扱う型で、
scanf()で標準入力から受け取った値を"%c"という書式文字列で受け取り、
-128~+127または0~255の範囲のいずれかの値をとる、
という状況はあっておかしくないです。
    • good
    • 3
この回答へのお礼

回答ありがとうございます。
正しいコードについて解説していただけて、参考になりました。

お礼日時:2007/05/26 10:18

いろいろと回答が出ていますが、一応。


----------------------
char c;
scanf("%c", &c);
printf("%c\n", c);
-----------------------
これはscanfの「指定した文字だけを読み込む」という
仕様から正しいプログラムです。
文字は文字列でないため、'\0'はありません。
但し、Enterなどで入力された改行文字が入力ストリームに残ります。

-----------------------
char c;
scanf("%s", &c);
printf("%s\n", &c);
-----------------------
これは文字列を読み込む時、領域の無い部分へ
書き込みを行うため誤ったプログラムです。
やってはいけません。

>何かの本で、未使用の領域は0である確率が高いという記述を
>みたことがあり、'\0'は0と同じだということなので
>問題なく動作する率が高いのではないかと思っています。
そもそも、確率なんていってる時点でダメダメですね。
スタティック変数などは定義された時初期化が保障されていますが、
ローカル変数などは初期化保障されていません。

-----------------------
char c;
char str[100];
scanf("%s", str);
scanf("%c", &c);
------------------------
>と書くと c には改行文字が代入されてしまいます。
上で書いたように、指定されず読み込まれなかったものは、
Enterなどで入力された改行文字が標準入力に残ります。

>"aasssdd "と最後に空白を入れると
>c には空白文字が代入されます。
>"asdfg "と最後に空白を入れても
>次のstr2が空白で始まることはありません。
これは上で少し述べましたが、矛盾した入力文字は読まれないまま
入力ストリーム上に残り入力中で後続し(改行文字を含む)
空白類は、指令に照合しない限り読み込まれないまま残る。
というscanfの指定子集合の仕様のためです。

scanfは多くの場合、バッファオーバーフローを起こすと言われますが、入力文字数の制限などは可能です。
    • good
    • 1
この回答へのお礼

詳細な回答ありがとうございます。
入力ストリームという概念とscanf関数の知識が足りなかったのだと分かりました。これからの学習に役立てていこうと思います。

お礼日時:2007/05/26 10:15

誤----------------------------


char c;

scanf("%s", &c);
printf("%s\n", &c);
誤----------------------------

例に上げられている後のプログラムは間違いです。たまたま正しく動いているように見えるだけです。
%sはNull終了の文字列を扱うので、通常は以下のように書きます。

正----------------------------
char str[100];

scanf("%s", str); // strは配列の先頭を示すアドレス
printf("%s\n", str);
正----------------------------

なお、scanfは、本当に「ちょっとお試しで入力」という「お勉強用」関数と考えたほうがいいですね。バッファオーバーフロー攻撃ってのもありますし。

質問者さんは、不可解な動作で悩まれていますが、その問題の原因が判ったとしても、使いにくいのは変わらないと思います。私は深く追求していません。scanf関数は通常は使いませんので。

その手の入力が必要な場合は、fgetsなどの1行単位で文字列入力を受け取ったあとに、sscanfで入力された文字列から、個々のデータを切り出すやり方を使っています。このほうがエラー検出も容易だし、使いやすいからです。

それから、蛇足ですが・・・.

C言語で、それなりに、まともに使える入力ルーチンを作ると、カーソル処理とか漢字対応や編集処理なども含めて、いろんな知識が必要で、コードも500行以上になる可能性があります。
最近はフォーム画面を使うので、画面処理もある意味で楽なんですが、エスケープコードを使ってCUI画面で入力処理するプログラムを作ると、結構勉強になります。ただし、あまり参考になる書籍が無い気がします。linuxのGNUライブラリにある1行入力ルーチン(たぶんあると思う)が見本になると思います。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
入出力についてはもっと勉強したいので、GNUライブラリも当たってみたいと思います。

お礼日時:2007/05/26 10:12

>僕自身は 下のプログラムの printf 関数については間違った使い方なのではないかと思っています。



どちらも間違っています。
1文字入力して、Enterキーで確定したとしても、最低でもchar型の配列が必要です。charで2つは必要。
「文字列」ですから、'\0'も書き込みます。
ちなみに、このままでは何百文字だろうと入力可能です。
無論、その後の動作は保証できませんが。

> 何かの本で、未使用の領域は0である確率が高いという記述をみたことがあり、'\0'は0と同じだということなので問題なく動作する率が高いのではないかと思っています。

そんな保証はないかと思いますが…
Cの規格書読んだわけではないので断言しかねますけど。

> この辺りの処理がどのような法則で実行されているのかが分かりづらくて悩んでいます。
> おそらく、
> scanf("%s", str);
> の場合には最初の文字が空白や改行文字でも、その次に有効な文字があればそれらの改行や空白を無視するのではないかと思っています。

標準ライブラリの仕様書を確認する必要があるかと思いますが…
"%s"がホワイトスペース(半角空白、水平タブ、改行)を読み飛ばす仕様になっているからではないかと。

# scanf()使わないのでなんとも…
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
暇を見つけて自分の使う関数の仕様は確認してみようと思います。

お礼日時:2007/05/26 10:07

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