質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

4308閲覧

2次元配列のポインタ関数

kicchii

総合スコア12

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2015/11/15 14:39

大学でC言語を勉強し、プログラミング歴1年未満の初心者です。

以下のコードのmain関数内の行列の掛け算を行うような関数mat_mulを作りたいのですが、うまくいかないので問題点を指摘していただきたいです。

#include<stdio.h>
#define ROW 3
#define COL 3
#define SUM 3

void mat_mul(float*,float*);

void mat_mul(float *a ,float *b){

int i,j,k; float c[100][100]={0}; for(i=0;i<ROW;i++){ for(j=0;j<COL;j++){ for(k=0;k<SUM;k++){ *(*(c+i)+j) += *(*(a+i)+k) * *(*(b+k)+j); } } } return c;

}

int main (void){

float mat[100][100]={ {1.0, 2.0, 3.0} , {4.0, 5.0, 6.0} , {7.0, 8.0, 9.0} }; float mat_pro[100][100]={0}; int i,j,k; //A*A for(i=0;i<ROW;i++){ for(j=0;j<COL;j++){ for(k=0;k<SUM;k++){ *(*(mat_pro+i)+j) += *(*(mat+i)+k) * *(*(mat+k)+j); } } } //出力 for(i=0;i<ROW;i++){ for(j=0;j<COL;j++){ if(j==2){ printf("%f\n",*(*(mat_pro+i)+j)); }else{ printf("%f ",*(*(mat_pro+i)+j)); } } } return 0;

}

mat_mul関数内の*(*(c+i)+j) += ((a+i)+k) * ((b+k)+j);でポインタの付け忘れというエラーが出てしまいます。
今回main関数内はこれはこれでいいのですが、最終的にはmat_mul関数を使ってmain関数内をもっとコンパクトにしたいです。

自分で調べたところ、こちらのサイト
http://bituse.info/c/30
を参考にしたのですが・・・。

疑問点として
①引数のポインタを2次元配列にしたい場合はどこで決めるべきなのか?
void func(int *temp){
のように書く場合、tempが2次元というのはどこで決めるべき?

②戻り値をポインタにした場合、main関数内でmat_mul(*a,*a)はmat_mul関数内でいうcの先頭アドレスを指すのか?

③void func(int*);の意味、必要性

以上の内容を踏まえて、正しい書き方を教えてください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

1次元配列の場合は*(a+i)とa[i]が同じ場所を指すのですが、2次元配列の場合は、残念ながら、((a+i)+j)とa[j][i]は同じになりません。(もちろん、a[i][j]とも異なります。)

固定長(行数と列数が決まっている)ようですので、全てROW, COLを使って定義すると良いと思いますよ。
void mat_mul(float a[ROW][COL] ,float b[ROW][COL])
などです。

後、c[100][100]はmat_mul()内で定義(確保)していますので、mat_mul()から戻る時に解放されてしまいます。ですので、そこへのポインタをmat_mul()のreturnで返却しても戻った時には指す先が解放され、使えません。
void mat_mul(float a[ROW][COL] ,float b[ROW][COL], float c[ROW][COL])
のようにして渡すと良いですよ。
もちろん、c[][]の領域もmain()側で確保して下さい。

②戻り値をポインタにした場合、main関数内でmat_mul(*a,*a)はmat_mul関数内でいうcの先頭アドレスを指すのか?

その通りなのですが、上記のようにcの内容はmat_mul()から戻った時に解放されるため、cの先頭アドレスに入っている値は壊れている可能性があります。(と言いますか、大抵の場合直ぐに壊れます。)

void mat_mul(float*,float*);

は上記ソースの場合、書いても意味が無いです。
これは、mat_mul()を定義しないで呼び出したい時に使う構文です。
例えば、foo.c内でmat_mul()関数を定義し、main.cからmat_mul()関数を呼び出したいような時に、void mat_mul(float*, float*);をfoo.hに書いてmain.cから#includeするような使い方が多いです。

投稿2015/11/15 15:31

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kicchii

2015/11/15 16:27

丁寧な説明ありがとうございます!どれも理解することができました!
guest

0

2次元配列は2次元配列として使用してください。ポインタのポインタ(ダブルポインタとも言う)を2次元配列として使うテクニックもありますが、float a[100][100]のように定義された配列変数に対しては使用できません。

とりあえず、提示されたソースコードを動くように直してみました。

C

1#include<stdio.h> 2#define ROW 3 3#define COL 3 4#define SUM 3 5 6void mat_mul(float a[ROW][COL], float b[ROW][COL], float c[ROW][COL]) 7{ 8 int i, j, k; 9 for(i = 0; i < ROW; i++) 10 { 11 for(j = 0; j < COL; j++) 12 { 13 for(k = 0; k < SUM; k++) 14 { 15 c[i][j] += a[i][k] * b[k][j]; 16 } 17 } 18 } 19} 20 21 22int main(void) 23{ 24 float mat[ROW][COL] = { {1.0, 2.0, 3.0} , 25 {4.0, 5.0, 6.0} , 26 {7.0, 8.0, 9.0} }; 27 float mat_pro[ROW][COL] = { 0 }; 28 int i, j; 29 30 //A*A 31 mat_mul(mat, mat, mat_pro); 32 33 //出力 34 for(i = 0; i < ROW; i++) 35 { 36 for(j = 0; j < COL; j++) 37 { 38 if(j == 2) 39 { 40 printf("%f\n", mat_pro[i][j]); 41 } 42 else 43 { 44 printf("%f ", mat_pro[i][j]); 45 } 46 } 47 } 48 49 return 0; 50}

①引数のポインタを2次元配列にしたい場合はどこで決めるべきなのか?

あまりポインタを意識しすぎると混乱しますので、2次元配列は2次元配列として考えてください。

②戻り値をポインタにした場合、main関数内でmat_mul(*a,*a)はmat_mul関数内でいうcの先頭アドレスを指すのか?

C言語では配列変数を戻り値として返すことはできません(方法がないわけではありませんが)。
上記コードのmat_mul関数を見ていただくと判ると思いますが、引数が一つ増えています。3つめの引数が所謂out引数(出力引数)と呼ばれるもので、関数内での計算結果を受け取るためのものです。C言語ではこのように計算結果を引数で受け取るというケースもあります。

③void func(int*);の意味、必要性

関数プロトタイプ宣言というもので、関数が、呼び出している場所より後ろに書かれていたり、別のファイルに書かれていたりするときに必要になります。ご質問で提示されているコードのように呼び出す場所(main関数)より前に書く場合は不要です。

それに加えて、せっかく#defineでROWとCOLを定義しているのですから、2次元配列もそれを使って定義しましょう。

投稿2015/11/15 15:43

catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

catsforepaw

2015/11/15 15:45

おっと、Chironianさんと内容がかぶっていますね。そちらをご参照ください。
kicchii

2015/11/15 16:31

わざわざコーディングありがとうございます!意向に沿って、ベストアンサーは先の方に。 確かに、ポインタはあまり使いたくないのですが、大学の授業の意図にそってやると難しいです・・・。
guest

0

2次元配列を扱う場合は、ダブルポインタを使用する必要があります。
今回の場合は、次の様に仮引数の型を変更すれば良いでしょう。

C

1void mat_mul(float **a ,float **b)

①の回答としては、2次元配列を使う場合はダブルポインタを利用してください、という事になります。

②は質問の内容がよく分かりませんが、mat_mul()の戻り値をポインタにしてはいけません。ローカル変数Cは、mat_mul()関数のスコープ内でのみ有効なので、その先頭アドレスを返却しても、呼び出し側に戻った時には既に無効になっています。

③は「#define」直下の宣言の必要性を問われているのでしょうか?
これはプロトタイプ宣言と呼ばれるものです。今回の場合は不要ですが、main()関数とmut_mul()関数の順序を逆にした場合は必要になります。
これは主にソースコードを複数のファイルに分割して記述する時などに使います。

投稿2015/11/15 15:02

shanxia

総合スコア1038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kicchii

2015/11/15 16:32

解答ありがとうございます!ダブルポインタという手法があるのですね。使いこなせるように勉強します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問