ここから本文です

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

all********さん

2013/6/1502:00:32

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

C言語を勉強しています。
ポインタの配列と配列のポインタについて理解を深めるために以下の3つのファイルを実行しましたが、ファイル③だけコンパイル時に警告が出て、実行はできたものの他のファイルと結果が異なりました。なぜ異なったのかを「ポインタの配列」と「配列のポインタ」の違いを含めて説明していただきたいです。また警告の意味も説明していただければと思います。よろしくお願いします。

-------------------------------------ここからがファイル①
#include <stdio.h>
int main(void){
char *parr[3] = {"ABC","DEF","GHI"};
printf("%c\n",parr[1][1]);
return 0;
}

/*
問題なくコンパイルでき、
実行結果は
E
*/
-------------------------------------ここまでがファイル①

-------------------------------------ここからがファイル②
#include <stdio.h>
int main(void)
{
char arr[3][3] = {"ABC","DEF","GHI"};
char (*arrp)[3] = arr;
printf("%c\n",arrp[1][1]);
return 0;
}

/*
問題なくコンパイルでき、
実行結果は
E
*/
-------------------------------------ここまでがファイル②

-------------------------------------ここからがファイル③
#include <stdio.h>
int main(void)
{
char (*arrp)[3] = {"ABC","DEF","GHI"};
printf("%c\n",arrp[1][1]);
return 0;
}

/*
コンパイル時に以下の様な警告
parrayt3_3.c: In function ‘main’:
parrayt3_3.c:4: warning: initialization from incompatible pointer type
parrayt3_3.c:4: warning: excess elements in scalar initializer
parrayt3_3.c:4: warning: (near initialization for ‘arrp’)
parrayt3_3.c:4: warning: excess elements in scalar initializer
parrayt3_3.c:4: warning: (near initialization for ‘arrp’)

実行結果は
%
*/
-------------------------------------ここまでがファイル③

閲覧数:
7,989
回答数:
4
お礼:
100枚

違反報告

ベストアンサーに選ばれた回答

xrq********さん

編集あり2013/6/1604:45:17

ファイル①について

parr は、「『文字列を指すポインタ』の配列(要素数 3)」です。
(parr が配列であることに注意してください)
parr[1] は配列の1番目の要素、つまり "DEF" を指すポインタです。

parr[1][1] は "DEF" の1番目の文字を表してるので、実行結果が E となっています。


ファイル②について

arr は、「『配列(要素数 3)』の配列(要素数 3)」です。
そして arrp は、「『配列(要素数 3)』を指すポインタ」です。
(arrp がポインタであることに注意してください)
arrp は arr を指しているので、arrp[1] とは arr の1番目の要素、つまり配列 {'D', 'E', 'F'} です。

arrp[1][1]は {'D', 'E', 'F'} の1番目の要素を表しているので、やはり実行結果が E となっています。

話が少しずれますが、実はこのコードは間違っていて、3文字の文字列を格納するためには4個分の領域を確保しないといけません。
(文字列の最後には '\0' が必要なため)
正しくは、
char arr[3][4] = {"ABC","DEF","GHI"};
となります。


ファイル③について

さて問題のファイル③ですが、
ファイル②でも述べた通り、char (*arrp)[3] というのは「『配列(要素数 3)』を指すポインタ」です。
ポインタである arrp は、配列を指さなければいけないのですが、指すべき配列がどこにもありません。
配列を指すポインタを定義して初期化するときには、「配列そのもの」を別に用意しておく必要があるのです。
(ファイル②のときは arr という配列を定義し、そこを指していました)

コンパイラはこのコードを、arrp という1個のポインタに "ABC", "DEF", "GHI" という 3個の値を代入しようとしていると解釈します。
そのため、コンパイラは excess elements in scalar initializer(一つの変数を初期化する要素が多すぎる)というメッセージを出しています。

コンパイラは、とりあえず最初の "ABC" だけを代入しようとします。
ところが、arrp は配列を指すポインタなのに、"ABC" は char 型を指すポインタです。
そのため、initialization from incompatible pointer type(互換性の無いポインタ型による初期化)というメッセージを出しています。

コンパイラは無理やり代入して、最終的に arrp[0] は "ABC" を指すポインタとなります。
しかし、arrp[1] 以降は何の意味も無い値になってしまいます。

質問した人からのコメント

2013/6/19 21:14:04

笑う 大変わかり易く説明していただきありがとうございます!
スムーズに理解することができました。

ベストアンサー以外の回答

1〜3件/3件中

並び替え:回答日時の
新しい順
|古い順

プロフィール画像

カテゴリマスター

hid********さん

2013/6/1522:00:18

ポインタの配列 と

配列のポインタ とは、

言葉を補って、言い換えれば、

複数のポインター変数が、並んでいる配列 と

1つの配列のアドレス(位置)を指し示すポインター

という意味です。

と考えて、ソース・コードを分析してみて下さい。


ポインターとだけ言うと、ポインターという、その言葉自体が、
ポインター変数に格納されるアドレス値を差すのか、
ポインター変数という器を差しているのか、曖昧になるので、
論理性が、求められる文脈での使い方には、
注意が、必要になる場合が、多々あります。

pyo********さん

2013/6/1511:15:05

>ですから、②も実は間違えで、
違うよ。
2次元配列は配列の配列。
配列名は一部の例外を除いて
その配列の先頭要素へのポインタに読み替えられる。
だから、
arrはその先頭要素である配列arr[0](char[3]型)へのポインタ(char(*)[3]型)
に読みかえられるので、②は正しい。

アバター

ID非公開さん

2013/6/1510:20:56

かなり、ややこしいですね。
結局、
char *parr[3];
と書くと、文字列のポインタが、3個あります。
と言う意味になるのですが、
char (*arrp)[3];
と書くと、3文字へのポインタが、1個あります。
と、いう意味になるのです。
ですから、②も実は間違えで、たまたま、メモリー上の一致する場所に正しいものが
落ちていたので、拾ってしまっただけで、実は、間違えです。
③は、1個なので、初期値に3個入れられないので、
"DEF","GHI" が、入らなかったのです。
そこで、たまたま、メモリー上に落ちていた、printf の % を拾ってしまったのです。

あわせて知りたい

この質問につけられたタグ

みんなで作る知恵袋 悩みや疑問、なんでも気軽にきいちゃおう!

Q&Aをキーワードで検索:

Yahoo! JAPANは、回答に記載された内容の信ぴょう性、正確性を保証しておりません。
お客様自身の責任と判断で、ご利用ください。
本文はここまでです このページの先頭へ

「追加する」ボタンを押してください。

閉じる

※知恵コレクションに追加された質問は選択されたID/ニックネームのMy知恵袋で確認できます。

不適切な投稿でないことを報告しました。

閉じる