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

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

ただいまの
回答率

90.45%

  • C

    4690questions

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

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

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 1,657

kicchii

score 4

大学で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*);の意味、必要性

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





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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

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/16 01:27

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

    キャンセル

0

2次元配列を扱う場合は、ダブルポインタを使用する必要があります。
今回の場合は、次の様に仮引数の型を変更すれば良いでしょう。
void mat_mul(float **a ,float **b)
①の回答としては、2次元配列を使う場合はダブルポインタを利用してください、という事になります。

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/16 01:32

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

    キャンセル

0

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

とりあえず、提示されたソースコードを動くように直してみました。
#include<stdio.h> 
#define ROW 3 
#define COL 3 
#define SUM 3 

void mat_mul(float a[ROW][COL], float b[ROW][COL], float c[ROW][COL])
{
    int i, j, k;
    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];
            }
        }
    }
}


int main(void)
{
    float mat[ROW][COL] = { {1.0, 2.0, 3.0} ,
                            {4.0, 5.0, 6.0} ,
                            {7.0, 8.0, 9.0} };
    float mat_pro[ROW][COL] = { 0 };
    int i, j;

    //A*A 
    mat_mul(mat, mat, mat_pro);

    //出力 
    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;
}

①引数のポインタを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/16 00:45

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

    キャンセル

  • 2015/11/16 01:31

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

    キャンセル

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

  • ただいまの回答率 90.45%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C

    4690questions

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