配列とポインタ
-
りん
配列とポインタ
はじめまして、りんと言います。配列とポインタについて質問させていただきます。
まず、ポインタというのはアドレスを格納している(格納する)ものですよね?
int a;
int *pa;
a = 5;
pa = &a aのアドレスをポインタpaに代入すると
*pa == a ということになりますよね。
つまり、*paというポインタはアドレスを入れてあげないことには、ほとんど意味がないものになりますよね?
また、pa=&aをすることによって*paはaのアドレスの中にある値を見ることができるようになるってことですよね? このような解釈でよろしいでしょうか?
次に、文字列とポインタなのですが、文字列は配列ですよね?
char str[/url] = "good";①
char *str = "good";② ①②は同じことらしいのですが、ここがよく理解できないです。
私のポインタについての理解の仕方が間違っているのですか??
まず、ポインタというのはアドレスを格納している(格納する)ものですよね?
int a;
int *pa;
a = 5;
pa = &a aのアドレスをポインタpaに代入すると
*pa == a ということになりますよね。
つまり、*paというポインタはアドレスを入れてあげないことには、ほとんど意味がないものになりますよね?
また、pa=&aをすることによって*paはaのアドレスの中にある値を見ることができるようになるってことですよね? このような解釈でよろしいでしょうか?
次に、文字列とポインタなのですが、文字列は配列ですよね?
char str[/url] = "good";①
char *str = "good";② ①②は同じことらしいのですが、ここがよく理解できないです。
私のポインタについての理解の仕方が間違っているのですか??
-
box
Re:配列とポインタ
> つまり、*paというポインタはアドレスを入れてあげないことには、ほとんど意味がないものになりますよね?
ほとんどではなく、全く意味がありません。
一般に、ポインタ変数を定義しただけでは、中身は不定(何が入っているかわからない)です。
中身が不定の状態で、その中身をどこかのアドレスだと思ってアクセスすると、
場合によってはプログラムが異常終了します。
> char str[/url] = "good";①
> char *str = "good";② ①②は同じことらしいのですが、ここがよく理解できないです。
①と②は同じではありません。
①は、char型の配列strの0番目~4番目の各要素に、'g', 'o', 'o', 'd', '\0' をセットしています。
②は、"good"という文字列リテラルのアドレスを、char *型のstrにセットしています。
②の後、
str = "no good";
とすることはできます。"no good"という、別の文字列リテラルのアドレスをchar *型の変数に
代入しているからです。
それに対して、①の後、
str = "no good";
とすることはできません。コンパイル時に「左辺値が必要」という内容のエラーが出ます。
ほとんどではなく、全く意味がありません。
一般に、ポインタ変数を定義しただけでは、中身は不定(何が入っているかわからない)です。
中身が不定の状態で、その中身をどこかのアドレスだと思ってアクセスすると、
場合によってはプログラムが異常終了します。
> char str[/url] = "good";①
> char *str = "good";② ①②は同じことらしいのですが、ここがよく理解できないです。
①と②は同じではありません。
①は、char型の配列strの0番目~4番目の各要素に、'g', 'o', 'o', 'd', '\0' をセットしています。
②は、"good"という文字列リテラルのアドレスを、char *型のstrにセットしています。
②の後、
str = "no good";
とすることはできます。"no good"という、別の文字列リテラルのアドレスをchar *型の変数に
代入しているからです。
それに対して、①の後、
str = "no good";
とすることはできません。コンパイル時に「左辺値が必要」という内容のエラーが出ます。
-
りん
ポインタを引数として使う
ポインタを引数として使うことについて質問させていただきます。
まず、コードなのですが、細かいところは省略しています。
int avg(int *px); ①
int main(void){
int test[2];
int i;
int ans;
printf("点数入力\n");
for(i = 0; i < 2; i++){
scanf("%d",&test);
}
int avg(test); ②
printf("%d",ans);
return 0;
}
int avg(int *px){ ③
int sum = 0;
int j;
for(j = 0; j < 2; j++){
sum = sum + *(px+1);
}
return sum/2;
}
ポインタというのはアドレスが入っているわけですよね?すると、②のtest先頭要素のアドレスがこの場合だと③のpxに入ってくるということでいいのでしょうか?
そして、*pxでtestのアドレスの中身を操作している。?その際、今回はポインタを使っているので、値はコピーではなくもとの値そのものを操作していることになりますよね?
ちょっとあまりうまく自分の言いたいことが書けず、分かりにくい文章だと思いますがよろしくおねがいします。
まず、コードなのですが、細かいところは省略しています。
int avg(int *px); ①
int main(void){
int test[2];
int i;
int ans;
printf("点数入力\n");
for(i = 0; i < 2; i++){
scanf("%d",&test);
}
int avg(test); ②
printf("%d",ans);
return 0;
}
int avg(int *px){ ③
int sum = 0;
int j;
for(j = 0; j < 2; j++){
sum = sum + *(px+1);
}
return sum/2;
}
ポインタというのはアドレスが入っているわけですよね?すると、②のtest先頭要素のアドレスがこの場合だと③のpxに入ってくるということでいいのでしょうか?
そして、*pxでtestのアドレスの中身を操作している。?その際、今回はポインタを使っているので、値はコピーではなくもとの値そのものを操作していることになりますよね?
ちょっとあまりうまく自分の言いたいことが書けず、分かりにくい文章だと思いますがよろしくおねがいします。
-
box
Re:ポインタを引数として使う
> ポインタというのはアドレスが入っているわけですよね?すると、②のtest先頭要素のアドレスがこの場合だと③のpxに入ってくるということでいいのでしょうか?
そうです。
> そして、*pxでtestのアドレスの中身を操作している。?その際、今回はポインタを使っているので、値はコピーではなくもとの値そのものを操作していることになりますよね?
pxには、呼び出し側のtestの先頭アドレスという値が入っています。
*pxとすれば、pxが指している領域の内容を取得できます。
なお、avg関数における
> sum = sum + *(px+1);
は正しくありません。+1ではなく、+jでなければなりません。
あるいは、
sum += px[j];
と書く方が簡潔かもしれません。
p をポインタ変数、n を整数とするとき、一般に
*(p+n) と p[n] は等価です。-
りん
Re:ポインタのポインタ
どなたか教えて下さい。
#include<stdio.h>
main()
{
int i=10;
int *p;
int **point;
p=&i;
point=&p; /* ポインタpのアドレスを代入 */
printf(" i = %d\n",i); ① 問題なし
printf(" *p = %d\n",*p); ② pにはiのアドレスが格納されていてそのアドレスの中にある値は10ということですよ
ね?
printf("**point = %d\n",**point); ③ pointのなかにはpのアドレスが格納され、さらにpの中にはiのアドレスがあるので、そ
その中アドレスの中にある値は10ということですよね?
printf(" &i = %p\n",&i); ④ 問題なし
printf(" p = %p\n",p); ⑤ iのアドレスが格納されている。
printf(" *point = %p\n",*point); ⑥
printf(" &p = %p\n",&p); ⑦ ここはp = &iをしていますが、この場合はp自身のアドレスを表しているのですよね?
printf(" point = %p\n",point); ⑧
printf(" &point = %p\n",&point); ⑨ここも⑦と同じように、point自身のアドレスを表しているだけですよね?
/* 結果
i = 10
*p = 10
**point = 10
&i = 0064FE00
p = 0064FE00
*point = 0064FE00
&p = 0064FDFC
point = 0064FDFC
&point = 0064FDF8
⑥・・・よく分からないです。
pにはiのアロレスが格納され、*pの値は10になりますよね?すると、pointに格納されているのはpのアドレスで値は10になると思う
のですが・・・。????
⑧・・・ここのよくわからないです。。
頭の中がグルグルしてます。。。あ~・・・
#include<stdio.h>
main()
{
int i=10;
int *p;
int **point;
p=&i;
point=&p; /* ポインタpのアドレスを代入 */
printf(" i = %d\n",i); ① 問題なし
printf(" *p = %d\n",*p); ② pにはiのアドレスが格納されていてそのアドレスの中にある値は10ということですよ
ね?
printf("**point = %d\n",**point); ③ pointのなかにはpのアドレスが格納され、さらにpの中にはiのアドレスがあるので、そ
その中アドレスの中にある値は10ということですよね?
printf(" &i = %p\n",&i); ④ 問題なし
printf(" p = %p\n",p); ⑤ iのアドレスが格納されている。
printf(" *point = %p\n",*point); ⑥
printf(" &p = %p\n",&p); ⑦ ここはp = &iをしていますが、この場合はp自身のアドレスを表しているのですよね?
printf(" point = %p\n",point); ⑧
printf(" &point = %p\n",&point); ⑨ここも⑦と同じように、point自身のアドレスを表しているだけですよね?
/* 結果
i = 10
*p = 10
**point = 10
&i = 0064FE00
p = 0064FE00
*point = 0064FE00
&p = 0064FDFC
point = 0064FDFC
&point = 0064FDF8
⑥・・・よく分からないです。
pにはiのアロレスが格納され、*pの値は10になりますよね?すると、pointに格納されているのはpのアドレスで値は10になると思う
のですが・・・。????
⑧・・・ここのよくわからないです。。
頭の中がグルグルしてます。。。あ~・・・
-
box
Re:ポインタのポインタ
くだんの結果によると、関連するアドレスとその中身は
下図のようになっています。
アドレス 中身
+------------+
0x0064FDF8 | 0x0064FDFC |
| |
+------------+
0x0064FDFC | 0x0064FE00 |
| |
+------------+
0x0064FE00 | 10 |
| |
+------------+
> printf(" *p = %d\n",*p); ② pにはiのアドレスが格納されていてそのアドレスの中にある値は10ということですよね?
そうです。
> printf("**point = %d\n",**point); ③ pointのなかにはpのアドレスが格納され、さらにpの中にはiのアドレスがあるので、そ
> その中アドレスの中にある値は10ということですよね?
そうです。
> printf(" *point = %p\n",*point); ⑥
> printf(" &p = %p\n",&p); ⑦ ここはp = &iをしていますが、この場合はp自身のアドレスを表しているのですよね?
p = &iは、していないです。
&pは、pのアドレスです。pointの値と同じです。
> printf(" point = %p\n",point); ⑧
> printf(" &point = %p\n",&point); ⑨ここも⑦と同じように、point自身のアドレスを表しているだけですよね?
そうです。
> i = 10
> *p = 10
> **point = 10
> &i = 0064FE00
> p = 0064FE00
> *point = 0064FE00
> &p = 0064FDFC
> point = 0064FDFC
> &point = 0064FDF8
>
> ⑥・・・よく分からないです。
> pにはiのアロレスが格納され、*pの値は10になりますよね?すると、pointに格納されているのはpのアドレスで値は10になると思う
> のですが・・・。????
「pointに入っているのはpのアドレス」までは、そのとおりです。
pのアドレスですから、0x0064FDFCです。
0x0064FDFC(pのアドレス)が指している先が、0x0064FE00(iのアドレス)です。
0x0064FE00(iのアドレス)が指している先が、10(iの値)です。
> ⑧・・・ここのよくわからないです。。
pointの値(0x0064FDFC)は、pのアドレスです。ここから、すぐ上の段落へと続いていきます。
つまり、
pointは&p(pのアドレス)に等しいです。
pは&i(iのアドレス)に等しいです。-
りん
Re:関数ポインタ
ちょっと微妙なことなんですけど・・・・。
このコードってちゃんと合計でる?私のだと出ないのですが。
又、for(i = 0; i < sizeof a /sizeof a[0]; i++)のところが
何をしてるのかよく分からないですね。とくにsizeofのところです。
あと、sum += *p++;ってのはsum = sum + *p;と同じことですよね?
何分、独学なもので間違って覚えてしまうと後々つらいと思うので。
このコードってちゃんと合計でる?私のだと出ないのですが。
又、for(i = 0; i < sizeof a /sizeof a[0]; i++)のところが
何をしてるのかよく分からないですね。とくにsizeofのところです。
あと、sum += *p++;ってのはsum = sum + *p;と同じことですよね?
何分、独学なもので間違って覚えてしまうと後々つらいと思うので。
#include <stdio.h>
int main(void) {
int a[/url] = {1,2,3,4,5,6,7,8,9,10};
int *p;
int i;
int sum;
p = a;
for(i = 0; i < sizeof a /sizeof a[0]; i++) {
sum += *p++;
}
printf("%d",sum);
return 0;
}-
box
Re:関数ポインタ
> このコードってちゃんと合計でる?私のだと出ないのですが。 合計値が正しくないのは、最初にゼロで初期化していないためです。 > 又、for(i = 0; i < sizeof a /sizeof a[0]; i++)のところが sizeof a は、配列a全体の大きさ(バイト数)を得ます。 sizeof a[0] は、配列aの先頭要素の大きさを得ます。 したがって、sizeof a /sizeof a[0] は、配列aの要素数を得ます。今回の例では、10です。 配列の要素数をコンピュータに数えさせる際の、定番のやり方です。 > あと、sum += *p++;ってのはsum = sum + *p;と同じことですよね? 同じではありません。 sum = sum + *p; p++; の2つの文と同じです。
-
box
Re:関数ポインタ
こんな絵が参考になるでしょうか。
a[0] a[1] a[2] a[3] a[4] ...
+----+----+----+----+----+
| 1 | 2 | 3 | 4 | 5 | ...
+----+----+----+----+----+
↑
p = a; で、pはaの先頭要素を指す。
for文によるループに入る。
sumに、現在pが指している領域の内容(==1)を加える。
pを、1要素分だけ進める。
この2行が、「sum += *p++;」の意味。
a[0] a[1] a[2] a[3] a[4] ...
+----+----+----+----+----+
| 1 | 2 | 3 | 4 | 5 | ...
+----+----+----+----+----+
↑
pは、ここを指している。
終了条件を満たしていないので、ループの中の処理を続ける。
sumに、現在pが指している領域の内容(==2)を加える。
pを、1要素分だけ進める。
a[0] a[1] a[2] a[3] a[4] ...
+----+----+----+----+----+
| 1 | 2 | 3 | 4 | 5 | ...
+----+----+----+----+----+
↑
pは、ここを指している。
終了条件を満たしていないので、ループの中の処理を続ける。
以下省略-
box
Re:関数ポインタ
> forの中身なのですが、p++の所って*p++でもできますよね?テキストではp++ですけど。 *p++と書いても、プログラムの動作は全く同じです。 つまり、今回の場合、* を付けることは無意味です。 【解説】 *p++ を実行する、ということは、次の2つの文を実行することと等価です。 *p; p++; ここで、 *p; という文は、p が指している領域の内容を取得するだけで、他の変数に代入するなどの 処理を行なっていません。 例えば、int 型の変数 a に任意の値が入っているとして、 a; という文に実質的な意味がないことはおわかりかもしれません。それと全く同じです。
-
keichan
Re:関数ポインタ
ポインタ変数と配列は別物ですので別々にして理解を深めた方が良いでしょう。
char* a = "abc";
これを図にしてみると
char* 型変数である a がメモリの p番地 に生成されたとすると
+----------------------------+
p番地 | "abc"を指すアドレス(n番地) |
+----------------------------+
そして n番地は以下の様になっています。
+----------+
n番地 | 'a' |
+----------+
n+1番地 | 'b' |
+----------+
n+2番地 | 'c' |
+----------+
n+3番地 | '\0' |
+----------+
これを単に配列にしているだけですので、
char* b[3] = {"abc", "de", "f"};
char* 型配列変数である b がメモリの p番地 に生成されたとすると
+-----------------------------+
p番地 | "abc"を指すアドレス(n1番地) |
+-----------------------------+
p+1番地 | "de"を指すアドレス(n2番地) |
+-----------------------------+
p+2番地 | "f"を指すアドレス(n3番地) |
+-----------------------------+
+----------+ +----------+ +----------+
n1番地 | 'a' | n2番地 | 'd' | n3番地 | 'f' |
+----------+ +----------+ +----------+
n1+1番地 | 'b' | n2+1番地 | 'e' | n3+1番地 | '\0' |
+----------+ +----------+ +----------+
n1+2番地 | 'c' | n2+2番地 | '\0' |
+----------+ +----------+
n1+3番地 | '\0' |
+----------+
単純にこれだけの事です。-
りん
Re:関数ポインタ
#include <stdio.h>
#include <string.h>
int main(void) {
char str1[/url] = "abcdefg";
char str2[80], *p1, *p2;
p1 = str1 + strlen(str1)-1;
p2 = str2;
while(p1 >= str1){
*p2++ = *p1--;
*p2 ='\0';
}
printf("%s,%s",str1,str2);
return 0;
}
p1 = str1 + strlen(str1)-1;p2 = str2;
この二つはそれぞれアドレスを入れてるのですよね?配列の宣言がしてありますから。
whileからが分からないです。
-
box
Re:関数ポインタ
> p1 = str1 + strlen(str1)-1; > p2 = str2; > この二つはそれぞれアドレスを入れてるのですよね?配列の宣言がしてありますから。 p1は、str1の先頭のアドレスから、str1の文字数分だけ進み、1つ戻っています。 str1の文字数は7ですので、結局、p1は、str1の先頭から6文字分進んでいます。 つまり、p1は、str1の最後の文字'g'を指しています。 一方、p2は、str2の先頭を指しています。 > whileからが分からないです。 7文字だと説明が少し長くなりますので、str1[/url]に"abc"が入っていることにして説明します。 【whileの前の準備段階】 p1は、上で書いたとおり、str1の最後の文字'c'を指している。 p2は、str2の先頭文字を指している。この時点では、内容は不定(str2を初期化していないため)。 【whileの中身】 p1は'c'を指しているので、str1(つまり、先頭文字'a'のアドレス)以上の値を持つ。 ループを継続する条件を満たしているので、ループの中に入る。 p2が指している場所(str2の先頭、0文字目と呼ぶことにする)に、 p1が指している場所の内容'c'を代入する。 代入後、p2のアドレスを1つ進めて、p1のアドレスを1つ戻す。 さらに、そのときp2が指している場所に、文字列終端用の'\0'を代入する。 この時点で、p2はstr2の1文字目('\0')を指し、p1は'b'を指している。 まだ、ループの継続条件を満たしている。 p2の1文字目に、p1が指している'b'を代入する。 代入後、p2のアドレスを1つ進めて、p1のアドレスを1つ戻す。 さらに、そのときp2が指している場所に、文字列終端用の'\0'を代入する。 この時点で、p2はstr2の2文字目('\0')を指し、p1は'a'を指している。 まだ、ループの継続条件を満たしている。 p2の2文字目に、p1が指している'a'を代入する。 代入後、p2のアドレスを1つ進めて、p1のアドレスを1つ戻す。 さらに、そのときp2が指している場所に、文字列終端用の'\0'を代入する。 この時点で、p2はstr2の3文字目('\0')を指し、p1は'a'の直前(内容は不定)を指している。 ここで、ループの継続条件を満たさなくなる。 【whileを抜けた後】 str1は"abc"の先頭文字'a'を指しているので、書式文字列"%s"を使うと、"abc"と出力する。 一方、while文によるループの中でp2が指している場所に文字を代入しているということは、 準備段階においてp2をstr2の先頭文字のアドレスで初期化していたことが効いてきて、 結局のところstr2の要素の中に文字を代入したことになる。書式文字列"%s"を使うと、"cba"と出力する。 【結論】 str2には、str1の文字順を逆転させた内容が入る。
-
box
Re:関数ポインタ
図を描くと、こんな感じです。
【準備段階】
+------+------+------+------+
str1 | 'a' | 'b' | 'c' | '\0' |
+------+------+------+------+
↑
p1は、ここを指している
+------+------+------+------+
str2 | 不定 | 不定 | 不定 | 不定 | ......
+------+------+------+------+
↑
p2は、ここを指している
【ループ(1回目終了時)】
+------+------+------+------+
str1 | 'a' | 'b' | 'c' | '\0' |
+------+------+------+------+
↑
p1は、ここを指している
+------+------+------+------+
str2 | 'c' | '\0' | 不定 | 不定 | ......
+------+------+------+------+
↑
p2は、ここを指している
【ループ(2回目終了時)】
+------+------+------+------+
str1 | 'a' | 'b' | 'c' | '\0' |
+------+------+------+------+
↑
p1は、ここを指している
+------+------+------+------+
str2 | 'c' | 'b' | '\0' | 不定 | ......
+------+------+------+------+
↑
p2は、ここを指している
【ループ(3回目終了時)】
+------+------+------+------+
str1 | 'a' | 'b' | 'c' | '\0' |
+------+------+------+------+
↑
p1は、ここを指している(内容は不定)
+------+------+------+------+
str2 | 'c' | 'b' | 'a' | '\0' | ......
+------+------+------+------+
↑
p2は、ここを指している
この時点でループの継続条件を満たさなくなる。
"abc"と"cba"を出力する。