【C言語】二重forループ内でscanfを使ってchar型変数に数値を入力すると、きちんとループ処理されないのはなぜ?

このQ&Aのポイント
  • char型変数に値を入力しているために、forループでiがきちんとインクリメントされません。
  • 入力値は-128~127の値しか想定していないため、char型で宣言したことが原因です。
  • int型で宣言すれば正常にループ処理が行われます。なぜchar型での挙動がおかしくなるのか知りたいです。
回答を見る
  • ベストアンサー

【C言語】二重forループ内でscanfを使ってchar型変数に数値(

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

質問者が選んだベストアンサー

  • ベストアンサー
  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.1

scanfのマニュアルをよくよんでください。 フォーマット%dが要求するのはint型へのポインタです。 例え、実際にはchar型へのポインタであったとしても、きっちりint型の分の領域にデータが書き込まれます。その場合、char型より大きい分は変数の領域の外へ書き込まれることになります。 今回は、そのあふれた先が変数iの領域だったので、iが異常な値になったのでしょう。

tsubatie
質問者

お礼

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

tsubatie
質問者

補足

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

その他の回答 (3)

  • kmee
  • ベストアンサー率55% (1857/3366)
回答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やコンパイラの特性、ソフトのパフォーマンス等を十分に知った上で行わないと、節約した積もりが、かえって消費していた、ということになります。

tsubatie
質問者

補足

なるほど。 「それをわざわざchar型に制限するために、その為の処理コードが追加される可能性もあります。」 の意味が分かりました。どうやら素人がメモリの節約に気を使うのはやめた方が良さそうです。 本題以外の質問にまでご丁寧にお答え下さってありがとうございました。

  • php504
  • ベストアンサー率42% (926/2160)
回答No.3

scanfで%hhdが使えるコンパイラでしたらchar型にも代入できます でも使えないコンパイラのほうが多いですからscanfで直接char型へは代入できないと思っていたほうがいいでしょう scanfでは変換指定子と変数の型はプログラマの責任できちんとあわせましょう %dは整数型なら何でもいいと思っている人はよく見ますね short input; scanf("%hd", &input); なら使えると思います

tsubatie
質問者

お礼

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

tsubatie
質問者

補足

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=quote&resno=32617&id=dixq&quo=32622&lognum=100&from=all

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.2

>scanf("%d", &input); で、"%d"によりint型分の領域に書き込もうとするから…です。 1リットルの入るコップに4リットルのペットボトルから飲み物入れたら溢れますよね? 使うのは1リットル分でも、そんなのお構いなしです。 たかだか変数1つをint型からchar型に変えたところで、スタックの使用量がほんの数バイト節約できるだけです。 # 処理系によっては次に配置するint型のアクセス効率のために余分にメモリをとっているかも知れません。 数値はchar型よりint型で扱う方がCPUにとって自然です。 それをわざわざchar型に制限するために、その為の処理コードが追加される可能性もあります。 int型が4バイトだとして…3バイト節約するために3バイト以上のコードが増えることは望ましい結果でしょうか?

tsubatie
質問者

お礼

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

tsubatie
質問者

補足

char型分の領域から溢れた分がint型分の領域に入って悪さをしていのですね。 >たかだか変数1つをint型からchar型に変えたところで、スタックの使用量がほんの数バイト節約できるだけです。 質問で書いた小さなプログラムではメモリの節約は無意味と言ってもいいでしょうが、非常に大きなプログラムの場合だとメモリは節約するに越したことはないと思うのですが、違うのでしょうか。「処理系によっては~~余分にメモリをとっているかも知れません。」と仰るように、処理系によるのですかね。 >それをわざわざchar型に制限するために、その為の処理コードが追加される可能性もあります。 質問の本題とは反れてしまいますが、プログラミング歴が浅いのでどのような場合がそれに当たるのか分かりませんので、簡単に教えてもらえないでしょうか。char型をint型にキャストする場合がそれに当たるのでしょうか。

関連するQ&A

  • 二重ループのあるプログラム(C言語)

    #include <stdio.h> int main(void) { int i, j, c, c2; c = 0; for(i = 100; i < 1000; i++) { c2 = 0; for(j = 1; j <= i; j++) { if (i % j == 0) c2++; } if (c2 % 2 == 1) c++; } printf("%d個です。\n", c); return 0; } というプログラムがあるのですが、2重ループ部分のそれぞれのループに対応して、 2つの関数として独立させるとどのようになりますか? また、2つの関数のいずれにおいても、ループを用いずに再帰呼び出しを用いるとどうなりますか?

  • forループ

    #include<stdio.h> int main(int argc, char** argv) { int i; printf("正の整数を入力してください。\n"); scanf("%d",&i); for(;i;i--) { printf("%d\n",i); } return 0; } このプログラムの判定部がiだけなのがわかりません。どうしてこれで1までのカウントダウンがとまるのでしょうか。

  • for 3ループについて教えて

    for 3ループについて教えて * ** *** **** * ** *** **** * ** *** **** と表示させたいのですが、 #include<stdio.h> int main(void) { int i,j,k; for(i=1; i<=4; i++) { for(j=1; j<=i; j++){ // for(k=1; k<=3; k+=i){ } printf("*"); } printf("\n"); } return 0; } * ** *** **** このように表示されてしまいます。//の所が違うなと思います。が、分かりそうで分かりません。 もし、分かるかたがいましたら、教えてください。 よろしくおねがいします。

  • シーザー暗号(C言語)

    シーザー暗号というものを作ろうとしていますが、文字のずらし方がわからず詰まっています。 手順は一応考えてあります。 (1)入力された文字を配列input[301]に代入 (2)何文字ずらすかを指定してその数値を変数countに代入 (3)入力された文字の長さを変数lenに代入 (4)inputとcountとlenを、文字をずらしてそれをoutputに返す関数shiftに渡す (5)outputを表示する のような感じですが、肝心の文字のずらし方がわかりません。 JISコードを使ってやるといいと聞いたんですが、そのやり方がわかりません。 ソース↓ #include <stdio.h> #include <string.h> int shift_char(char x[], int y, int z) { int i; for(i = 0; i <= z; i++) { x[i] = x[i] + y; } return x; } int main(int argc, char* argv[]) { char input[301]; char output[301]; int count, len, i; printf("文字を入力してください: "); scanf("%s" ,input); printf("何文字ずらしますか: "); scanf("%d" ,count); len = strlen(input); output = shift_char(input, count, len); for(i = 0; i <= len; i++) { printf("%s" ,output[i]); } return 0; } よろしくお願いします。

  • C言語プログラミングで・・・

    つい最近、C言語プログラミングのお勉強をはじめた中学生です。 ある程度かけるようになってきて、こちらのサイトに掲載されている練習問題を解いていました。 http://akabas.net/lib/CExercise.aspx いろいろといていく中で、配列の章にはいりました。 配列の章の中の6問目と7問目の九九についてのコードを書いていたところ、どちらも問題なく実行を終了した後 「○○.exeは動作を停止しました。」というメッセージが現れました。 これはコードに何か問題があるのでしょうか? _____________________________________________________________________ //6問目 #include <stdio.h> void line (void){ int cnt; for (cnt = 0; cnt < 30; cnt++) printf("_"); printf("\n"); } int main() { int kuku[9][9], i, j; printf("九九を表示します\n"); for (i = 1; i <= 9; i++){ for (j = 1; j <= 9; j++){ kuku[i][j] = i * j; printf("%d * %d = %d\n", i, j, kuku[i][j]); } line(); } return 0; } ____________________________________________________________________ //7問目 #include <stdio.h> int main() { int kuku[9][9]; int i, j; for (i = 1; i <= 9; i++){ for (j = 1; j <= 9; j++) kuku[i][j] = i * j; } printf("1桁の数値(1 ~ 9に限定)を入力してください 1回目---"); scanf("%d", &i); printf("1桁の数値(1 ~ 9に限定)を入力してください 2回目---"); scanf("%d", &j); printf("\n"); printf("%d * %d = %d", i, j, kuku[i][j]); return 0; } ________________________________________________________________________________ まだ初心者のレベルなので、回答はできたらきつい言葉をつかってほしくないです^^; よろしくおねがいします ※こちらでは、Tabをうっているのですが、表示されてないみたいです。見づらいかと思いますが、よろしくおねがいします

  • C言語で困っています...

    入力した数値の列だけ○と×を縦に、段々になるように表示する (最後の行は×にならなければいけない)プログラムを作成しています。 (実行例) ○と×を表示します。何列?:10            ×           ○×          ×○×         ○×○×         ×○×○×       ○×○×○×      ×○×○×○×    ○×○×○×○× ×○×○×○×○× ○×○×○×○×○× 途中までプログラミングできたのですが、 偶数の数値を入れたときはきちんと最後の行は×になっても 奇数の数値を入力すると○が最後の行になってしまい、うまくいきません。 どこがだめなのでしょうか? どなたか教えてください。 /* 入力した数値の列だけ、○と×を縦に、   段々になるように表示する(最後の行は×になる) */ #include <stdio.h> int main(void) { int i, j, n; printf("○と×を表示します。何列?:"); scanf("%d", &n); for (i=0; i<n; i++){ for (j=0; j<n; j++){ if (j >= (n - 1) - i){ if (j % 2 == 0) printf("○"); else printf("×"); } else printf(" ");//全角スペース } printf("\n"); } return 0; }

  • c言語です

    c言語初心者です。今大学の課題でc言語のプログラムをかいているのですが、うまくいかないので助けて欲しいです。以下のようなプログラムで調和平均の値を出したいのですがなぜか表示されません。infと表示されてしまいます。あと調和平均とはn/(1/d1+1/d2+1/d3+.........+1/dn)の値です。ここでd1~dnは入力された数のことです。なので0が入力された場合は値を返さないようにしたいです。   どこをどのように直せばいいのか教えてください。よろしくお願いします。 #include <stdio.h> #include<math.h> #define M 1000 int main (int argc, const char * argv[]) { // insert code here... float d[M]; double e[M]; double wa=0.0,av,bun=0,kika=1,tyowa=0; /*avはaverageを,bunは分散を意味します*/ int i=0,j=0; printf("複数の数字を入力してください\n"); printf("数字入力を終了するときはnull文字を入力してください\n"); printf("数字以外が入力されたら\n"); printf("それまでの数字の計算結果を表します\n"); /*数字を入力、和を求める*/ while (scanf("%f",&d[i])!='\0') { wa=wa+d[i]; i++; } if (i!=0) { printf("算術平均は%fです\n",wa/i); av=wa/i; for (j=0; j<i; j++) { kika=kika*d[j]; } printf("幾何平均は%fです\n",pow(kika,1./i)); for (j=0; j<i; j++) { if (d[j]=0) { printf("調和平均は出せません\n"); } else e[j]=1/d[j]; } for (j=0; j<i; j++) { tyowa=tyowa+e[j]; } printf("調和平均は%fです\n",e[1]); for (j=0; j<i; j++) { bun=bun+(d[j]-av)*(d[j]-av); } printf("分散の値は%fです\n",bun/i); } else { printf("数字を入力してください\n"); } return 0; }

  • c言語のプログラミングについて聞きたいのですが

    自分は課題で「10人分の名前と点数を入力して降順に並び替える」というプログラムを作ろうとしているのですが、なかなかうまくいきません。一応↓の形までできたのですがなぜか数字が変になります。 どうしてか教えていただけないでしょうか。 #include<stdio.h> int main(void) {     int i,j;     int tensu[10][3];     char namae[10][6]; for(i=0;i<10;i++){ printf("名前の入力>>"); scanf("%s",&namae[i]); printf("点数の入力>>"); scanf("%d",tensu[i]); } printf("名前   点数\n"); for(j=0;j<10;j++) { printf("%s ",&namae[j]); printf("%10d\n",tensu[j]); } return 0; } それとポインタ?みたいなのもよく理解できず、点数で降順にしたあとに点数を使って名前を並び替えるのかなと思うのですがそれもよくわかりません。 どなたか教えてください

  • C言語をお願いします

    何が違うのか教えてください。 segmentation faultになります。 よく分からないので、プログラムを作っていただければ、助かります。 問 整数を入力し、降順並び変えてに表示。 ・入力した整数は配列に入れ、その配列を使って並び変える(入力終りの印は 1000 とする)。 ・入力する整数の個数は #define NUM 100 を使いなさい。 ・使うデータは、下記の例のように、キーボードから入力すること。 #include<stdio.h> #define NUM 100 int main(void){ int d[NUM]; int temp; int i,j,n; printf("Input scores.\n"); for(i=0; i<NUM && d[i]!=1000; i++){ scanf("%d",&d[i]); } n = i; for(i = 0; i < n; i++){ for(j = i + 1; j < n;j++){ if(d[j] > d[i]){ temp = d[i]; d[j] = d[i]; d[i] = temp; } } } printf("After sort."); for(i=0; i < n; i++){ printf("%d\n",d[i]); } return 0; } 実行例 Input scores. 60 30 45 90 100 0 1000 After sort. 100 90 60 45 30 0 よろしければ 問2 並び変えをする部分を mysort 関数にしたプログラムを作ってください。 main 関数から mysort 関数には点数の個数と sort 前の配列を渡し、並び変え結果の表示はmain 関数でお願いします。 (問題の意味が分かりません) 関数はさっぱり分かりません。 では、お願い致します。

  • for文内でscanf関数により配列に数値を格納することについて

    プログラミング初心者です。 よろしくお願いします。 ◎1------------------------------------ #include<stdio.h> int main(void) { int i,dt[3]; for(i=0;i<3;++i){ printf("dt[0]=%d\n",dt[0]); printf("dt[1]=%d\n",dt[1]); printf("dt[3]=%d\n",dt[3]); scanf("%d",&dt[i]); printf("i=%d\n",i); puts(""); if(dt[i]==0){ break; } } printf("dt[0]=%d\n",dt[0]); printf("dt[1]=%d\n",dt[1]); printf("dt[2]=%d\n",dt[2]); printf("dt[3]=%d\n",dt[3]); printf("i=%d\n",i); puts(""); i=1; while(1){ printf("%d ",i); if(1000<i){ break; } i*=2; } puts(""); return 0; } ---------------------------------------- ◎2------------------------------------ #include<stdio.h> int main(void) { int i,dt[2]; for(i=0;i<3;++i){ printf("dt[0]=%d\n",dt[0]); printf("dt[1]=%d\n",dt[1]); printf("dt[2]=%d\n",dt[2]); scanf("%d",&dt[i]); printf("i=%d\n",i); puts(""); if(dt[i]==0){ break; } } printf("dt[0]=%d\n",dt[0]); printf("dt[1]=%d\n",dt[1]); printf("dt[2]=%d\n",dt[2]); printf("i=%d\n",i); puts(""); i=1; while(1){ printf("%d ",i); if(1000<i){ break; } i*=2; } puts(""); return 0; } ---------------------------------------- 以上2つのプログラムについて疑問があります。 まず◎1については、あえて添え字のdt[3]の値を見てみようと思ったら、iの値が入っているとわかりました。 しかし、何故添え字の番号の配列にiの値が入っているのかがわかりません。 次に◎2ですが、3回目のループで、添え字の番号の配列自身に入力した数値を格納すると、iと添え字番号配列に入力した数値が入っていました。 何故このようになっているのか疑問です。 以上のような疑問があります。 添え字の番号の配列とiが何か関連があるようですがいまいちわかりません。 教えていただけると嬉しいです。