• ベストアンサー

for 文における処理の改善(C言語プログラム)

はじめまして。panicdjです。 いまCでプログラムを組んでいます。 環境はVC++ver6.0 Win32 Console Applicationです。 以下のプログラムを見てください。 #define X_MAX 10 #define Y_MAX 20 #define Z_MAX 5 int main(int argc , char ** argv) { int i, j, k; int aa[10][20][5]; for (i = 0; i < X_MAX; i ++) { for (j = 0; j < Y_MAX; j ++) { for (k = 0; k < Z_MAX; k ++) { aa[i][j][k] = 10.0; } } return 0; } 過去のスレッドでポインタ型によるアクセスを すれば、処理が高速になるとかかれていました。 自分は,for文による繰り返す処理ではなく, その「ポインタ型によるアクセス」を実装したいのです。 こんな私にアドバイスお願い致します。

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

  • ベストアンサー
  • yatokesa
  • ベストアンサー率40% (201/496)
回答No.9

> C言語などを解説しているサイトでのサンプルコードとかでしょうか?? も、含めてですね。オープンソースなプログラムとかでもよいと思います。勉強の種は沢山転がってますから探してみてください。 > これは、たとえばVCを用いているのであれば、デバッカの プログラミングの際の話のつもりで書きました。頭の中にメモリのイメージ(箱とか?)を思い浮かべると良いです。今宣言した変数はどこにあるのか、とか、ポインタの先の実体はどこなのかというのを常に意識してということです。 > できることなら、参考になる書籍やサイトを提示させて 残念ながら Cに関しての書籍・サイトともに知りません。 が、http://alfin.mine.utsunomiya-u.ac.jp/~niy/algo/index.html で集められた関数等は勉強になると思います。

参考URL:
http://alfin.mine.utsunomiya-u.ac.jp/~niy/algo/index.html
panicdj
質問者

お礼

yatokesaさん、何度もアドバイスありがとうございます。 >> C言語などを解説しているサイトでのサンプルコードとかでしょうか?? >も、含めてですね。オープンソースなプログラムとかでもよいと思います。勉強の種は沢山転がってますから探してみてください。 はい、承知しました。 yatokesaさん、いろいろ教えて頂きありがとうございました。 なお、本スレッドでお世話になった方にも感謝しています。 ありがとうございました!!

その他の回答 (8)

  • yatokesa
  • ベストアンサー率40% (201/496)
回答No.8

>for (i = X_MAX*Y_MAX*Z_MAX; i; i--) { >とすると、速くなるのでしょうか? 理屈は #6:terra5さんのとおりです。また、terra5さんの仰るとおり、コンパイラが勝手に最適化する可能性もありますので、知っている人が見れば気分的に早いコードだ!って気分になれる程度の速さです^^;)。デバッグモードなどでアセンブラとの混合リストを見ると違いが分かるかもしれません。 >レベルなのでなんとかその域から脱却したのですが。 私が Cを始めたのはもう十ン年前なので...。私が学習したのは「はじめてのC」というやつですがね。公開されている色々な人のコードを見るのが良い勉強になると思います。 ポインタを理解したいのなら、常に変数がどのメモリ(スタックも含めて)に配置されているのかを考えながらコーディングすることです。

panicdj
質問者

お礼

yatokesaさん,何度もアドバイスいただきありがとうございます。 >公開されている色々な人のコードを見るのが良い勉強になると思います ちょっと、別件なのですが、↑は,サイトで公開されているコードとは、 C言語などを解説しているサイトでのサンプルコードとかでしょうか?? >ポインタを理解したいのなら、常に変数がどのメモリ(スタックも含めて)に配置されているのかを考えながらコーディングすることです。 上記についてもう少し教えてください. これは、たとえばVCを用いているのであれば、デバッカの 混合モードを開いてアセンブラでどの変数がpushされているか とか確認することなのでしょうか?? 私は、詳しく混合モードの使い方 (例えば、espレジスタなどのレジスタの詳しい機能 他) がよくわかっていません。 できることなら、参考になる書籍やサイトを提示させて 頂けると助かるのですが. 以上、どうかよろしくおねがいします!

回答No.7

#4 a-kuma様 int* p_end = &aa[X_MAX][Y_MAX][Z_MAX]; は int* p_end = &aa[X_MAX-1][Y_MAX-1][Z_MAX-1] + 1; が正しいのではないかと... (^^;;;

panicdj
質問者

お礼

syuyamakawaさん、ご助言ありがとうございましたっ!!

  • terra5
  • ベストアンサー率34% (574/1662)
回答No.6

ポインタを理解するなら意味がありますが、 高速化の為に・・なら、あまり意味がないですね。 まず、意味があるほどの差がでないか、変わらないですし、 読みづらいプログラムは悪です(^^; >for (i = X_MAX*Y_MAX*Z_MAX; i; i--) { >とすると、速くなるのでしょうか? アセンブラレベルの話になりますが、 CPUの命令は、ある値とある値が等しいかを比べる命令が無いか, あってもある数値が0かどうかを調べる命令より実行速度がたいてい遅いからです。 (命令が無い場合は引き算をして、結果が0かどうかを調べるという、二つの命令を実行することになります) ただ、コンパイルしてアセンブラになる時に どんな命令になるかはわかりませんから、 必ずしも速いとは言い切れません。 現在のコンパイラは最適化といって、 こういう高速化のための小細工は勝手にやりますので。

panicdj
質問者

お礼

terra5さん、詳しい解説ありがとうございました. 「処理はコンパイラが最適化する」とのことですが、 処理の高速を考える際は,どのような観点から コードを組めばよろしいのでしょうか?? よろしくおねがいします!!

  • mkii
  • ベストアンサー率40% (43/105)
回答No.5

int aa[X_MAX][Y_MAX][Z_MAX]; int *p = (int *)aa + sizeof (aa) / sizeof (int); while (p != (int *)aa) *--p = 10; 怒られそうだ(笑)

panicdj
質問者

お礼

mkiiさん、ご助言ありがとうございます!!

  • a-kuma
  • ベストアンサー率50% (1122/2211)
回答No.4

No.2 の回答にある >「見やすさを優先するためのプログラミングをした方が良いと思います。」 に大きく同意しつつ、もうちょっと早そうなコードを。 int aa[X_MAX][Y_MAX][Z_MAX]; int* p = &aa[0][0][0]; int* p_end = &aa[X_MAX][Y_MAX][Z_MAX]; while (p != p_end) {   *p++ = 10; } もし、 int* p_end = &aa[X_MAX][Y_MAX][Z_MAX]; が気持ち悪いのであれば、 int* p_end = p + X_MAX * Y_MAX * Z_MAX; でも OK 。 # この程度のサイズなら、たいした差は出ないと思いますよ。

panicdj
質問者

お礼

a-kumaさん、名前によらず天使のようなアドバイスありがとうございます! >int* p_end = &aa[X_MAX][Y_MAX][Z_MAX]; (1) >が気持ち悪いのであれば、 >int* p_end = p + X_MAX * Y_MAX * Z_MAX; (2) 僕の場合、なぜか(1)の方の初期化の方がしっくりきてしまいました。 (2)のような初期化はたいへん参考になります。 どうもありがとうございました!

  • yatokesa
  • ベストアンサー率40% (201/496)
回答No.3

fuji1さんと同じ回答になってしまいました for (i = 0; i < X_MAX*Y_MAX*Z_MAX; i++) {  ↓ for (i = X_MAX*Y_MAX*Z_MAX; i; i--) { とした方が、多少スピードアップになると思います。

panicdj
質問者

補足

すみません、なぜ以下のように、 for (i = X_MAX*Y_MAX*Z_MAX; i; i--) { とすると、速くなるのでしょうか?

  • yatokesa
  • ベストアンサー率40% (201/496)
回答No.2

見やすさを優先するためのプログラミングをした方が良いと思いますよ。コンパイラの最適化によってあまり速度差がでないこともあります。 上限の決まっている領域の初期化は、for文かwhileを使うのは仕方ないことでしょう。それをふまえて、ポインタを使うと次のような感じで出きると思います。 int *p; int aa[X_MAX][Y_MAX][Z_MAX]; // 折角defineしているのだから、定数を使いましょう int i; p = (int*)aa; for (i = 0; i < X_MAX*Y_MAX*Z_MAX; i++) {  *p++ = 10; // 10.0は浮動小数です } ですか、ね。

panicdj
質問者

お礼

yatokesaさん、お返事ありがとうございます。 >見やすさを優先するためのプログラミングをした方が良いと思いますよ。 ごもっともですが、僕のプログラムはまだまだです。 細かい点も注意して頂きありがとうございます。 PS。yatokesaさんがプログラムを組む上で、または勉強した本やサイトの 中で推薦できるものがあったら教えて頂きたいのですが。。 僕は、一般的なC言語本にあるサンプルコードのような レベルなのでなんとかその域から脱却したのですが。

  • fuji1
  • ベストアンサー率29% (109/371)
回答No.1

こんにちは。 int 型なのに、10.0 入れてはいけませんよ。(^o^)丿 さて、配列の場合は、基本的にはリニアにメモリを確保されることになります。 ですから、 #define X_MAX 10 #define Y_MAX 20 #define Z_MAX 5 int main(int argc , char ** argv) { int i; int aa[10][20][5]; int *p ; p = (int *)aa ; for (i = 0; i < X_MAX * Y_MAX * Z_MAX; i ++) { *(p++) = 10 ; } return 0; } でOKだと思います。 ただ、このリニアにとられるというのは、どんなときにもとは行かないので、アーキテクチャを確認する必要はありますね。 Wintel 系だったら大丈夫だと思いますが。

panicdj
質問者

お礼

fuji1さん早速の解答ありがとうございます。 御返答の内容について質問があります。 >配列は、基本的にはリニアにメモリを確保されることになります。 つまり、malloc()関数を用いたときと 異なり、連続的にメモリが確保されるというこですね? >ただ、このリニアにとられるというのは、どんなときにもとは行かないので、 >アーキテクチャを確認する必要はありますね。 ↑のことがよくわかりません。 リニアに確保できないarchitectureとは例えばどのような 構造でしょうか? よろしければついでに教えて頂きたいのですが・・・ >Wintel 系だったら大丈夫だと思いますが。 Pentium 3を使っています.

関連するQ&A

  • C言語のプログラムでおかしな動作をするのですが教えて頂けないでしょうか?

    VisualStudio2008使用しています。 問題は、サイコロを200回振ってその出た目の数の個数分*を表示するプログラムです。 サイコロの目はランダムで出しています。 次のプログラムは正常に動作するものです。 /* #include <stdio.h> #include<stdlib.h> #include<time.h> #define N 200 int DICE(int min,int max); int main() { int n,i,j; int y[7]={0}; srand((unsigned int)time(NULL)); for(i=0;i<N;i++){ n=DICE(1,6); y[n]++; } for(i=1;i<7;i++){ printf(" %2d: ",i); for(j=0;j<y[i];j++){ printf("*"); } printf("\n"); } return 0; } int DICE(int min,int max) { return min+(int)(rand()*(max-min+1.0)/(1.0+RAND_MAX)); } */ 次のプログラムが問題で、授業で先生が配列にはstaticをおまじないとしてつけないと暴走すると言われたので、つけて見ると明らかに間違ってると思われるプログラムで動作するのですが原因を教えて頂けないでしょうか? 以下問題のプログラム! 配列の前にstaticをつけたら、添え字をいくつにしても正常に動作します。普通は添え自分しか領域って確保されないですよね??? /* #include <stdio.h> #include<stdlib.h> #include<time.h> #define N 200 int DICE(int min,int max); int main() { int n,i,j; //以下が問題の配列宣言 static int y[2]={0}; srand((unsigned int)time(NULL)); for(i=0;i<N;i++){ n=DICE(1,6); y[n]++; } for(i=1;i<7;i++){ printf(" %2d: ",i); for(j=0;j<y[i];j++){ printf("*"); } printf("\n"); } return 0; } int DICE(int min,int max) { return min+(int)(rand()*(max-min+1.0)/(1.0+RAND_MAX)); } 質問の意味が正確に伝わらなかった場合は補足しますので、ご回答よろしくお願いします。

  • C言語のプログラムで質問です。

    C言語のプログラムで質問です。 下のプログラム(最小二乗法の計算)を実行したところ -1.#IND00 というエラーが出てしまいます。 どこを直せばいいのでしょうか、教えてください。 #include <stdio.h> #include <math.h> /* gauss33.c */ #define N 3 main(){ double A[N][N],Aa[N][N]; double b[N],x[N], bb[N], e[N]; int n=N; int i, j, k; double akk, aik, s; double y[N]; double xx,yy; for(i=0;i<n;i++){ /*変数の初期化*/ x[i]=y[i]=0; for(j=0;j<n;j++) A[i][j]=0; } for(i=0;i<5;i++){ /*データ点は5点*/ printf("\n(x,y)="); scanf("%lf,%lf",&xx,&yy); A[0][0]+=xx*xx*xx*xx; /*Σx^4*/ A[0][1]+=xx*xx*xx; /*Σx^3*/ A[0][2]+=xx*xx; /*Σx^2*/ A[0][1]=A[1][0]; A[0][2]=A[1][1]=A[2][0]; A[1][2]+=xx; /*Σx*/ A[1][2]=A[2][1]; A[2][2]=n; y[0]+=xx*xx*yy; /*Σx^2y*/ y[1]+=xx*yy; /*Σxy*/ y[2]+=yy; /*Σy*/ } /* save original coefficients */ for(i=0; i<n; i++){ for(j=0; j<n; j++){ Aa[i][j]=A[i][j]; } bb[i]=b[i]; } /* forward operation */ for(k=0; k<n-1; k++){ akk=1/A[k][k]; for (i=k+1; i<n; i++){ aik=-A[i][k]*akk; for (j=k+1; j<n; j++){ A[i][j]+=aik*A[k][j]; } b[i]+=aik*b[k]; } for(j=k+1; j<n; j++){ A[k][j]*=akk; } b[k]*=akk; } /* backward operation */ x[n-1]=b[n-1]/A[n-1][n-1]; for(k=n-2; k>=0; k--){ s=0.0; for (j=k+1; j<n; j++){ s+=A[k][j]*x[j]; } x[k]=b[k]-s; } /* chek */ for(i=0; i<n; i++){ s=0.0; for(j=0; j<n; j++){ s+=Aa[i][j]*x[j];} e[i]=s-bb[i]; printf("\nx(%d)=%f error=%f\n",i, x[i], e[i]); } }

  • C言語に関して

    C言語に関して 100までの自然数を文字列に変換したいのですが、以下のプログラムを実行すると、001,002,…010,…099,100のようになってしまいます。左詰めにしたいのですが、どこが間違っているかご教示下さい。 #include <stdio.h> #define N1 100 #define N2 5 int get_ketasuu(); void henkankun(); int main(void) { int i, dig, x; int num1 = N1; int num2 = N2; int buff1[N1], buff2[N1]; char buff3[N1][N2]; for (i = 0; i < N1; i++) { x = buff2[i] = buff1[i] = i + 1; dig = get_ketasuu(x); henkankun(&buff2[i], &buff3[i], dig); printf("%s\n", buff3[i]); } return 0; } int get_ketasuu(x) int x; { int dig; dig = 0; do { x /= 10; dig++; } while (x > 0); return dig; } void henkankun(x, y, dig) int *x; int dig; char (*y)[N2]; { int j, k; switch (dig) { case 1 : k = 1; case 2 : k = 10; case 3 : k = 100; } j = 0; do { (*y)[j] = (*x / k) + '0'; *x %= k; k /= 10; j++; } while (k > 0); (*y)[j] = '\0'; }

  • C言語のプログラムで質問です。

    C言語のプログラムで質問です。 これは、2元1次連立方程式の解を求めるプログラムです。 このプログラムを (1)3元1次連立方程式の解を求めるプログラムにする (2)係数行列、定数行列(6、7行目)をキーボードからの入力にする。 ようにしたいのですが、どうすればよいでしょうか。 前半の部分を変えれば良いようなのですが分かりません。教えてください。 #include <stdio.h> #include <math.h> /* gauss22.c */ #define N 2 main(){ double A[N][N]={1.,4.,3.,2.}, Aa[N][N]; /*簡単のため係数行列を予め指定*/ double b[N]={4.,5.}, x[N], bb[N], e[N]; /*簡単のため定数ベクトルを予め指定*/ int n=2; int i, j, k; double akk, aik, s; /* save original coefficients */ for (i=0; i<n; i++){ for (j=0; j<n; j++){ Aa[i][j]=A[i][j]; } bb[i]=b[i]; } /* forward operation */ for (k=0; k<n-1; k++){ akk=1/A[k][k]; for (i=k+1; i<n; i++){ aik=-A[i][k]*akk; for (j=k+1; j<n; j++){ A[i][j]+=aik*A[k][j]; } b[i]+=aik*b[k]; } for (j=k+1; j<n; j++){ A[k][j]*=akk; } b[k]*=akk; } /* backward operation */ x[n-1]=b[n-1]/A[n-1][n-1]; for (k=n-2; k>=0; k--){ s=0.0; for (j=k+1; j<n; j++){ s+=A[k][j]*x[j]; } x[k]=b[k]-s; } /* chek */ for (i=0; i<n; i++){ s=0.0; for (j=0; j<n; j++){ s+=Aa[i][j]*x[j];} e[i]=s-bb[i]; printf("x(%d)=%f error=%f?n",i, x[i], e[i]); } }

  • C言語、行列の積を求めるプログラムについて

    「次に示す行列x,yの積を求めるプログラムを作成せよ。   x[2][3]={{1,2,3},{4,5,6}} y=[3][2]={{1,5},{5,3},{81}}」 という問題です。自分ではとりあえず、 #include<stdio.h> int main(void) { int i,j; int x[2][3]={{1,2,3},{4,5,6}}; int y[3][2]={{1,5},{5,3},{8,1}}; int xy[3][3]={0}; for(i=0;i<3;i++) for(j=0;j<3;j++) xy[i][j]=x[i][j]*y[i][j]; for(i=0;i<3;i++){ for(j=0;j<3;j++) printf("%3d",xy[i][j]); putchar('\n'); } return 0; } というプログラムを作ってみましたが、ダメでした。 ちゃんと積の表示が出るようにするにはどこをどう変えるべきでしょうか?

  • c言語

    c言語で写真の課題を出されたのですが自分のプログラムでは上手くいきません。どこが間違っているのか教えて欲しいです。 自分のプログラム #include<stdio.h> #include<math.h> int main(){ int i,j; double c,d,x,y,z; for(i=0;i<=360;i++){ c=10*cos(i*M_PI/180); d=10*sin(i*M_PI/180); if(c>=0 && d>=0){ for(j=0;j<=1000;j++){ x=0.001*j; y =x*d/c; z=1-x*x-(sqrt(x)+y)*(sqrt(x)+y); if(z<=0.0){break;} } } if(c<=0 && d>=0){ for(j=0;j<=1000;j++){ x=-0.001*j; y=x*d/c; z=1-x*x-(sqrt(-x)+y)*(sqrt(-x)+y); if(z<=0.0){break;} } } if(c<=0 && d<=0){ for(j=0;j<=1000;j++){ x=-0.001*j; y=x*d/c; z=1-x*x-(sqrt(-x)+y)*(sqrt(-x)+y); if(z<=0.0){break;} } } if(c>=0 && d<=0){ for(j=0;j<=1000;j++){ x=0.001*j; y=x*d/c; z=1-x*x-(sqrt(x)+y)*(sqrt(x)+y); if(z<=0.0){break;} } } printf("x=%lf y=%lf z=%lf\n",x,y,z); } return(0); }

  • C言語 行列の積

    行列の積をfor文を使って計算したいです。 #include<stdio.h> int main(void){ int x[2][2]={{1,3},{2,4}}; int y[1][2]={3,2}; int xy[1][2]; int i, j, k; for (i = 0; i < 1; i++) { for (j = 0; j < 2; j++) { xy[i][j] = 0; for (k = 0; k < 2; k++) { xy[i][j] += x[i][k] * y[k][j]; } } } xの二行目の計算からできません。 プログラムの修正をお願いします。

  • c言語 パスカルの三角形

    c言語でパスカルの三角形を出力するプログラムを作りたいのですが、上手くいきません。 何を直せばいいのか教えてください。 #include <stdio.h> #define N 10 int main(void){ int i, j = 1, x, y; int d[N][N]; /* 三角形を作成 */ for (i = 1 ; i < N ; i++){ d[i][0] = 1; while (j <= i - 1){ d[i][j] = d[i-1][j-1] + d[i-1][j]; j ++; } } /* 三角形の表示 */ for (y = 0; y < N; y++) { for (x = 0; x < N-y; x++) printf(" "); for (x = 0; x < y; x++) printf("%3d ", d[x][y]); printf("\n"); } return 0; } 実行結果 -2147417616 2665208 1629976532 1627572249 1629101723 1 1629982744 2665256 2665548 3407923 1629345053 1627571017 0 3538997 1629739051 10 1629345053 2665368 3670071 2665384 1629739040 1627927140 2665244 1628040295 57 1628810863 1629476960 1628602749 2665560 2665304 1629345053 0 1629739040 1629740576 1628992224 2 4411498 1628040588 -2147417600 0 1629476960 1629740664 1629739040 1 267574 0

  • for文を簡単な処理に書き変えたい

    いま、配列を作る作業をしているのですが、for文を使うと、forをいくつ書けば良いのか分からない状態です。 作ろうとしている配列が、A[X][Y]で、Xの要素数は固定で良いのですが、Yの要素数が実験のたびに変動するためです。そして、処理したいのが、この配列の各列の値を適当な値(このfor文の前に値が決まっています)を代入することです。 例えば、for文だと、Y=3のときには for(i=0;i<○;i++){ for(j=0;j<○;j++){ for(k=0;k<○;k++){ A[line][0] = C[i];//(Cという配列のi成分) A[line][1] = B[j]; A[line][2] = E[k]; } } } のように書くような内容なのですが、Yが増えると、for文も増やさないといけません。 ここで、再帰処理のような方法をつかってうまく書きたいのですが、どう書いたら良いのか分かりません。 一般的なfor文の書き換え方みたいなことでも良いので、教えて頂けませんか?宜しくお願いします。

  • C言語 配列の確保

    はじめまして。C言語の勉強を最近始めたのですが、 以下のプログラムで教えていただきたい点があります。 #include <stdio.h> #include <math.h> #define x 10000 #define y 200000 #define z 1.0E-12 #define k 1.38 #define kE 1.0E-23 #define h 6.63 #define hE 1.0E-34 #define c 3.00 #define cE 1.0E+08 void main(void){ int i; double A[x+1]; double B[x+1]; for(i=0;i<=x;i++) { A[i]=(i+y)*z; B[i] = exp(-(h*hE*c*cE)/(A[i]*k*kE*1000)); printf("%e %e\n",A[i],B[i]); }} このプログラムで、xを100000にするとプログラムが動かなくなってしまいます。OSはWindowsXP、ソフトはVisual C++ 6.0を使っています。 解決方法を教えていただけないでしょうか。

専門家に質問してみよう