関数の定義で引数を(char *str[])
として、実際に渡す変数をchar str[3][15]
としたところコンパイルできなかったのですが、多次元配列とポインタの配列は別物なのでしょうか。
C
1void func(char *[]); 2 3void main(){ 4 char str[3][15]; 5 func(str); 6 return; 7} 8 9void func(char *str[]){ 10 return; 11}
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
ベストアンサー
多次元配列とポインタの配列は別物なのでしょうか
別物です。その違いは、字面(言葉・コード)ではなく、メモリ上の姿を図で具体的に理解することをおすすめします。
同じような問いに答えたことがあります。C言語の二次元配列で二重ポインタを使ってもうまくいきませんへの私の回答の、途中(”メモリ上の配置を図に描いて理解するについて、拙い図を描きました”)からご覧ください。そこに4つの図を描きました。
(A)二次元配列のイメージ
(B)実際のメモリ上の配置
(C)配置の基本
(D)配列をポイントする場合
(A)二次元配列のイメージと、(D)配列をポイントする場合では、メモリ上の姿が異なります。まず、この違いをご理解ください。
この2つは異なるデータ構造だということ。にも関わらず、ある一要素にアクセスするコードは、字面上は同じ書き方ができます。それゆえ、「ポインタの配列と二次元配列は同じものだ」とか「ダブルポインタは二次元配列だ」といった、危険な誤解がはびこってしまうのです。
その上で、気になることがあります。
関数の定義で引数を(char *str[])として
とは、たとえば関数名を func とすれば、こういうことでしょう。
C
1void func(char *str[]) 2{ 3 // 関数本体のコードが始まる
仮引数「str」は、ポインタの配列を指すポインタ変数であり、私が描いた図(D)では dptr 変数に相当します。
さて、問題は次です。
実際に渡す変数をchar str[3][15]とした
実際のコードが示されてないので、渡した側、即ち関数を呼び出す側のコードは、少なくとも二通りに解釈できる文章です。
C
1int main(void) 2{ 3 char str[?][?]; // 要素数いくつ? 4 5 func(str[3][15]); // 可能性1 6
私には str 配列の要素数がわかりませんが、「実際に渡す変数をchar str[3][15]とした」というなら、まずは、こう推測できます。これだと二次元配列の中の[3][15]の位置の、一箇所の値を func() に渡した、という意味になります。
もうひとつは次のようなものです。
C
1int main(void) 2{ 3 char str[3][15]; // 3×15の要素を持つ二次元配列 4 5 func(str); // char str[3][15]配列を渡した? 6
これは str 配列の先頭アドレスをfunc() に渡したという意味になります。
さらには、この2つ以外の可能性も考えられます。そして、どう書いたのか、それによっては、もっと重篤な誤解をしている可能性があります。さあて、どうなんでしょうか?
追記です。
前者の func(str[3][15]); ではなく、後者の func(str); という呼出しだったというので重篤ではなかったとホッとしています。
ポインタ配列でも二次元配列でもデータ構造が違うだけで要素へのアクセスの仕方などは同じ(e.g. str[1][2])なのにどうしてエラーがでるのでしょうか。
誤解してます。同じなのは str[1][2]
というコードの字面であって、データ構造が違うのだから要素へのアクセスの仕方は違うのです。
void func(char *str[]) と引数を受け取った関数が str[1][2] をアクセスする時、
- str ポインタが指す所にはポインタ配列があるはずだ。
- そのポインタ配列の2番目のポインタを読む。私の図(D)で言えば「int型データのアドレス」の[1] でインデックスされたポインタを読む、ということ。それが str[1] です。
- さらに、そのポインタが指す配列の[2]ですから3番目の要素が str[1][2] です。これは (str[1])[2] という意味です。
つまり、ポインタ配列を経由して目的の要素をアクセスするのが char *str[] における str[1][2] です。
ところが char str[3][15]; という配列にはポインタ配列はありません。あるのは実データです。
関数を func(str); と呼び出す時、関数 func に渡されるのは配列 str の先頭アドレスです。私の図(B)に書いた「先頭アドレス」が渡されるという事。繰り返しますが、そのメモリにあるのはポインタ配列ではありません。よって関数は配列の中の要素をアクセスできません(無理にアクセスすれば高い確率で不正なメモリアクセスを起こし異常終了する。場合によっては実際に起こりうる)。
一方、配列の先頭アドレスを受け取った場合、str[1][2] をアクセスするのは、先頭アドレス+(15 * 1 + 2)と計算するアドレスのメモリをアクセスするということです。この計算にポインタ配列は不要です。
配列 char str[3][15]; を関数に渡したいなら、関数側は
C
1void func(char str[][15]) 2{
と受け取ってください。これで良いことは太字で強調したアドレスの計算式から読み取っていただきたい。
投稿2020/03/11 05:54
編集2020/03/11 09:59総合スコア1382
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/03/11 08:40
2020/03/11 09:57
2020/03/11 10:19
2020/03/11 11:02
2020/03/11 11:49
0
void func(char str[][15]){ return; } void func(char (*str)[15]){ return; } コード
どちらかで。
15 は省略不可です。
void func(char str[3][15]){
と書いても、3 は無視されます。
投稿2020/03/11 11:33
総合スコア478
0
次の2つを比べてみてください。
C
1#include <stdio.h> 2 3void func(char (*)[15]); 4 5int main() 6{ 7 char str[3][15] = { "abcdefghijklmn", "opqrstuvw", "xyz" }; 8 func(str); 9} 10 11void func(char (*str)[15]) 12{ 13 for (int i = 0; i < 3; i++) puts(str[i]); 14}
C
1#include <stdio.h> 2 3void func(char *[]); 4 5int main() 6{ 7 char str[3][15] = { "abcdefghijklmn", "opqrstuvw", "xyz" }; 8 char *ptr[3] = { str[0], str[1], str[2] }; 9 func(ptr); 10} 11 12void func(char *str[]) 13{ 14 for (int i = 0; i < 3; i++) puts(str[i]); 15}
投稿2020/03/11 11:24
総合スコア8224
0
ポインタの配列について・・・
text
1usr ~/Project/test % ./a.out 2This is a pen. 3hello, world 4foo bar 5usr ~/Project/test % cat t1.c 6#include <stdio.h> 7 8static void func(const char *ptr[]) 9{ 10 for(size_t i = 0; i < 3; i++) { 11 printf("%s\n", ptr[i]); 12 } 13} 14 15static char s3[] = "foo bar"; 16 17int main(void) 18{ 19 // 20 char s1[] = "This is a pen."; 21 char s2[] = "hello, world"; 22 23 const char *sp[] = {s1, s2, s3}; 24 25 func(sp); 26 // 27 return 0; 28} 29
↑のように、多次元配列とは違います。main()のargv[]と同じですねd^^
投稿2020/03/11 00:32
総合スコア6851
0
maisumakunさんが仰る通り別物です。
こちら(Samurai Blog)の「【C言語入門】2次元配列の使い方まとめ」に「関数の引数で使う方法について」説明があります。
投稿2020/03/11 00:00
総合スコア1095
0
多次元配列とポインタの配列は別物なのでしょうか。
はい、別物です。多次元配列は要素がそのまま順にメモリに詰まっていて、各列を指すポインタは入っていません。
投稿2020/03/10 23:47
総合スコア146018
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。