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ぺこり
チェック忘れました。m(..)m
hai
これは、2次元配列全体を指すポインタ。
hai[3]
これは、3行目((3,0)-(3,10))の1次元配列へのポインタ。
&hai[3][5]
これは、位置(3,5)のintへのポインタ。
でも実は、全部 int* です。
何次元の配列であっても、そのポインタは
単に先頭要素へのポインタです。
>でも実は、全部 int* です。
違います。
hai自体はint [5][10]型で,そのままint [10]へのポインタにはなりますが,
int *にはなりません。
つまり,
int (*p)[10] = hai; /* int [5][10]→int (*)[10] */
は正しいですが,
int *p = hai;
は正しくないです。
>>でも実は、全部 int* です。
>
>違います。
すみません。間違いでした。
1次元のサイズがわからなくなってしまいますね。
dairygoodsさん YuOさん ありがとうございます。
>int *p = hai;
>は正しくないです。
私はいまだに理解できません。
できたら、例をあげて教えてもらえませんか?m(..)mペコリ
hai は &hai[0][0] と同じであり、 int型のポインタだと思っています。
間違えでしょうか?
>1次元のサイズがわからなくなってしまいますね。
この意味もわかりませんですぅ。(T-T;
>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 *型
です。
ちなみに、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]の関係がわかりやすくなるでしょうか?
ならないかな?
YuOさん たいちうさんさん お返事ありがとうございます。
むずかしいので、じっくり読んで勉強したいと思います。
皆さん、頭こんがらがりませんかぁ?(笑)
Puppyはプログラマーに向いてないのかなぁ。(T-T;
>hai
あ、arrayでしたぁ。恥ずかしいですぅ。(*'-'*)
未熟者ですが、これからもヨロシクお願いします。 m(..)mぺこり
>これで、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 *に変換されるので,ポインタと配列がごっちゃになって
覚えているのかもしれませんね。
うりょ?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]|
+---------------+---------------+---------------+---------------+---------------
+---------------+
ダメでした……。
#Netscape 6.2.2とIE 6.00.2600.0000……
エディタにコピーして,改行を削除して見て下さい。
>皆さん、頭こんがらがりませんかぁ?(笑)
こんがらがりました。まだ慣れてません。
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]型」へのポインタでいいんですよね?
>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 *からなる配列ですよね。