アプリ版:「スタンプのみでお礼する」機能のリリースについて

【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型で宣言すればいいだけなのですが、なぜこんな挙動になるのか知りたいです。
よろしくお願いします。

A 回答 (4件)

scanfのマニュアルをよくよんでください。


フォーマット%dが要求するのはint型へのポインタです。

例え、実際にはchar型へのポインタであったとしても、きっちりint型の分の領域にデータが書き込まれます。その場合、char型より大きい分は変数の領域の外へ書き込まれることになります。

今回は、そのあふれた先が変数iの領域だったので、iが異常な値になったのでしょう。

この回答への補足

なるほど、そういう訳でしたか。
同じように考えてみると、long型に入力する場合に %ld とするのは、long型の分の領域に書き込むためなのですかね。

補足日時:2010/06/25 11:00
    • good
    • 0
この回答へのお礼

>char型より大きい分は変数の領域の外へ書き込まれることになります。
>今回は、そのあふれた先が変数iの領域だったので、iが異常な値になったのでしょう。
の説明で、 i がおかしな値になっていることに合点がいきました。
ありがとうございました。

お礼日時:2010/07/18 18:16

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型に制限するために、その為の処理コードが追加される可能性もあります。」
の意味が分かりました。どうやら素人がメモリの節約に気を使うのはやめた方が良さそうです。

本題以外の質問にまでご丁寧にお答え下さってありがとうございました。

補足日時:2010/07/18 18:10
    • good
    • 0

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 …

補足日時:2010/06/25 11:53
    • good
    • 0
この回答へのお礼

>short input;
>scanf("%hd", &input);
のやり方を教えて下さりありがとうございました。

お礼日時:2010/07/18 18:18

>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型にキャストする場合がそれに当たるのでしょうか。

補足日時:2010/06/25 11:38
    • good
    • 0
この回答へのお礼

>1リットルの入るコップに4リットルのペットボトルから飲み物入れたら溢れますよね?
の例えがとても分かり易かったです。ありがとうございました。

お礼日時:2010/07/18 18:19

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