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);
の場合には最初の文字が空白や改行文字でも、その次に有効な文字があればそれらの改行や空白を無視するのではないかと思っています。
分かる方がいましたら回答をよろしくお願いします。
No.1ベストアンサー
- 回答日時:
> char c;
> scanf("%s", &c);
char c では1文字分の領域しか確保されていないので、
1文字以上の文字列を無理矢理格納すると
他のデータが存在しているかもしれない領域を書き換えてしまいます。(メモリ破壊)
なお、'\0'はscanfが(本来書き込んではいけない領域に対してですが)書き込んでいます。
> と書くと c には改行文字が代入されてしまいます。
最初のscanfが改行文字を読み込んでいないからです。
以下のページに書かれている内容と本質的には同じ。
http://www9.plala.or.jp/sgwr-t/c/sec05.html#s5-
> 最初の文字が空白や改行文字でも、その次に有効な文字があればそれらの改行や空白を無視するのではないかと思っています。
そう考えて問題ないと思います。
No.4
- 回答日時:
いろいろと回答が出ていますが、一応。
----------------------
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は多くの場合、バッファオーバーフローを起こすと言われますが、入力文字数の制限などは可能です。
詳細な回答ありがとうございます。
入力ストリームという概念とscanf関数の知識が足りなかったのだと分かりました。これからの学習に役立てていこうと思います。
No.3
- 回答日時:
誤----------------------------
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行入力ルーチン(たぶんあると思う)が見本になると思います。
No.2
- 回答日時:
>僕自身は 下のプログラムの printf 関数については間違った使い方なのではないかと思っています。
どちらも間違っています。
1文字入力して、Enterキーで確定したとしても、最低でもchar型の配列が必要です。charで2つは必要。
「文字列」ですから、'\0'も書き込みます。
ちなみに、このままでは何百文字だろうと入力可能です。
無論、その後の動作は保証できませんが。
> 何かの本で、未使用の領域は0である確率が高いという記述をみたことがあり、'\0'は0と同じだということなので問題なく動作する率が高いのではないかと思っています。
そんな保証はないかと思いますが…
Cの規格書読んだわけではないので断言しかねますけど。
> この辺りの処理がどのような法則で実行されているのかが分かりづらくて悩んでいます。
> おそらく、
> scanf("%s", str);
> の場合には最初の文字が空白や改行文字でも、その次に有効な文字があればそれらの改行や空白を無視するのではないかと思っています。
標準ライブラリの仕様書を確認する必要があるかと思いますが…
"%s"がホワイトスペース(半角空白、水平タブ、改行)を読み飛ばす仕様になっているからではないかと。
# scanf()使わないのでなんとも…
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# str[j++]の意味 2 2022/08/30 16:20
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# c言語 コマンドライン引数 4 2023/02/09 18:47
- C言語・C++・C# C言語 3 2022/10/04 15:07
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語、単語ごとに改行したい
-
反転した数値を表示させるやり方
-
CStringのFindで文字列検索を行...
-
sscanfとscanfの違いがよくわか...
-
strstrを利用しない文字列検索...
-
toupper関数とstrcmp関数を使っ...
-
C言語の課題です
-
ブランクのチェック
-
文字列中に含まれる文字の個数...
-
C++
-
str[j++]の意味
-
itoaわかりません
-
C言語 空白の行(改行のみ)が...
-
putsとputcharの違い?
-
エディットボックスに入力され...
-
C言語 strlen 再入力を促す
-
fgets関数を使用したときの文字...
-
16進数の文字列
-
単語検索プログラム
-
単語数のカウントについて
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
CStringのFindで文字列検索を行...
-
sscanfとscanfの違いがよくわか...
-
charと%c , %s の関係について
-
fgets関数を使用したときの文字...
-
反転した数値を表示させるやり方
-
fgetsで拾われる改行文字を削除...
-
C言語 空白の行(改行のみ)が...
-
itoaわかりません
-
Cで「大文字、小文字の判定」は...
-
文字列中に含まれる文字の個数...
-
C言語で16進数文字列から16進数...
-
小文字のみを数える方法
-
strstrを利用しない文字列検索...
-
C言語のステップ数をカウントす...
-
C言語でパスワード作成ツール
-
単語数のカウントについて
-
str[j++]の意味
-
fgetsでバッファ残留文字列を無...
-
教えていただけませんか?C言語...
-
エディットボックスに入力され...
おすすめ情報