2次元配列の引数の渡しかた – プログラミング – Home

2次元配列の引数の渡しかた
 
通知
すべてクリア

[解決済] 2次元配列の引数の渡しかた

固定ページ 1 / 2

Puppy
 Puppy
(@Puppy)
ゲスト
結合: 23年前
投稿: 71
Topic starter  

Puppyです。 基礎のところなんですけど、教えて下さい。

int hai[5][10]; という配列があって

Func(&hai[3][0]);
Func(&hai[4][0]); というように関数を呼び出して、この関数内で

hai[3][5]とか、hai[4][5]のデータに変更を加えたいのです。

void Func(int *ptr[])
{
int x;
x = ptr[5] + 10;
ptr[5] = x;

}

とすると、エラーが出てコンパイルがとおりません。
int * から int *[]に変換できません と怒られました。(T-T;

どうしたらうまくいくでしょうか?
C言語の本は何冊か持ってるんですけど、こーゆーことは出ていません。
よろしくお願いします。m(..)mぺこり


引用未解決
トピックタグ
Puppy
 Puppy
(@Puppy)
ゲスト
結合: 23年前
投稿: 71
Topic starter  

Puppyです。
下記のページにのっていました。(*^-^*;
お騒がせしました。

http://www.ht-net21.ne.jp/~sgwr-t/sec11-2.htm


返信引用
Puppy
 Puppy
(@Puppy)
ゲスト
結合: 23年前
投稿: 71
Topic starter  

チェック忘れました。m(..)m


返信引用
dairygoods
 dairygoods
(@dairygoods)
ゲスト
結合: 23年前
投稿: 1421
 

hai
これは、2次元配列全体を指すポインタ。

hai[3]
これは、3行目((3,0)-(3,10))の1次元配列へのポインタ。

&hai[3][5]
これは、位置(3,5)のintへのポインタ。

でも実は、全部 int* です。
何次元の配列であっても、そのポインタは
単に先頭要素へのポインタです。


返信引用
YuO
 YuO
(@YuO)
ゲスト
結合: 24年前
投稿: 252
 

>でも実は、全部 int* です。

違います。
hai自体はint [5][10]型で,そのままint [10]へのポインタにはなりますが,
int *にはなりません。

つまり,

int (*p)[10] = hai; /* int [5][10]→int (*)[10] */
は正しいですが,
int *p = hai;
は正しくないです。


返信引用
dairygoods
 dairygoods
(@dairygoods)
ゲスト
結合: 23年前
投稿: 1421
 

>>でも実は、全部 int* です。

>違います。

すみません。間違いでした。
1次元のサイズがわからなくなってしまいますね。


返信引用
Puppy
 Puppy
(@Puppy)
ゲスト
結合: 23年前
投稿: 71
Topic starter  

dairygoodsさん YuOさん ありがとうございます。

>int *p = hai;
>は正しくないです。

私はいまだに理解できません。
できたら、例をあげて教えてもらえませんか?m(..)mペコリ

hai は &hai[0][0] と同じであり、 int型のポインタだと思っています。

間違えでしょうか?

>1次元のサイズがわからなくなってしまいますね。

この意味もわかりませんですぅ。(T-T;


返信引用
YuO
 YuO
(@YuO)
ゲスト
結合: 24年前
投稿: 252
 

>hai は &hai[0][0] と同じであり、 int型のポインタだと思っています。
>間違えでしょうか?

間違いです。

int hai[5][10];
という宣言は,
typedef int T1[10]; // T1は10個のintからなる配列の型
T1 hai[5]; // haiは5個のT1からなる配列
と同じです。 … (a)

任意の型Tと整定数nがあって,
T array[n]; // arrayはn個のTからなる配列
の時,必要に応じて(というか,ほとんどの場合),
array // arrayの型は,n個のTからなる配列

&array[0] // array[0]の型はT,&array[0]の型はTへのポインタ
となります。 … (b)

よって,(a)と(b)から,
hai // haiの型は5個のT1からなる配列

&hai[0] // hai[0]の型はT1,&hai[0]の型はT1へのポインタ
となります。

haiがint *になるためには,もう一度(b)を適用する必要があります。
ところが,一度(b)を適用するためには,&hai[0]が配列である必要がありますが,
&hai[0]は上からポインタです。

ちなみに,
hai : int [5][10]型 / int (*)[10]型になる→&hai[0]
&hai : int (*)[5][10]型
hai[0] : int [10]型 / int *型になる→&hai[0][0]
&hai[0] : int (*)[10]型
hai[0][0] : int型
&hai[0][0] : int *型
です。


返信引用
たいちう
 たいちう
(@たいちう)
ゲスト
結合: 23年前
投稿: 662
 

ちなみに、mallocで2次元の配列を確保すると、

int** hai = (int**)malloc(sizeof(int*)*M);
for (i=0; i<M; i++) {
hai[i] = (int*)malloc(sizeof(int)*N);
}

こうなります。あるいは、

hai = (int**)malloc(sizeof(int*) * M + sizeof(int) * M * N);
for (i=0;i<M;i++)
hai[i] = (int*)((char*)hai + sizeof(int*) * M + sizeof(int) * N * i);

これで、hai、hai[i]、hai[i][j]の関係がわかりやすくなるでしょうか?
ならないかな?


返信引用
Puppy
 Puppy
(@Puppy)
ゲスト
結合: 23年前
投稿: 71
Topic starter  

YuOさん たいちうさんさん お返事ありがとうございます。
むずかしいので、じっくり読んで勉強したいと思います。

皆さん、頭こんがらがりませんかぁ?(笑)
Puppyはプログラマーに向いてないのかなぁ。(T-T;

>hai

あ、arrayでしたぁ。恥ずかしいですぅ。(*'-'*)

未熟者ですが、これからもヨロシクお願いします。 m(..)mぺこり


返信引用
YuO
 YuO
(@YuO)
ゲスト
結合: 24年前
投稿: 252
 

>これで、hai、hai[i]、hai[i][j]の関係がわかりやすくなるでしょうか?

う~ん,int hai[5][10]とint **haiは違うのですよ……。

int hai[5][10];
をmallocで真似ると,
typedef int T1[10];
T1 * p = malloc(sizeof(T1) * 5);
です。

int array[5][10];

int **pointer;
について,

array : int [5][10]型 / &array : int (*)[5][10]型 / (int)(&array + 1) -
(int)(&array) : sizeof(int) * 50
pointer : int **型 / &pointer : int ***型 / (int)(&pointer + 1) -
(int)(&pointer) : sizeof(int **)

array[0] : int [10]型 / &array[0] : int (*)[10]型 / (int)(&array[0] + 1) -
(int)(&array[0]) : sizeof(int) * 10
pointer[0] : int *型 / &pointer[0] : int **型 / (int)(&pointer[0] + 1) -
(int)(&pointer[0]) : sizeof(int *)

array[0][0] : int型 / &array[0][0] : int *型 / (int)(&array[0][0] + 1) -
(int)(&array[0][0]) : sizeof(int)
pointer[0][0] : int型 / &pointer[0][0] : int *型 / (int)(&pointer[0][0] + 1) -
(int)(&pointer[0][0]) : sizeof(int)

です。

int array[2][3]のメモリイメージは,
+-----------------------------------------------------------------------+
|                 array                 |
+-----------------------------------+-----------------------------------+
|       array[0]       |      array[1]        |
+-----------+-----------+-----------+-----------+-----------+-----------+
|array[0][0]|array[0][1]|array[0][2]|array[1][0]|array[1][1]|array[1][2]|
+-----------+-----------+-----------+-----------+-----------+-----------+
ですが,int **pointer;に2x3の疑似配列を確保した場合,
pointer

+----------+----------+
|pointer[0]|pointer[1]|
+----------+----------+

pointer[0]

+-------------+-------------+-------------+
|pointer[0][0]|pointer[0][1]|pointer[0][2]|
+-------------+-------------+-------------+

pointer[1]

+-------------+-------------+-------------+
|pointer[1][0]|pointer[1][1]|pointer[1][2]|
+-------------+-------------+-------------+

ちなみに,int (*ptr_array)[3]に2x3の疑似配列を確保した場合
ptr_array

+-----------------------------------------------+-----------------------------------------------+
|         ptr_array[0]         |        ptr_array[1]  
        |
+---------------+---------------+---------------+---------------+---------------+---------------+
|ptr_array[0][0]|ptr_array[0][1]|ptr_array[0][2]|ptr_array[1][0]|ptr_array[1][1]|ptr_array[1][2]|
+---------------+---------------+---------------+---------------+---------------+---------------+
になります。
#図の部分は等幅フォントで見て下さい。

>皆さん、頭こんがらがりませんかぁ?(笑)

慣れました(爆)。
とりあえず,上の図で,arrayとptr_arrayが同じように扱える為に,(b)があります。

arrayとpointerはメモリの配置が全く違いますので,同じように扱うことは出来ません。
が,arrayとptr_arrayは,arrayが配列全体を示すのに対して,ptr_arrayが先頭要素への
ポインタである以外,
同じメモリ配置ですから,同じように扱うことが出来ます。

で,通常arrayをptr_arrayに変換して使います。
#これはコンパイラが勝手に行います。

一次元だと,int var[5]がint *に変換されるので,ポインタと配列がごっちゃになって
覚えているのかもしれませんね。


返信引用
YuO
 YuO
(@YuO)
ゲスト
結合: 24年前
投稿: 252
 

うりょ?ptr_arrayが崩れていますね……。
よし,ブラウザかえて再送信。

ptr_array

+-----------------------------------------------+-------------------------------
----------------+
|         ptr_array[0]         |        ptr_array[1]    
      |
+---------------+---------------+---------------+---------------+---------------
+---------------+
|ptr_array[0][0]|ptr_array[0][1]|ptr_array[0][2]|ptr_array[1][0]|ptr_array[1][1]
|ptr_array[1][2]|
+---------------+---------------+---------------+---------------+---------------
+---------------+


返信引用
YuO
 YuO
(@YuO)
ゲスト
結合: 24年前
投稿: 252
 

ダメでした……。
#Netscape 6.2.2とIE 6.00.2600.0000……
エディタにコピーして,改行を削除して見て下さい。


返信引用
たいちう
 たいちう
(@たいちう)
ゲスト
結合: 23年前
投稿: 662
 

>皆さん、頭こんがらがりませんかぁ?(笑)

こんがらがりました。まだ慣れてません。

int array[5][10]; と int **pointer;の違いについては、
一応理解していたつもりでした。
不注意で関数の引数に入れたりして、
コンパイラに文句を言われてましたから。
でも理解は曖昧だったようで、
mallocの例も不適切だったかもしれません。

YuOさんの説明((a)や(b)と書いてあるあたり)を再読して、
頭がすっきりしたと思いましたが、

> ちなみに,int (*ptr_array)[3]に2x3の疑似配列を確保した場合

について、こんがらがりました。例えば、
ptr_array[0][0] = 5
等のように使うための宣言(と確保?)のやり方が分かりません。

とりあえず、int (*ptr_array)[3]は、
「int [3]型」へのポインタでいいんですよね?


返信引用
YuO
 YuO
(@YuO)
ゲスト
結合: 24年前
投稿: 252
 

>ptr_array[0][0] = 5
>等のように使うための宣言(と確保?)のやり方が分かりません。

二次元配列を扱う場合,二種類の方法があります。

(i)
int (*ptr_array)[3]; /* 宣言 */
ptr_array = malloc((sizeof(int) * 3) * 2); /* 確保 */

これは,int array[2][3];と同等のことを,動的に確保した場合です。
最高次以外の配列の数が,コンパイル時に既知である必要があります。

(ii)
int i;
int **ptr_array; /* 宣言 */
/* 確保 */
ptr_array = malloc(sizeof(int *) * 2);
for (i = 0; i < 2; ++i) ptr_array[i] = malloc(sizeof(int) * 3);

これは,たいちうさんの書かれた方法で,
動的に2次元配列を作成するときに一般的に使われている方法です。

コンパイル時に配列の数が決定している必要はありません。
ptr_array[0]とptr_array[1]で含まれる配列の数を変えることもできます。

関数は,単一の方法で両方を受け取ることはできません。

void func (int array[][3]);
または
void func (int (*array)[3]);
であれば,(i)またはarray[2][3]を渡せます。

void func (int **array);
であれば,(ii)のみを渡せます。

関数内でのみ使うのであれば,使いやすい方を使えばいいですが,
他の関数に渡す場合には,その関数に合わせてメモリを確保する必要があります。

>とりあえず、int (*ptr_array)[3]は、
>「int [3]型」へのポインタでいいんですよね?

そうです。
*は[]より結合が弱いので,宣言に括弧が使われています。
int *ptr_array[3];とすると,3つのint *からなる配列ですよね。


返信引用
固定ページ 1 / 2

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました