【C言語】ポインタ
ポインタって分かりにくいですよね。。。
改めて自分の理解のためにメモ書きしていきたいと思います。
今日のお話
環境
対象言語
- c
メモ
int型、double型のように、ポインタ型がある。
ポインタ型にも
ポインタ(int)型:int *
やポインタ(double)型:double *
がある。
→型として理解すればOKの認識。
ポインタ型は変数のアドレスを保持する
1回作成した変数に対して、操作をしたい時に、アドレスに対して操作を行えばよいため、効率が良い。 →「正直使わなくても問題ないですよね?」と言われてしまうと、この段階では問題は発生しない。
#include <stdio.h> void AddNumber(int *value) { *value = *value + 100; // 直接対象のアドレスに対して値を加えている } int main() { int a = 100; AddNumber(&a); printf("%d\n", a); return 0; }
#include <stdio.h> int AddNumber(int value) // 仮引数が生成される時に新たにvalue変数が生成されている { value = value + 100; return value; } int main() { int a = 100; a = AddNumber(a); // 戻ってきた値を改めてaに代入 printf("%d\n", a); return 0; }
- ポインタが必要になるのは配列の場合で、生成した(or 引数で受け取った)値をアドレスを通じて戻さないといけない。
#include <stdio.h> #include <stdlib.h> int *AddNumber() { int *values = (int *)malloc(sizeof(int) * 100); for (int i = 0; i < 100; i++) { values[i] = 100; } return values; } int main() { // int a[100]; int *a; a = AddNumber(); for (int i = 0; i < 100; i++) { printf("No.%d: %d\n", i, a[i]); } return 0; }
関数から配列(値)返すことができない。 →ローカル変数は関数の中でのみ有効になるため
↓はNG(出力するとおかしな値になる)
#include <stdio.h> int *AddNumber() { int values[100]; for (int i = 0; i < 100; i++) { values[i] = 100; } return values; // returnした後にvaluesが解放されてしまう } int main() { // int a[100]; int *a; a = AddNumber(); for (int i = 0; i < 100; i++) { printf("No.%d: %d\n", i, a[i]); // 正しい値が出力されない } return 0; }
- 関数の引数は値渡しになるため
NULL
値を渡してローカル変数で設定しても 関数のスコープを抜けたときに解放されてしまう。 どうしてもやりたい場合は、ダブルポインターを使う。
#include <stdio.h> #include <stdlib.h> void AddNumber(int **value) { *value = (int *)malloc(sizeof(int)); **value = **value + 100; } int main() { int *a = NULL; AddNumber(&a); printf("%d\n", *a); return 0; }
- 配列の指定方法はどちらで同じ
- a[i] = 100;
- *(pa + i) = 200;
int a[10]; int *pa = a; // &a[0]と同じ for (int i; i < 10; i++) { a[i] = 100; *(pa + i) = 200; printf("No.%d: %d\n", i, *(pa + i)); }
- 関数の引数で配列を使う場合、以下は全てパターン1と同じ意味になる。 →どれで書いても同じなので、パターン1を使った方が無難そう
int SampleFunc(int *a); // パターン1 int SampleFunc(int a[]); // パターン2 int SampleFunc(int a[5]); // パターン3
- 下記は全て同じ結果になる。
int a[10]; printf("&a : %p\n", (void *)&a); printf("&a[0] : %p\n", (void *)&a[0]); printf("a : %p\n", (void *)a); printf("============\n"); for (int i = 0; i < 5; i++) { printf("%d : %p\n", i, (void *)&a[i]); } printf("============\n"); int *p = a; for (int i = 0; i < 5; i++) { printf("%d : %p\n", i, (void *)&p[i]); } printf("============\n"); // この形式で追加しても良いが型がint[10]へのポインタになる int(*pa)[10]; pa = &a; for (int i = 0; i < 5; i++) { // (int *)にキャストして、そこからポインタ演算でインクリメント printf("%d : %p\n", i, (void *)(((int *)pa) + i)); }
戯言
- ポインタもそうだけど、C言語に詳しくならないと。。。