【C言語】二重forループ内でscanfを使ってchar型変数に数値(%d)を入力すると、きちんとループ処理されないのはなぜ?
プログラムを下に用意しましたのでご覧下さい。
二重forループ内で入力を繰り返すプログラムです。
ついでに i j の値を出力するようにしました。
-----------------プログラム----------------
int main (void){
char input = 0; // 入力値 char型にするとforループでインクリメントエラー(int型にすると問題ない)
int i = 0, j = 0;
, printf("数値を入力して下さい。('-1' で入力終了)\n");
for( i=0 ; i<3 ; i++ ){ // i がちゃんとインクリメントされない
for( j=0 ; j<3 ; j++ ){
scanf("%d", &input); // char 型変数に %d で入力すると、i がきちんとインクリメントされない
printf("[i][j] = [%d][%d]\n", i, j);
if( input == -1 ){
printf("入力を終了します。\n");
break;
}
}
if( input == -1 )
break;
}
return 0;
}
----------------------------------------
---------実行結果(入力値はchar型)---------
数値を入力して下さい。('-1' で入力終了)
1 2 3 4 5 6 7 8 9
[i][j] = [0][0]
[i][j] = [0][1]
[i][j] = [0][2]
[i][j] = [0][0]
[i][j] = [0][1]
[i][j] = [0][2]
[i][j] = [0][0]
[i][j] = [0][1]
[i][j] = [0][2]
-1
[i][j] = [16777215][0]
入力を終了します。
----------------------------------------
----------実行結果(入力値はint型)----------
数値を入力して下さい。('-1' で入力終了)
1 2 3 4 5 6 7 8 9
[i][j] = [0][0]
[i][j] = [0][1]
[i][j] = [0][2]
[i][j] = [1][0]
[i][j] = [1][1]
[i][j] = [1][2]
[i][j] = [2][0]
[i][j] = [2][1]
[i][j] = [2][2]
----------------------------------------
ご覧の通り、char型変数に値を入力しているために、forループで i がきちんとインクリメントされません。
入力値は -128~127 の値しか想定していないので、メモリの消費を少しでも抑えようと思いchar型で宣言したのですが、思わぬ所でおかしな挙動が起こってしまいました。
int型で宣言すればいいだけなのですが、なぜこんな挙動になるのか知りたいです。
よろしくお願いします。
No.1ベストアンサー
- 回答日時:
scanfのマニュアルをよくよんでください。
フォーマット%dが要求するのはint型へのポインタです。
例え、実際にはchar型へのポインタであったとしても、きっちりint型の分の領域にデータが書き込まれます。その場合、char型より大きい分は変数の領域の外へ書き込まれることになります。
今回は、そのあふれた先が変数iの領域だったので、iが異常な値になったのでしょう。
この回答への補足
なるほど、そういう訳でしたか。
同じように考えてみると、long型に入力する場合に %ld とするのは、long型の分の領域に書き込むためなのですかね。
>char型より大きい分は変数の領域の外へ書き込まれることになります。
>今回は、そのあふれた先が変数iの領域だったので、iが異常な値になったのでしょう。
の説明で、 i がおかしな値になっていることに合点がいきました。
ありがとうございました。
No.4
- 回答日時:
4バイト単位のメモリアクセスをする32bitCPUを考えます。
1バイト単位では読み込みはできないので、C言語風に書くと
char c = MEMORY[1] ; /* 1バイト目の1バイト分のデータを取り出す */
としたい場合には
int a=MEMORY[0] ; /* 0~3バイト目を取り出す */
a >>= 8 ; /* 1バイト目にあたる部分を最下位へシフトする(リトルエンディアンの場合) */
char c = a & 0xff ; /* 余分なビットを0に消去する */
と、intなら1命令で済むところを3命令必要です。
CPUによっては、1命令で実行できる場合もあるし、実際に上のような3命令必要なものもあります。
また、1命令でできるの場合でも、intと同じ時間で実行できるものもあれば、内部で上のような処理をしていてintより長い時間がかかるCPUもあります。
また、コンパイラの種類やオプションによって、積極的に1命令のを使ったり、逆に(互換性等を考えて)1命令のものは使用しなかったり、します。
使用メモリ量は、たしかに考えなければなりません(特に、制限の多い組込みの分野では)
しかし、ターゲットCPUやコンパイラの特性、ソフトのパフォーマンス等を十分に知った上で行わないと、節約した積もりが、かえって消費していた、ということになります。
この回答への補足
なるほど。
「それをわざわざchar型に制限するために、その為の処理コードが追加される可能性もあります。」
の意味が分かりました。どうやら素人がメモリの節約に気を使うのはやめた方が良さそうです。
本題以外の質問にまでご丁寧にお答え下さってありがとうございました。
No.3
- 回答日時:
scanfで%hhdが使えるコンパイラでしたらchar型にも代入できます
でも使えないコンパイラのほうが多いですからscanfで直接char型へは代入できないと思っていたほうがいいでしょう
scanfでは変換指定子と変数の型はプログラマの責任できちんとあわせましょう
%dは整数型なら何でもいいと思っている人はよく見ますね
short input;
scanf("%hd", &input);
なら使えると思います
この回答への補足
scanf("%d", &input);
を
scanf("%hhd", &input);
に試しに変更してみたところ、動作に変化はありませんでした。
short input = 0;
scanf("%hd", &input);
としたところ、正常に動作しました。
>%dは整数型なら何でもいいと思っている人はよく見ますね
まさに私のことですね。
long型は %ld
double型は %lf
は知っていたのですが、char型の整数に関しては何も説明が無かったので、%d でいいのかと勝手に解釈していました。
でも、printf の場合はchar型を %d で出力したらint型と同じように整数が出力されるのはなぜなんでしょうね。
本質問と似た問題に直面したWebページを見つけたので、書いておきます。
http://www.play21.jp/board/formz.cgi?action=quot …
No.2
- 回答日時:
>scanf("%d", &input);
で、"%d"によりint型分の領域に書き込もうとするから…です。
1リットルの入るコップに4リットルのペットボトルから飲み物入れたら溢れますよね?
使うのは1リットル分でも、そんなのお構いなしです。
たかだか変数1つをint型からchar型に変えたところで、スタックの使用量がほんの数バイト節約できるだけです。
# 処理系によっては次に配置するint型のアクセス効率のために余分にメモリをとっているかも知れません。
数値はchar型よりint型で扱う方がCPUにとって自然です。
それをわざわざchar型に制限するために、その為の処理コードが追加される可能性もあります。
int型が4バイトだとして…3バイト節約するために3バイト以上のコードが増えることは望ましい結果でしょうか?
この回答への補足
char型分の領域から溢れた分がint型分の領域に入って悪さをしていのですね。
>たかだか変数1つをint型からchar型に変えたところで、スタックの使用量がほんの数バイト節約できるだけです。
質問で書いた小さなプログラムではメモリの節約は無意味と言ってもいいでしょうが、非常に大きなプログラムの場合だとメモリは節約するに越したことはないと思うのですが、違うのでしょうか。「処理系によっては~~余分にメモリをとっているかも知れません。」と仰るように、処理系によるのですかね。
>それをわざわざchar型に制限するために、その為の処理コードが追加される可能性もあります。
質問の本題とは反れてしまいますが、プログラミング歴が浅いのでどのような場合がそれに当たるのか分かりませんので、簡単に教えてもらえないでしょうか。char型をint型にキャストする場合がそれに当たるのでしょうか。
>1リットルの入るコップに4リットルのペットボトルから飲み物入れたら溢れますよね?
の例えがとても分かり易かったです。ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- C言語・C++・C# プログラミング c言語 4 2023/03/07 01:05
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
- Java javaでのプログラム(配列)について質問です. 2 2022/10/14 22:27
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
*をユーザーが入力した数字の数...
-
数字以外が入力されたらエラー...
-
正負を反転させて出力するプロ...
-
プログラミング初心者です。 Py...
-
scanfが2回使えない・・・?;
-
Linuxで入力待ちなしkeyread関...
-
if文の条件にscanf関数を使うと…?
-
scanf が無視されます
-
scanf関数 バッファに残ったエ...
-
java初心者です。入力されたの...
-
scanf("%s", buf);でスペースを...
-
Eclipseコンソール表示を、リセ...
-
double型が正常に認識されてい...
-
入力エラーの処理について。
-
【C言語】入力された文字種別ご...
-
少数部の判定
-
fgetsとループ処理
-
C言語scanf_sで何故か2回入力に...
-
Userformの入力順序をタブオー...
-
enterでループ終了
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
double型が正常に認識されてい...
-
プログラミング初心者です。 Py...
-
正負を反転させて出力するプロ...
-
Excel VBAで、Application.Inpu...
-
C言語について。
-
batプログラム上で文字列を入力...
-
*をユーザーが入力した数字の数...
-
cout関数を使っているのですが...
-
漢字のソートについて
-
数字以外が入力されたらエラー...
-
Userformの入力順序をタブオー...
-
ワードで文字を入力する時の変...
-
Linuxで入力待ちなしkeyread関...
-
java初心者です。入力されたの...
-
EDITコントロールで入力できる...
-
Eclipseコンソール表示を、リセ...
-
小数か整数かを判定する方法
-
C言語scanf_sで何故か2回入力に...
-
VB.NETで16進数+16進数や16進...
-
Linuxプログラミングで、キーボ...
おすすめ情報