*(C入門 + 3)

ポインタ

複数の変数を一度に用意しなければいけないときがあります。そういうときに配列を使うのですが,配列を理解するためにはポインタを理解していないといけません。

int型のaという変数を宣言したとき,メモリ上のどこかに数バイト(環境によるけど4バイトくらい)の領域が確保されます。その場所をアドレスといいます。

アドレスが&演算子で取得できることは説明しました。ここでは,アドレスそのものを変数として扱う方法を紹介します。

int *p;

 とすると,int*型の変数pが宣言されます。int*はintではありません。int型の変数のアドレスの値を格納する変数です(int型以外の変数のアドレスをそのまま格納することはできません)。例えば

int a;
int *p;
p = &a;

 とするとpはaのアドレスを指します。それを参照するときはpの前に*をつけます。pを宣言したときの*とは違うので注意。

あと,int *a, b;とすると,どういうわけかaはint*型,bはint型になるようになっています。int*型の変数を「,」で区切って2つ以上宣言する場合はint *a, *b;としなければいけません。

int a;
int *p = &a;
a = 10;
printf("%d\n", a);
printf("%p\n", &a);
printf("%p\n", p);
printf("%d\n", *p);

printfでアドレスを表示するときは%dではなく%pを使って16進数にするのがマナーです。

実行すると,aの値(10),aのアドレス(unspecified),pの値すなわちaのアドレス(unspecified),pの参照先すなわちaの値(10)が順に表示されます。

ちなみにprintf関数は有能なので

printf("%d\n%p\n%p\n%d\n", a, &a, p, *p);

とまとめて書くこともできます。

ここで,*pはaのエイリアス(別名)です。

ちなみに,pそのものも変数なので,pのアドレスをとることもできます。

printf("%p", &p);

 暇があれば実行してみて下さい。

さらに付け加えると,int*はint型の変数へのアドレスを格納しましたが,int*型の変数へのアドレスを格納するint**も作れます。

int a, *p = &a, **pp = &p;
printf("%d %p %p %d %p %p %d %p %p\n", a, &a, p, *p, &p, pp, **pp, *pp, &pp);

 本当に暇な人はやってみて下さい。

配列

100個の変数を一度に用意したいとき,Cでは配列を使います。

int a[100];

 これでintがメモリ上に連続して100個用意されます。intが一つ4バイトなら全部で400バイトです。

配列の要素にアクセスしたいときはポインタの出番です。

aというのが実は配列の先頭へのアドレスを指すことになっています。よって1つ目の要素にアクセスするときは *a です。

さて,n番目の要素にアクセスしたい。ここで,100個の変数の領域が「連続して」確保されているというのが重要になります。つまりaが19FDBCでintが4バイトなら(今僕のPCでやってみたらこうだった),1つ目の要素が19FDBCのところにあるので,2つ目の要素は19FDC0,3つ目の要素は19FDC4のところにあることになります。なのでポインタに4足して参照すれば2つ目の要素,8足して参照すれば3つ目の要素が出てくるわけです。

ここでポインタと整数の足し算というものがどう定義されているかを知っていなくてはいけません。

int *a = 1000; int b = 10;

 とすると,a+bは実は1040になります(intが4バイトの場合)。配列を扱いやすくするためにそう定義したんです。よって配列のn+1番目の要素にアクセスしたいときは

*(a + n)

 とすればいいんです。そしてこれには別の表記法があります。

a[n]

 は*(a+n)と同じ意味です。配列を宣言するときにも[]を使いましたが,ちょっと違うので注意してください。あと足し算はどちらの順番で計算してもよいのでn[a]でも配列aのn番目の要素になります。

注意:配列は少し多めにとっておく(575)。例えば1000個の配列が欲しいときはint a[1024];くらいにするのがよいです。

scanfに渡すときはi番目の要素は a + i (アドレスなので)。

for(int i = 0; i < 10; i++) scanf("%d", a + i);

配列の初期化

int a[10] = {3, 7, 1, 2, 0, 0, 1, 2, 8, 4};

 と書くと,a[0]が3,a[1]が7,a[2]が1,a[3]が2,a[4]が0,a[5]が0,a[6]が1,a[7]が2,a[8]が8,a[9]が4である長さ10の配列ができます。こういうふうに初期化するときは[]の中の10は省略できます。

int a[] = {3, 7, 1, 2, 0, 0, 1, 2, 8, 4}; // 長さ10の配列

malloc

int n;
int a[n];

 みたいなことは実はCではできません。配列を宣言するときの[]の中身は定数式,つまり「3」とか「100」とか「4+3」とかになっていなければいけません(プリプロセッサは大丈夫)。

nが10000以下とか制約がついていればint a[10100]くらいとって最初のn個だけを使っていればいいです。

メモリを節約する場合はstdlib.hのmallocという関数を使います。

int *a;
a = (int *)malloc(sizeof(int) * n);

 説明は面倒なので放棄。malloc(sizeof(型) * 個数)でメモリが確保できるよ。それに(型*)をつけて代入するよ。以上。

嘘です,ちゃんと説明します。別にここだけ読み飛ばしてもいいけど。

malloc(n) で n バイトの領域を確保します。

C言語には型というものがあります。これまでintだけを扱ってきましたが,他にもunsignedやcharやdoubleなどがあります。int*型とdouble*型は違うものとして扱われます。

では,int型の配列の確保にもdouble型の配列の確保にも使われるmalloc関数は何の型を返せばいいのか。

実はvoid*型というものがあるんです。malloc関数はそれを返します。

それをint*型にキャスト(変換)して代入します。キャストしないと怒られます。

キャストの方法は簡単。値の前に(型)とつけるだけ。

よって(型 *)malloc(sizeof(型) * 個数)となります。

mallocで確保した配列は内部的には配列として宣言した配列と同じですが,sizeof演算子の挙動だけが異なります。

int a[10];
int *b = (int *)malloc(sizeof(int *) * 10);
printf("%d %d %d", sizeof(int), sizeof(a), sizeof(b));

僕の環境では「4 40 4」と出力されました。

2次元配列

int a[5][3];

とするとメモリ上にsizeof(int) * 15バイトの領域が確保されます。

ここで

a…先頭へのアドレス

**a (= a[0][0] ) …最初の要素

*a (= a[0] ) … a[0][0] ~ a[0][2] が入ってる配列(長さ3)

a[1] … a[1][0] ~ a[1][2] が入ってる配列(長さ3)

*a[1] … *(a[1]) (= a[1][0] ) (優先順位)

*((int *)a + 5) … a[1][2] (キャスト)

こういうのテストに出やすいから覚えとけよー

初期化するときは

int a[5][3] = {{1, 7, -2}, {0, -1, 2}, {-5, 3, 3}, {1, 0, 4}, {1, -2, -4}};

 とする。このとき[5][3]の5は省略できますが3は省略できません。

int a[][3] = {{1, 7, -2}, {0, -1, 2}, {-5, 3, 3}, {1, 0, 4}, {1, -2, -4}};//はい
int a[5][] = {{1, 7, -2}, {0, -1, 2}, {-5, 3, 3}, {1, 0, 4}, {1, -2, -4}};//いいえ
int a[][] = {{1, 7, -2}, {0, -1, 2}, {-5, 3, 3}, {1, 0, 4}, {1, -2, -4}};//いいえ

mallocやるときはforを使います。

int m, n, **a;
a = malloc(sizeof(int *) * m);
for(int i = 0; i < m; i++) a[i] = malloc(sizeof(int) * n);

 これマクロにしておくと便利だけど使ってる人見たことないな…。

同じようにして3次元配列とか4次元配列とかも作れるけど滅多に使いません。