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

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

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

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

Q&A

解決済

6回答

1858閲覧

多次元配列とポインタ配列の違いについて

Sukabo

総合スコア31

C

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

0グッド

0クリップ

投稿2020/03/10 23:32

編集2020/03/11 08:44

関数の定義で引数を(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ページで確認できます。

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

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

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

kazuma-s

2020/03/11 07:57

質問を編集して、コンパイルできなかったコードを追記してください。
guest

回答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] をアクセスする時、

  1. str ポインタが指す所にはポインタ配列があるはずだ。
  2. そのポインタ配列の2番目のポインタを読む。私の図(D)で言えば「int型データのアドレス」の[1] でインデックスされたポインタを読む、ということ。それが str[1] です。
  3. さらに、そのポインタが指す配列の[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
rubato6809

総合スコア1382

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

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

Sukabo

2020/03/11 08:40

ご回答ありがとうございます。 実際のコード上で関数を呼び出すときにはfunc(str)の形で使うつもりでした。 リンク先の図が分かりやすかったのでデータ構造が違うということは理解できましたが、まだわからないことがあります。 func(char *str[])にchar str[3][15]を渡そうとするとコンパイルエラーがでます。ポインタ配列でも二次元配列でもデータ構造が違うだけで要素へのアクセスの仕方などは同じ(e.g. str[1][2])なのにどうしてエラーがでるのでしょうか。
rubato6809

2020/03/11 09:57

回答に追加しました。
yumetodo

2020/03/11 10:19

関数の仮引数宣言における`char *str[]`は`char** str`と等価であることを思い出してください。
rubato6809

2020/03/11 11:02

char** str`と等価・・・そうそう、ダブルポインタですから、メモリアクセスは2回あります。順に手繰る(たぐる)ことで目的の位置をアクセスする、そういうイメージですね。 配列はそうではない。アドレス計算に次数はあれど、メモリアクセスは一回だけです。
Sukabo

2020/03/11 11:49

理解しました。 ご丁寧に回答してくださってありがとうございました。とても分かりやすかったです。
guest

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

PingHermit

総合スコア478

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

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

Sukabo

2020/03/11 11:50

ご回答ありがとうございます。
guest

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

kazuma-s

総合スコア8224

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

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

Sukabo

2020/03/11 11:50

ご回答ありがとうございます。
guest

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

cateye

総合スコア6851

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

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

cateye

2020/03/11 00:40

C言語には、“配列要素は必ず連続したメモリ領域に配置される”というのが有りますが、ポインタの配列では、その先(ポインタの指す場所)は連続する必要はありません。
Sukabo

2020/03/11 11:50

ご回答ありがとうございます。
guest

0

maisumakunさんが仰る通り別物です。
こちら(Samurai Blog)の「【C言語入門】2次元配列の使い方まとめ」に「関数の引数で使う方法について」説明があります。

投稿2020/03/11 00:00

DreamTheater

総合スコア1095

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

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

Sukabo

2020/03/11 11:50

ご回答ありがとうございます。
guest

0

多次元配列とポインタの配列は別物なのでしょうか。

はい、別物です。多次元配列は要素がそのまま順にメモリに詰まっていて、各列を指すポインタは入っていません。

投稿2020/03/10 23:47

maisumakun

総合スコア146018

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

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

Sukabo

2020/03/11 11:50

ご回答ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問