痔になりやすい生活習慣とは?

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

このQ&Aに関連する最新のQ&A

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に関連する人気のQ&A

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

このQ&Aと関連する良く見られている質問

QC言語の2次元配列 容量が大きすぎる場合の対処方法

私はC言語をもちいて2次元配列を作ろうとしています。

しかし、配列数が double c[10000][10000];
と大きいものにすると、エラーになってしまいます。

もちろん小さい double c[10][10];
のような配列では問題ありません。

malloc関数とかも調べたのですがなかなかいい文献が見つからずに
困っています。
どうかいいご意見があればよろしくお願いします。

Aベストアンサー

No.5です。
>今はa[],b[]に10000個の配列があります。これをc[a][b]に格納するためにどうするか、例文を書いていただいてもよろしいでしょうか?

例文ではありませんが、感じだけ書きましたので参考にしてください。
パラメタの順序や型は正しくないと思いますので、各関数はよく調べて使ってください。あくまで、こんな感じ、ということです。
-------------------
#include <stdio.h>
#include <io.h>

double read_c(FILE *fp, int x, int y) {
 double c;
 fseek(fp,(x*10000+y)*8L, SEEK_SET);
 fread(&c, 1,8, fp);
 return c;
}

void write_c(FILE *fp, double *c, int x, int y) {
 fseek(fp,(x*10000+y)*8L, SEEK_SET);
 fwrite(c, 1,8, fp);
}

int main(void)
{
 FILE *fp;
 double c,s;
 int x,y;
 int a[10000],b[10000];
 
 fp = fopen("c.dat","w+b");// double c[10000][10000]; の意味
 
 for(x=0; x<10000; x++) {
  for(y=0; y<10000; y++) {
   c=a[x]*b[y];
   write_c(fp, &c, x,y);// c[x][y]=a[x]*b[y]; の意味
  }
 }
 
 for(x=0; x<10000; x++) {
  s=0;
  for(y=0; y<10000; y++) {
   s += read_c(fp, x,y);// s += c[x][y]; の意味
  }
  b[x] = s / 10000;
 }
 
 fclose(fp);
 return 0;
}

No.5です。
>今はa[],b[]に10000個の配列があります。これをc[a][b]に格納するためにどうするか、例文を書いていただいてもよろしいでしょうか?

例文ではありませんが、感じだけ書きましたので参考にしてください。
パラメタの順序や型は正しくないと思いますので、各関数はよく調べて使ってください。あくまで、こんな感じ、ということです。
-------------------
#include <stdio.h>
#include <io.h>

double read_c(FILE *fp, int x, int y) {
 double c;
 fseek(fp,(x*10000+y)*8L, SEEK_SET);...続きを読む

Q配列の要素数に変数を入れたいときには

よろしくお願いします。
配列の要素数には定数しか入れられないのですが,どうしても変数を入れたいときは,それを引数として関数を呼び出すしか方法はないでしょうか。
具体的には,scanfで手に入れたint型の変数を要素数とする配列を宣言したいのですが,どうすれば良いでしょうか。
ご教授ください。

Aベストアンサー

c99と呼ばれる最近の規格では、配列の大きさに変数を使用できます。
bccはc99に対応していないようです。

それ以前の規格では、動的領域確保関数 malloc や callocを使って領域を確保するか、効率等を無視してバカデカい配列を用意しておくかです。
「それを引数として関数を呼び出す」っていうのは、malloc/callocのことですか?


人気Q&Aランキング