c言語でポインタのポインタを学習していてわからないサンプルプログラムがあり、それについて質問します。
以下、サンプルプログラムです。
C
1#include <stdio.h> 2 3int main(void){ 4 char *s[3] = { "abc" , "pqr" , "xyz" }; 5 char **pps=NULL; 6 int i; 7 for(i = 0;i < 3;i++){ 8 printf("%s\n",s[i]); 9 } 10 pps = s; 11 for(i = 0;i < 3;i++){ 12 printf("%s\n",*pps); 13 pps++; 14 } 15 return 0; 16}
上記のプログラムで4点質問です。
1点目は、配列sは定義していないのに使えているのか。
2点目は、定義していない配列sのアドレスをppsのアドレスを取得できているのか。
3点目は、**ppsと定義したのに、使用するときに*ppsとなっているのか。
4点目は、*s[3]ではなく、s[3]と宣言すると、s[0]=a,s[1]=b,s[2]=cの様に代入されてしまうのはなぜなのか。
4点目は追加させていただきました。1〜3点目に回答をいただき考えていたのですが、4点目がわからなくなりました。
回答お願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/02/10 15:13 編集
回答6件
0
ベストアンサー
1点目、2点目
char *s[3] = { "abc" , "pqr" , "xyz" };
は、char*
型で要素数3の配列を宣言/定義し、"abc"のアドレス、"pqr"のアドレス、"xyz"のアドレスを与えて初期化しています。つまり、配列sは定義されています。配列sは定義されて実体を持っているのでアドレスを得ることができます。
3点目
**pps
と定義したのではありません。char**
型の変数ppsを宣言、定義しています。
*pps
は、変数ppsに参照演算子*を作用させているものです。なんの問題もないです。
「なぜなのか」それはそういうことに決まっているから、です。
char* s[3];はchar*型の変数3つの配列です。
char s[4];はchar型の変数4つの配列です("abc"は終端文字'\0'が付加されるのでchar4つのスペースを占めます)。
投稿2021/02/07 10:06
編集2021/02/11 01:25総合スコア7703
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/02/07 10:30
2021/02/07 11:00
2021/02/07 11:03
2021/02/07 11:06
2021/02/07 11:20
2021/02/07 11:41
2021/02/10 15:16
2021/02/11 06:44
2021/02/11 11:46
2021/02/13 11:12
0
char s[3] = { "abc" , "pqr" , "xyz" };
ではなく
char s[3] = { "abc" , "pqr" , "xyz" };
とすれば、
「char* を 3つ持った 配列 s」と読めるんだが。
投稿2021/02/07 11:47
総合スコア16612
0
4点目は、*s[3]ではなく、s[3]と宣言すると、s[0]=a,s[1]=b,s[2]=cの様に代入されてしまうのはなぜなのか。
char s[3] = "abc";
と宣言すると、
s[0] の値が 'a'、s[1] の値が 'b'、s[2] の値が 'c' になるのはなぜか、
という質問ですね。
int a[3] = { 3, 5, 7 };
と宣言すると、
a[0] の値が 3、a[1] の値が 5、a[2] の値が 7 になるのは分かりますか?
a[0] の型は int です。a[1]、a[2] の型も int です。
char s[3] = { 'a', 'b', 'c' };
と宣言すると、
s[0] の値が 'a'、s[1] の値が 'b'、s[2] の値が 'c' になるのは分かりますか?
s[0] の型は char です。s[1]、s[2] の型も char です。
char s[3] = "abc";
と char s[3] = { 'a', 'b', 'c' };
は
C の宣言としては同じ意味です。(C++ では異なります)
`char *s[3] = { "abc", "pqr", "xyz" }; と宣言すると、
s[0] の値は、文字列 "abc" のアドレスです。
s[1] の値は、文字列 "pqr" のアドレスです。
s[2] の値は、文字列 "xyz" のアドレスです。
s[0] の型は char * です。s[1]、s[2] の型も char * です。
型がポインタだから、その型の変数の値はアドレスです。
投稿2021/02/10 12:32
総合スコア8224
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
C言語の変数は、マッチ箱だと思ってください。
int型の変数はマッチ箱の大きさがsizeof(int)、char型の変数はマッチ箱の大きさがsizeof(char)というようになっています。
配列はマッチ箱が並んだものです。
int x[4];
ならば、sizeof(int)の大きさのマッチ箱が4個並んでいます。
intとかcharとかdoubleのようなものは、整数、文字、倍精度浮動小数がそのマッチ箱の中に入ります。
このマッチ箱は、広い部屋のどこかに置いてあります。その置いてある場所のことをアドレスといいます。
アドレスが必要になるときがあるので、アドレスの書いた紙をマッチ箱にしまっておくということもできます。
アドレスの書いた紙の入ったマッチ箱のことをポインタといいます。
int型のマッチ箱の場所の書いた紙を入れておくマッチ箱をint型へのポインタといいます。
ポインタというマッチ箱の大きさは、64ビットOSでは8バイト、32ビットOSでは4バイトなのが普通です。
ポインタの名前がpだとすると、pにはいっているアドレスの先のマッチ箱を*pと書きます。
char s[3]はマッチ箱が3個並んでいて、そのそれぞれの中身はchar型のマッチ箱のアドレスです。
分かりにくいですね。
s[0]、s[1]、s[2]という三つのマッチ箱が並んだものの全体にsという名前を付けました。
s[0]に入っているのはchar型のアドレスの入った紙、つまりポインタです。その先には'a'という文字が入ったマッチ箱があります。そのあとには、'b'、'c'、'\0'という文字が入った箱があります。こういうのがs[1]、s[2]にもありますよ、と言っています。
char **pps=NULL;
は、ppsというマッチ箱を準備しておいてください、という意味です。
pps = s;
とすると、ppsには、sというマッチ箱の最初のものであるs[0]のアドレスを書いた紙が入ります。この紙に書かれたアドレスの先はs[0]なのです、*ppsはs[0]になります。
*ppsは文字へのポインタなので、printfの%sに対応する引数にいれてうまく動きます。
というようなことをまず理解していてください。
投稿2021/02/07 13:27
総合スコア24670
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ポインタとはという概念をこういうところで説明するのは大変に難しいので、
https://www.amazon.co.jp/dp/477419381X/
などを読んだ方が学習効率がよいと思います。
投稿2021/02/07 12:38
総合スコア2022
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
個別の質問に答えるというよりも、複数ある「*」を混同してとらえていることがコードの理解の妨げになっていると思われるのでそこを指摘したいと思います。
変数宣言に付く「」は変数の型がポインタであることを示す宣言の一部です。
変数を使うときに付く「」はポインタ変数からその実体を取り出す演算子です。
(ついでにいうと配列の宣言に付く[]と、配列変数の個別要素アクセス時に付く配列アクセス演算子[]も区別して理解する必要があります。)
以下は変数宣言です。上記は「*s」とか「*s[3]」という変数の宣言ととらえるのではなく、sという変数の宣言でその型がポインタ型変数の要素数3の配列であることを宣言しています。
c
1 char *s[3] = { "abc" , "pqr" , "xyz" };
こちらのほうは変数sから必要な情報を取り出しています。
s[i]は、sの配列のi番目のアクセスを意味します。
c
1 printf("%s\n",s[i]);
以下は変数宣言です。**ppsという変数の宣言ととらえるのではなく、ppsという変数の宣言でその型がcharポインタ型を指すポインタ型の変数であることを宣言しています。
c
1 char **pps=NULL;
こちらは変数ppsのアクセスです。ppsに「*」を付けてポインタ変数からその実体を取り出しています。その型はcharポインタ型です。
c
1 printf("%s\n",*pps);
こちらも変数ppsのアクセスです。charポインタのポインタ型にデータサイズ1要素分(今どきのPCだと8バイト)ポインタを後ろにずらすという操作です。
c
1 pps++;
#※コメントを受けて追記
なぜ、ポインタの配列のsがアドレスを持っているのかわかりません。
int t[3]
s = &t
こんな感じでアドレスを入力しなくても大丈夫なのでしょうか?
配列にアドレスを与えることは出来ません。
変数を配列として宣言することとと、ポインタ変数を宣言することでは確保されるデータ構造が違います。
配列として宣言する場合、なにもしなくてもその配列変数を格納するのに必要な領域が確保されアドレスが確定するので、アドレスを与える必要が無く、逆にアドレスを変更したいと思っても変更することは出来ません。
ポインタ変数ならば、初期化をしないとアドレスが不定なのであらかじめ有効な領域のアドレスを与えてあげる必要があります。
Cの場合、ポインタと配列は同じような演算が行えることもあり両者を混同してしまいがちかもしれませんが、明確に違うデータ構造であることを理解して区別することが必要です。
投稿2021/02/07 10:45
編集2021/02/07 13:00総合スコア1248
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/02/07 11:02
2021/02/07 11:05
2021/02/07 11:07
2021/02/07 12:57
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。