プログラミング言語/C/ポインタ
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
*ポインタ [#yc48fc3c]
C/C++のポインタの概念を説明します。
#contents
**アドレス [#ga610581]
int i = 10;
というコードがあるとします。
これがコンパイルされたとき、EXEのなかではどう表現されてい...
変数の値は、実際にはメモリのどこかに保存されています。
そして「どこに保存されているか」というメモリの住所は、番...
上記のコードは、たとえば
45450721番目のメモリに 10 を代入せよ
のようにコンパイルされます。
何番目のメモリを使うかは、コンパイラが勝手に決めてくれま...
C言語を使えば、このようにどの変数にどこのメモリを割り当て...
自動的に割り振ってくれるわけですね。
この、"何番目のメモリ" という番号のことを「アドレス」と呼...
**ポインタ [#b9e4e979]
さて、上記のコードにおいて、変数 i が何番目のメモリに格納...
用語を用いて言い換えれば、変数 i のアドレスが知りたいわけ...
そういう時は &i と書けばおkです。上記の例では &i は 4545...
C言語には、アドレスを格納するための特別な変数型があります...
ポインタの宣言は
int *p;
のように書きます。たとえば
int i = 10;
int *p = &i;
などとします。この p には、たとえば 45450721 などが格納さ...
この例では、p のことを「i へのポインタ」といいます。
「i へのポインタ」とは、"i のアドレスが格納されているポイ...
では 45450721番目のメモリに入っている値(すなわち i に格...
printf("%d", *p);
と書きます。
*p のことを、「p が指す値」と呼びます。
**ポインタの宣言 [#f183fa34]
int i;
という宣言文は「i は int型である」と宣言しています。これ...
int *p;
という宣言文は「*p は int型である」、もしくは「pとは、*p ...
ちょっと複雑な例を考えましょう。intへのポインタを10個並べ...
ap[index] はポインタですね。その指す値(int型)は、 *(ap[...
int *(ap[10]);
と書けばよいのです。
次に、intを10個並べた配列へのポインタ pa が欲しいときはど...
*pa は配列ですから、(*pa)[index] は int 型ですね。で...
int (*pa)[10];
と宣言すればよいです。
*関数の引数について [#r19e389b]
**値渡し [#j2c6d7ac]
#include <stdio.h>
void func(int a)
{
a = 100;
}
int main()
{
int b = 1;
func(b);
printf("%d", b);
}
というプログラムを実行すると、"1" と表示されます。決して ...
具体的に追っていきましょう。&a は 45450721、&b は 6741 だ...
main関数のなかでは、まず
int b = 1;
で 6741番目のメモリが 1 になります。次に
func(b);
では、45450721番目のメモリに 1 がコピーされて
func の先頭にジャンプします。
func のなかで、45450721番目のメモリに 100 が代入されます...
printf("%d", b);
に戻ってきたとき、6741番目のメモリには何が入っているでし...
1 ですね。
このように、関数の引数には、呼び出し元の変数の値がコピー...
関数内部ではその''コピーされたものしかいじれません''。
**関数の引数にポインタを用いると [#m8becd41]
#include <stdio.h>
void func(int *p)
{
*p = 100;
}
int main()
{
int b = 1;
func(&b);
printf("%d", b);
}
を最初から追っていきましょう。&p は 45450721、&b は 6741 ...
まず
int b = 1;
で 6741番目のメモリが 1 になります。つぎに
func(&b);
で、45450721番目のメモリに 6471 がコピーされて、funcの先...
funcのなかで、45450721番目のメモリに格納されている番号(6...
メモリに 100 が代入されます。そうして
printf("%d", b);
に戻ってきたとき、6741番目のメモリには 100 が入っています。
ですから、画面には "100" と表示されることになります。
このように、ポインタを関数の引数に用いると、
関数内で呼び出し元の変数をいじることができるようになりま...
*配列 [#k8dd863f]
**配列 [#afd152dd]
char ac[100];
と配列を宣言したとします。配列は、''メモリ上に連続して並...
&(ac[0]) が 45450721 だとすると
&(ac[0]) = 45450721
&(ac[1]) = 45450722
&(ac[2]) = 45450723
&(ac[3]) = 45450724
.
.
.
とならびます。そうして、ac を単独で用いると、&(ac[0])...
printf("%p\n", ac); // %d が int を表示するように...
printf("%p\n", &(ac[0]));
このコードを実行すると、同じ値が表示されます。
このように、配列の識別子は、その配列の先頭のアドレスに変...
変換されるだけで、配列がアドレスそのもの、というわけでは...
また、先頭のアドレスが格納されているポインタから、
配列へ逆変換することは''できません''。
**配列とポインタ [#ie104a63]
さて、上記の ac を使って
*(ac + 3);
は何を表すでしょう? ac + 3 は 45450724 ですから、
それに * をつけると ac[3] にアクセスできますね。
一般に
ac[index] ⇔ *(ac + index)
の関係が成り立ちます。
ac は &(ac[0]) に変換されるといいましたが、ac[0] は c...
&(ac[0]) は char へのポインタです:
char ac[100];
char *pc = ac;
と書くことができます。配列をポインタに代入しているように...
実際には''配列の先頭のアドレスをポインタに代入しているだ...
このポインタを使って、ac の要素にアクセスすることを考えま...
たとえば ac[3] にアクセスしたければ
*(pc + 3)
と書けばよいですね。この節を先頭から読み直すと、これが
pc[3]
と書ける気がしませんか? 書けます。ですが
pc[3] は、''単なる *(pc + 3) の言い換えに過ぎません''。
ac[3] は、「配列 ac の 3 番目の要素」を指しますが、
pc[3] は、単に*(pc + 3) を言い換えただけです。
この違いは今は気になりませんが、2次元配列を考えたときに...
何はともあれ、ポインタをつかって配列と同じような書き方が...
でもポインタは(ここでは)配列の要素を指し示すものであっ...
char *pc = ac + 5; // &(ac[0]) + 5 のこと
とすれば、pc[3] ⇔ *(pc + 3) ⇔ *(ac + 5 + 3) ⇔ ac[5 + 3] ...
このように、ポインタを使って、配列の先頭をずらしたように
見せかけることができます。あくまで''見せかけるだけ''です。
**配列を関数に渡すには [#ka5f6988]
配列を関数に渡したいとき、仮引数の宣言は 2 種類あります。
void func(char pc[]);
void func(char *pc);
これはどちらも''全く同じ''です。ポインタの宣言になってい...
そしてこの関数を呼び出すとき、配列そのものを渡すことはで...
代わりに配列の先頭の要素のアドレスを渡します。
char ac[10];
func(ac); // func(&(ac[0])); だとみなされる
それでもこれまで見てきたように、func のなかで
このポインタをあたかも配列のように用いることができるので...
*オブジェクトのサイズ [#j3e4b75f]
話は変わりまして、今度はオブジェクト(≒変数)のサイズの話...
**char, short, int, long [#k0c3df0f]
これらは全て整数を表しますが、どうしてこんなに沢山の種類...
整数をノートに記録するとき、
大きな数は沢山の桁数が必要です。
"4" なら1桁ですみますが、"123456" なら 6 桁も必要です。
コンピュータでも同じで、大きな数を表すには沢山のメモリが...
使用するメモリ量の順に char ≤ short ≤ int ≤ long...
つまり、char より long のほうが、大きな数まで表すことがで...
メモリ量の最小単位を「1バイト」といいます。
たとえば、Intel 32bit CPU ならば、int は4バイトです。
int i = 200000000;
と書くと、i の内容は 4 つに分割して格納されます。
たとえば &i が 45450721 なら、45450721番 ~ 4545074番のメ...
そうして、&i は使用されているメモリのうち、先頭の番号を表...
これまで「○○番目のメモリに格納される」と表現してきました...
これは正しくは、「○○番から××番までのメモリに格納される」...
(が、誤解の生じない限り、これからも従来の表現を用います)
**ポインタの加減算 [#wd8f6ca8]
上のほうで
char ac[100];
と宣言したら、5 番目の要素のアドレスは ac + 5 で得られる...
これは char 型が1バイトだからです。ですが、int 型の配列を
int ai[100];
と宣言したとしましょう。そして &(ai[0]) は 45450721 だと...
すると、(1 つの要素の大きさが 4 バイトなので) 5 番目の...
45450721 + (5 * 4) になります。
では 5 番目の要素のアドレスは ai + (5 * 4) と書かないとい...
実はこれは間違いです。単に
ai + 5
と書くだけでよいのです。x4 の部分は、コンパイラが勝手に行...
同様に
int ai[100];
int* pi = ai;
++pi;
というコードを実行すると、&(ai[0]) が 45450721 ならば、pi...
なります。++pi; は単なるインクリメントではなく、
ポインタが指す型のサイズ分だけ加算されるのです。
逆に言えば、ポインタを使うときは、コンパイラがポインタが...
知っている必要があります。
*配列再び [#b678afb4]
今度は2次元配列について考えます。
**2次元配列 [#vf3c36bc]
char aac[3][2];
と2次元配列を宣言したとします。するとこれは
「要素が2つの配列を、3つ並べたもの」になります。
aac[0][0], aac[0][1], aac[1][0], aac[1][1], aac[2][0], ...
の順に、''全ての要素が連続して''並べられます。
そして &aac[0][0] が 45450721 だとすると、aac[x][y] のア...
45450721 + (x * 2) + y
で計算できます。
aac[x] と一つ目の添え字だけを指定すると、{ aac[x][0], aac...
とならんでいる部分を char 型の配列だとみなしたものが得ら...
すなわち aac[x] は &(aac[x][0]) に変換されます。
char* pc1 = aac[1];
などと書けるわけです。pc1 の値は、実際には
45450721 + (1 * 2)
と計算されます。
そうして pc1[x] ⇔ aac[1][x] になります。
では aac を単独で用いたときは、何に変換されるのでしょうか?
aac[x] が char 型の配列なのですから、aac は「char型の配列...
ですから、aac は、「char 型の配列」へのポインタに変換され...
配列へのポインタは上のほうでやりましたね。
char (*pac)[2] = aac;
と変換できるわけです。型はややこしいですが、値は 45450721...
この場合、++pac; などと書けば、要素 2 つの配列の長さ分だ...
になります。すると pac[x][y] は aac[x + 1][y] と同じもの...
気づいた方がおられるでしょうか?
char aac[3][2];
char (*pac)[2] = aac;
char *pc0 = aac[0];
printf("%p\n%p\n", pac, pc0);
を実行すると、pac も pc0 も同じ値であることがわかります。...
値は同じでも''型が違う''のです。
pac[x] は char 型の配列になりますが、pc0[y] は char 型の...
**関数に渡すには [#c8ea3c1f]
関数の引数に int aai[3][2] を渡したいとしましょう。仮引数...
よいでしょうか?
配列を渡すには、その先頭の要素へのアドレスを受け渡しする...
今回の場合、aai は「配列の」配列ですから、受け取る側は「...
なければいけません。すなわち
void func(int (*pai)[2]);
と宣言します。もっと簡単に
void func(int pai[][2]);
と書いても同じです。この "2" を省略することはできません。...
++pai;
というコードがあったとき、「要素 2 つの int 型配列」分の
大きさだけ加算する必要があります。同様に
pai[2]; // *(pai + 2) のこと
というコードがあったとき、「pai の値よりも『要素 2 つの i...
進んだところ」から始まる配列を取得しないといけません。
このように要素 2 つ、というのは重要な情報なのです。
**似非2次元配列 [#u8e14d09]
お気づきの方はいるでしょうか? main 関数の宣言は
int main(int argc, char *(argv[])); // これは char *argv...
int main(int argc, char (*argv)[]);
int main(int argc, char **argv);
int main(int argc, char argv[][]);
などと書きます(全部同じです。char **argv; が宣言されたこ...
最後の宣言方法を見るとわかりますが、argv[][ここがない] で...
普段 argv[1][0] などと2次元配列と同じように用いています...
これは本来の2次元配列ならありえない話です。
なぜなら、argv[1] というのが、argv[0] から「要素いくつ分...
離れたところにあるか分からないのですから。
実は argv に渡されるのは、2次元配列ではなく、「ポインタ...
これの宣言は次のようにします。
char ac0[3];
char ac1[6];
char ac2[2];
char *(apc[3]) = { ac0, ac1, ac2 }; // { &(ac0[0]), &(ac...
apc[x] は、配列先頭の要素へのポインタですから
*(apc[x] + y)
もしくは
apc[x][y]
と書けば、x 番目の配列の、第 y 要素が得られることになりま...
この場合、コンパイラは要素にアクセスするために、アドレス...
そのアドレスは最初から配列内に格納されているのですから。
そして、「ポインタがならんだ配列」を関数に渡したいときは...
アドレスを渡すのでしたね。ですから関数の仮引数は「ポイン...
宣言しないといけません。それが
void func(char **ppc);
void func(char *(ppc[]));
などとなるのです。そのほかの書き方でもかまいません。
ちなみに、今回の変数を使って
printf("%p\n%p\n", apc, apc[0]);
を実行してみてください。前回と違い、今度は違うアドレスが...
*関数ポインタ [#b693a8df]
**関数のアドレス [#p1450f63]
プログラムが実行されているとき、変数の値はメモリのどこか...
そのメモリの番号をアドレスというのでした。
今回は、そのプログラムのコード自体に焦点を当てます。
実はプログラムというのは、メモリにロードされて実行されま...
つまり、プログラム自体もメモリのどこかに格納されているの...
プログラム中で関数を使うなら、その関数自体がメモリのどこ...
そこで、関数が格納されているメモリ領域の、先頭の番号を「...
呼ぶことにしましょう。
**関数の呼び出され方 [#naa2d704]
void func(void)
{
printf("hello, world!\n");
}
int main()
{
func();
}
というプログラムがあって、func のアドレス(func の先頭の...
が 45450721 だとしましょう。すると main 関数内の
func();
という行は「45450721番目のメモリに格納されているコードへ...
されます。
ですから、アドレスさえわかっていれば、その関数を呼び出す...
**関数ポインタ [#rd08d18a]
そこで、関数のアドレスを格納するポインタ pfn があるとしま...
このポインタを使って、関数を呼び出すには
int result = (*pfn)(x, y);
などと書けばよいのです。ただし
int result = pfn(x, y);
と書いても''全く同じに''解釈されます。*をつけてもつけなく...
この関数ポインタ独特の仕様は、関数ポインタをわかりにくく...
気がします。
さて、このように使う pfn を宣言するにはどうすればよいので...
今までと同じで「(*pfn)(int型, int型) (の返り値)は int ...
int (*pfn)(int, int);
先ほど述べたように pfn(int, int); と呼び出してもよいので...
× int pfn(int, int);
では、pfn という関数のプロトタイプ宣言になってしまうので...
必ず前者の方法を取ります。
関数ポインタに関数のアドレスを代入するには
int (*pfn)(int, int) = func; // func は int func(int x, ...
と書けばよいです。簡単ですね。
**関数ポインタの使い道 [#i4c16399]
int Kakeru(int x, int y)
{
return x * y;
}
int Tasu(int x, int y)
{
return x + y;
}
int Enzan(x, y, int (*pfn)(int, int))
{
return pfn(x, y);
}
int main()
{
printf("%d, %d\n",
Enzan(3, 4, Kakeru),
Enzan(3, 4, Tasu)
);
}
のように使えます。Enzan という関数ひとつで、掛け算と足し...
選べるようになるわけです。
C++にステップアップしたときに、virtual 関数というものに出...
それは関数ポインタを用いて実装されているのです。
*加筆と訂正おながいします [#lec075c5]
終了行:
*ポインタ [#yc48fc3c]
C/C++のポインタの概念を説明します。
#contents
**アドレス [#ga610581]
int i = 10;
というコードがあるとします。
これがコンパイルされたとき、EXEのなかではどう表現されてい...
変数の値は、実際にはメモリのどこかに保存されています。
そして「どこに保存されているか」というメモリの住所は、番...
上記のコードは、たとえば
45450721番目のメモリに 10 を代入せよ
のようにコンパイルされます。
何番目のメモリを使うかは、コンパイラが勝手に決めてくれま...
C言語を使えば、このようにどの変数にどこのメモリを割り当て...
自動的に割り振ってくれるわけですね。
この、"何番目のメモリ" という番号のことを「アドレス」と呼...
**ポインタ [#b9e4e979]
さて、上記のコードにおいて、変数 i が何番目のメモリに格納...
用語を用いて言い換えれば、変数 i のアドレスが知りたいわけ...
そういう時は &i と書けばおkです。上記の例では &i は 4545...
C言語には、アドレスを格納するための特別な変数型があります...
ポインタの宣言は
int *p;
のように書きます。たとえば
int i = 10;
int *p = &i;
などとします。この p には、たとえば 45450721 などが格納さ...
この例では、p のことを「i へのポインタ」といいます。
「i へのポインタ」とは、"i のアドレスが格納されているポイ...
では 45450721番目のメモリに入っている値(すなわち i に格...
printf("%d", *p);
と書きます。
*p のことを、「p が指す値」と呼びます。
**ポインタの宣言 [#f183fa34]
int i;
という宣言文は「i は int型である」と宣言しています。これ...
int *p;
という宣言文は「*p は int型である」、もしくは「pとは、*p ...
ちょっと複雑な例を考えましょう。intへのポインタを10個並べ...
ap[index] はポインタですね。その指す値(int型)は、 *(ap[...
int *(ap[10]);
と書けばよいのです。
次に、intを10個並べた配列へのポインタ pa が欲しいときはど...
*pa は配列ですから、(*pa)[index] は int 型ですね。で...
int (*pa)[10];
と宣言すればよいです。
*関数の引数について [#r19e389b]
**値渡し [#j2c6d7ac]
#include <stdio.h>
void func(int a)
{
a = 100;
}
int main()
{
int b = 1;
func(b);
printf("%d", b);
}
というプログラムを実行すると、"1" と表示されます。決して ...
具体的に追っていきましょう。&a は 45450721、&b は 6741 だ...
main関数のなかでは、まず
int b = 1;
で 6741番目のメモリが 1 になります。次に
func(b);
では、45450721番目のメモリに 1 がコピーされて
func の先頭にジャンプします。
func のなかで、45450721番目のメモリに 100 が代入されます...
printf("%d", b);
に戻ってきたとき、6741番目のメモリには何が入っているでし...
1 ですね。
このように、関数の引数には、呼び出し元の変数の値がコピー...
関数内部ではその''コピーされたものしかいじれません''。
**関数の引数にポインタを用いると [#m8becd41]
#include <stdio.h>
void func(int *p)
{
*p = 100;
}
int main()
{
int b = 1;
func(&b);
printf("%d", b);
}
を最初から追っていきましょう。&p は 45450721、&b は 6741 ...
まず
int b = 1;
で 6741番目のメモリが 1 になります。つぎに
func(&b);
で、45450721番目のメモリに 6471 がコピーされて、funcの先...
funcのなかで、45450721番目のメモリに格納されている番号(6...
メモリに 100 が代入されます。そうして
printf("%d", b);
に戻ってきたとき、6741番目のメモリには 100 が入っています。
ですから、画面には "100" と表示されることになります。
このように、ポインタを関数の引数に用いると、
関数内で呼び出し元の変数をいじることができるようになりま...
*配列 [#k8dd863f]
**配列 [#afd152dd]
char ac[100];
と配列を宣言したとします。配列は、''メモリ上に連続して並...
&(ac[0]) が 45450721 だとすると
&(ac[0]) = 45450721
&(ac[1]) = 45450722
&(ac[2]) = 45450723
&(ac[3]) = 45450724
.
.
.
とならびます。そうして、ac を単独で用いると、&(ac[0])...
printf("%p\n", ac); // %d が int を表示するように...
printf("%p\n", &(ac[0]));
このコードを実行すると、同じ値が表示されます。
このように、配列の識別子は、その配列の先頭のアドレスに変...
変換されるだけで、配列がアドレスそのもの、というわけでは...
また、先頭のアドレスが格納されているポインタから、
配列へ逆変換することは''できません''。
**配列とポインタ [#ie104a63]
さて、上記の ac を使って
*(ac + 3);
は何を表すでしょう? ac + 3 は 45450724 ですから、
それに * をつけると ac[3] にアクセスできますね。
一般に
ac[index] ⇔ *(ac + index)
の関係が成り立ちます。
ac は &(ac[0]) に変換されるといいましたが、ac[0] は c...
&(ac[0]) は char へのポインタです:
char ac[100];
char *pc = ac;
と書くことができます。配列をポインタに代入しているように...
実際には''配列の先頭のアドレスをポインタに代入しているだ...
このポインタを使って、ac の要素にアクセスすることを考えま...
たとえば ac[3] にアクセスしたければ
*(pc + 3)
と書けばよいですね。この節を先頭から読み直すと、これが
pc[3]
と書ける気がしませんか? 書けます。ですが
pc[3] は、''単なる *(pc + 3) の言い換えに過ぎません''。
ac[3] は、「配列 ac の 3 番目の要素」を指しますが、
pc[3] は、単に*(pc + 3) を言い換えただけです。
この違いは今は気になりませんが、2次元配列を考えたときに...
何はともあれ、ポインタをつかって配列と同じような書き方が...
でもポインタは(ここでは)配列の要素を指し示すものであっ...
char *pc = ac + 5; // &(ac[0]) + 5 のこと
とすれば、pc[3] ⇔ *(pc + 3) ⇔ *(ac + 5 + 3) ⇔ ac[5 + 3] ...
このように、ポインタを使って、配列の先頭をずらしたように
見せかけることができます。あくまで''見せかけるだけ''です。
**配列を関数に渡すには [#ka5f6988]
配列を関数に渡したいとき、仮引数の宣言は 2 種類あります。
void func(char pc[]);
void func(char *pc);
これはどちらも''全く同じ''です。ポインタの宣言になってい...
そしてこの関数を呼び出すとき、配列そのものを渡すことはで...
代わりに配列の先頭の要素のアドレスを渡します。
char ac[10];
func(ac); // func(&(ac[0])); だとみなされる
それでもこれまで見てきたように、func のなかで
このポインタをあたかも配列のように用いることができるので...
*オブジェクトのサイズ [#j3e4b75f]
話は変わりまして、今度はオブジェクト(≒変数)のサイズの話...
**char, short, int, long [#k0c3df0f]
これらは全て整数を表しますが、どうしてこんなに沢山の種類...
整数をノートに記録するとき、
大きな数は沢山の桁数が必要です。
"4" なら1桁ですみますが、"123456" なら 6 桁も必要です。
コンピュータでも同じで、大きな数を表すには沢山のメモリが...
使用するメモリ量の順に char ≤ short ≤ int ≤ long...
つまり、char より long のほうが、大きな数まで表すことがで...
メモリ量の最小単位を「1バイト」といいます。
たとえば、Intel 32bit CPU ならば、int は4バイトです。
int i = 200000000;
と書くと、i の内容は 4 つに分割して格納されます。
たとえば &i が 45450721 なら、45450721番 ~ 4545074番のメ...
そうして、&i は使用されているメモリのうち、先頭の番号を表...
これまで「○○番目のメモリに格納される」と表現してきました...
これは正しくは、「○○番から××番までのメモリに格納される」...
(が、誤解の生じない限り、これからも従来の表現を用います)
**ポインタの加減算 [#wd8f6ca8]
上のほうで
char ac[100];
と宣言したら、5 番目の要素のアドレスは ac + 5 で得られる...
これは char 型が1バイトだからです。ですが、int 型の配列を
int ai[100];
と宣言したとしましょう。そして &(ai[0]) は 45450721 だと...
すると、(1 つの要素の大きさが 4 バイトなので) 5 番目の...
45450721 + (5 * 4) になります。
では 5 番目の要素のアドレスは ai + (5 * 4) と書かないとい...
実はこれは間違いです。単に
ai + 5
と書くだけでよいのです。x4 の部分は、コンパイラが勝手に行...
同様に
int ai[100];
int* pi = ai;
++pi;
というコードを実行すると、&(ai[0]) が 45450721 ならば、pi...
なります。++pi; は単なるインクリメントではなく、
ポインタが指す型のサイズ分だけ加算されるのです。
逆に言えば、ポインタを使うときは、コンパイラがポインタが...
知っている必要があります。
*配列再び [#b678afb4]
今度は2次元配列について考えます。
**2次元配列 [#vf3c36bc]
char aac[3][2];
と2次元配列を宣言したとします。するとこれは
「要素が2つの配列を、3つ並べたもの」になります。
aac[0][0], aac[0][1], aac[1][0], aac[1][1], aac[2][0], ...
の順に、''全ての要素が連続して''並べられます。
そして &aac[0][0] が 45450721 だとすると、aac[x][y] のア...
45450721 + (x * 2) + y
で計算できます。
aac[x] と一つ目の添え字だけを指定すると、{ aac[x][0], aac...
とならんでいる部分を char 型の配列だとみなしたものが得ら...
すなわち aac[x] は &(aac[x][0]) に変換されます。
char* pc1 = aac[1];
などと書けるわけです。pc1 の値は、実際には
45450721 + (1 * 2)
と計算されます。
そうして pc1[x] ⇔ aac[1][x] になります。
では aac を単独で用いたときは、何に変換されるのでしょうか?
aac[x] が char 型の配列なのですから、aac は「char型の配列...
ですから、aac は、「char 型の配列」へのポインタに変換され...
配列へのポインタは上のほうでやりましたね。
char (*pac)[2] = aac;
と変換できるわけです。型はややこしいですが、値は 45450721...
この場合、++pac; などと書けば、要素 2 つの配列の長さ分だ...
になります。すると pac[x][y] は aac[x + 1][y] と同じもの...
気づいた方がおられるでしょうか?
char aac[3][2];
char (*pac)[2] = aac;
char *pc0 = aac[0];
printf("%p\n%p\n", pac, pc0);
を実行すると、pac も pc0 も同じ値であることがわかります。...
値は同じでも''型が違う''のです。
pac[x] は char 型の配列になりますが、pc0[y] は char 型の...
**関数に渡すには [#c8ea3c1f]
関数の引数に int aai[3][2] を渡したいとしましょう。仮引数...
よいでしょうか?
配列を渡すには、その先頭の要素へのアドレスを受け渡しする...
今回の場合、aai は「配列の」配列ですから、受け取る側は「...
なければいけません。すなわち
void func(int (*pai)[2]);
と宣言します。もっと簡単に
void func(int pai[][2]);
と書いても同じです。この "2" を省略することはできません。...
++pai;
というコードがあったとき、「要素 2 つの int 型配列」分の
大きさだけ加算する必要があります。同様に
pai[2]; // *(pai + 2) のこと
というコードがあったとき、「pai の値よりも『要素 2 つの i...
進んだところ」から始まる配列を取得しないといけません。
このように要素 2 つ、というのは重要な情報なのです。
**似非2次元配列 [#u8e14d09]
お気づきの方はいるでしょうか? main 関数の宣言は
int main(int argc, char *(argv[])); // これは char *argv...
int main(int argc, char (*argv)[]);
int main(int argc, char **argv);
int main(int argc, char argv[][]);
などと書きます(全部同じです。char **argv; が宣言されたこ...
最後の宣言方法を見るとわかりますが、argv[][ここがない] で...
普段 argv[1][0] などと2次元配列と同じように用いています...
これは本来の2次元配列ならありえない話です。
なぜなら、argv[1] というのが、argv[0] から「要素いくつ分...
離れたところにあるか分からないのですから。
実は argv に渡されるのは、2次元配列ではなく、「ポインタ...
これの宣言は次のようにします。
char ac0[3];
char ac1[6];
char ac2[2];
char *(apc[3]) = { ac0, ac1, ac2 }; // { &(ac0[0]), &(ac...
apc[x] は、配列先頭の要素へのポインタですから
*(apc[x] + y)
もしくは
apc[x][y]
と書けば、x 番目の配列の、第 y 要素が得られることになりま...
この場合、コンパイラは要素にアクセスするために、アドレス...
そのアドレスは最初から配列内に格納されているのですから。
そして、「ポインタがならんだ配列」を関数に渡したいときは...
アドレスを渡すのでしたね。ですから関数の仮引数は「ポイン...
宣言しないといけません。それが
void func(char **ppc);
void func(char *(ppc[]));
などとなるのです。そのほかの書き方でもかまいません。
ちなみに、今回の変数を使って
printf("%p\n%p\n", apc, apc[0]);
を実行してみてください。前回と違い、今度は違うアドレスが...
*関数ポインタ [#b693a8df]
**関数のアドレス [#p1450f63]
プログラムが実行されているとき、変数の値はメモリのどこか...
そのメモリの番号をアドレスというのでした。
今回は、そのプログラムのコード自体に焦点を当てます。
実はプログラムというのは、メモリにロードされて実行されま...
つまり、プログラム自体もメモリのどこかに格納されているの...
プログラム中で関数を使うなら、その関数自体がメモリのどこ...
そこで、関数が格納されているメモリ領域の、先頭の番号を「...
呼ぶことにしましょう。
**関数の呼び出され方 [#naa2d704]
void func(void)
{
printf("hello, world!\n");
}
int main()
{
func();
}
というプログラムがあって、func のアドレス(func の先頭の...
が 45450721 だとしましょう。すると main 関数内の
func();
という行は「45450721番目のメモリに格納されているコードへ...
されます。
ですから、アドレスさえわかっていれば、その関数を呼び出す...
**関数ポインタ [#rd08d18a]
そこで、関数のアドレスを格納するポインタ pfn があるとしま...
このポインタを使って、関数を呼び出すには
int result = (*pfn)(x, y);
などと書けばよいのです。ただし
int result = pfn(x, y);
と書いても''全く同じに''解釈されます。*をつけてもつけなく...
この関数ポインタ独特の仕様は、関数ポインタをわかりにくく...
気がします。
さて、このように使う pfn を宣言するにはどうすればよいので...
今までと同じで「(*pfn)(int型, int型) (の返り値)は int ...
int (*pfn)(int, int);
先ほど述べたように pfn(int, int); と呼び出してもよいので...
× int pfn(int, int);
では、pfn という関数のプロトタイプ宣言になってしまうので...
必ず前者の方法を取ります。
関数ポインタに関数のアドレスを代入するには
int (*pfn)(int, int) = func; // func は int func(int x, ...
と書けばよいです。簡単ですね。
**関数ポインタの使い道 [#i4c16399]
int Kakeru(int x, int y)
{
return x * y;
}
int Tasu(int x, int y)
{
return x + y;
}
int Enzan(x, y, int (*pfn)(int, int))
{
return pfn(x, y);
}
int main()
{
printf("%d, %d\n",
Enzan(3, 4, Kakeru),
Enzan(3, 4, Tasu)
);
}
のように使えます。Enzan という関数ひとつで、掛け算と足し...
選べるようになるわけです。
C++にステップアップしたときに、virtual 関数というものに出...
それは関数ポインタを用いて実装されているのです。
*加筆と訂正おながいします [#lec075c5]
ページ名: