Post on 13-Jul-2015
ポインタとは
● メモリのアドレスメモリのアドレスを格納する変数
● バグの温床
● でもこれがないとC言語ははじまらない
アドレス メモリの内容
0x00000000
0x00000001
0x00000002
0x00000003
0x00000004
0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
ポインタ・・・の前に
int hoge = 12;char piyo = 'a';double fuga = 0.123456;
32ビット環境ではint型は4バイトchar型は1バイトdouble型は8バイト
アドレス メモリの内容
0x00000000
0x00000001 12(hoge)0x00000002
0x00000003
0x00000004
0x00000005
0x00000006 'a'(piyo)
0x00000007 0.123456(fuga)0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
ポインタ関連の文法
int hoge = 12;
printf(“%p\”,&hoge);//結果:0x00000001
変数のアドレスを得る&変数名
アドレス メモリの内容
0x00000000
0x000000010x00000001 12(hoge)0x00000002
0x00000003
0x00000004
0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
ポインタ関連の文法
int hoge = 12;int *phoge;
phoge = &hoge;
ポインタの定義型の名前 *変数の名前;
/*32ビット環境ではポインタは4バイト*/
アドレス メモリの内容
0x00000000
0x000000010x00000001 12(hoge)0x00000002
0x00000003
0x00000004
0x00000005
0x00000006 0x00000001
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
ポインタ関連の文法
int hoge = 12;int *phoge;
phoge = &hoge;
printf(“%d” , *phoge);
ポインタを通して間接的に変数を見る*変数名
アドレス メモリの内容
0x00000000
0x000000010x00000001 12(hoge)0x00000002
0x00000003
0x00000004
0x00000005
0x00000006 0x00000001
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
ダブルポインタの動作int hoge = 22;int *phoge;int **pphoge;
phoge = &hoge;pphoge = &phoge;
printf("%p\n" , phoge );printf("%p\n" , *pphoge );//結果:0x00000000
printf("%p\n" , &phoge );printf("%p\n" , pphoge );//結果:0x00000004
printf(“%d\n”,**pphoge);//結果:22
アドレス メモリの内容
0x00000000 22(hoge)0x00000001
0x00000002
0x00000003
0x00000004 0x00000000(phoge)0x00000005
0x00000006
0x00000007
0x00000008 0x00000004(pphoge)0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
ポインタの例:スワップ
swap1(int a , int b){ int tmp = b; b = a; a = tmp;}
swap2(int *a , int *b){ int tmp = *b; *b = a; *a = tmp;}
int hoge = 5;int piyo = 10;
swap1(hoge,piyo);//結果?
swap2(&hoge,&piyo);//結果?
スワップswap1(int a , int b){ int tmp = b; b = a; a = tmp;}...
int hoge = 5;int piyo = 10;//←今ココ
swap1(hoge,piyo);printf(“%d,%d”,hoge,piyo);
アドレス メモリの内容
0x00000000 5(hoge)0x00000001
0x00000002
0x00000003
0x00000004 10(piyo)0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
0x0000000F
スワップswap1(int a , int b)//←今ココ{ int tmp = b; b = a; a = tmp;}
...
int hoge = 5;int piyo = 10;
swap1(hoge,piyo);//←ココのprintf(“%d,%d”,hoge,piyo);
アドレス メモリの内容
0x00000000 5(hoge)0x00000001
0x00000002
0x00000003
0x00000004 10(piyo)0x00000005
0x00000006
0x00000007
0x00000008 5(a)0x00000009
0x0000000A
0x0000000B
0x0000000C 10(b)0x0000000D
0x0000000E
0x0000000F
スワップswap1(int a , int b){ int tmp = b; b = a; a = tmp;//←今ココ}
...
int hoge = 5;int piyo = 10;
swap1(hoge,piyo);//←ココのprintf(“%d,%d”,hoge,piyo);
アドレス メモリの内容
0x00000000 5(hoge)0x00000001
0x00000002
0x00000003
0x00000004 10(piyo)0x00000005
0x00000006
0x00000007
0x00000008 10(a)0x00000009
0x0000000A
0x0000000B
0x0000000C 5(b)0x0000000D
0x0000000E
0x0000000F
スワップswap1(int a , int b){ int tmp = b; b = a; a = tmp;}...
int hoge = 5;int piyo = 10;
swap1(hoge,piyo);printf(“%d,%d”,hoge,piyo);//↑今ココ
アドレス メモリの内容
0x00000000 5(hoge)0x00000001
0x00000002
0x00000003
0x00000004 10(piyo)0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
0x0000000F
スワップswap2(int *a , int *b){ int tmp = *b; *b = *a; *a = tmp;}...
int hoge = 5;int piyo = 10;//←今ココ
swap1(&hoge,&piyo);printf(“%d,%d”,hoge,piyo);
アドレス メモリの内容
0x00000000 5(hoge)0x00000001
0x00000002
0x00000003
0x00000004 10(piyo)0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
0x0000000F
スワップswap2(int *a , int *b)//←今ココ{ int tmp = *b; *b = *a; *a = tmp;}...
int hoge = 5;int piyo = 10;
swap1(&hoge,&piyo);//←ココのprintf(“%d,%d”,hoge,piyo);
アドレス メモリの内容
0x00000000 5(hoge)0x00000001
0x00000002
0x00000003
0x00000004 10(piyo)0x00000005
0x00000006
0x00000007
0x00000008 0x00000000(a)0x00000009
0x0000000A
0x0000000B
0x0000000C 0x00000004(b)0x0000000D
0x0000000E
0x0000000F
スワップswap2(int *a , int *b){ int tmp = *b; *b = *a; *a = tmp;//←今ココ}...
int hoge = 5;int piyo = 10;
swap1(&hoge,&piyo);//←ココのprintf(“%d,%d”,hoge,piyo);
アドレス メモリの内容
0x00000000 10(hoge)0x00000001
0x00000002
0x00000003
0x00000004 5(piyo)0x00000005
0x00000006
0x00000007
0x00000008 0x00000000(a)0x00000009
0x0000000A
0x0000000B
0x0000000C 0x00000004(b)0x0000000D
0x0000000E
0x0000000F
スワップswap2(int *a , int *b){ int tmp = *b; *b = *a; *a = tmp;}...
int hoge = 5;int piyo = 10;
swap1(&hoge,&piyo);printf(“%d,%d”,hoge,piyo);//↑今ココ
アドレス メモリの内容
0x00000000 10(hoge)0x00000001
0x00000002
0x00000003
0x00000004 5(piyo)0x00000005
0x00000006
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
0x0000000F
複数の「何か」から同じデータを参照
int apple_num;//0x00334455
0x00334455
MARISHI
0x00334455
kano
アドレスさえあれば、同じデータを簡単に共有できる。
複数の「何か」から同じデータを参照
int apple_num;//0x00334455
0x00334455MARISHI
0x00334455kano
乱用すると、意図しない値の操作が行われた時に、どのプログラムが間違ってるか分かり辛い
0x00334455*apple_num -= 5
xALTx
誰だ5個も食いやがった奴は!
ククク・・・
関数の引数に構造体を使うとき
typedef struct Human{ int age; int height; int weight;} Human;
//構造体をまるごとコピーして重い。void print_human(Human h){ printf(“%d\n”,sizeof(h) ); //結果:16(環境依存}
//アドレスのみコピーvoid print_human_size_p( Human *ph){ printf(“%d\n”,sizeof(ph) ); //結果:4(環境依存}
ポインタと配列の衝撃の事実
● 配列へのアクセスにはポインタも利用できる
int i;int ary[3] = {7,5,3};int *p = ary;
for(i = 0 ; i < 3 ; ++i){ printf(“%d\n” , ary[i] ); //出力結果は printf(“%d\n” , *(p+i) ); //一緒}
配列の先頭
int ary[3] = {7,5,3};int *p = ary;
配列の先頭だけ記述↓配列の先頭ポインタ
アドレス メモリの内容
0x00000000 7
0x00000001
0x00000002
0x00000003
0x00000004 5
0x00000005
0x00000006
0x00000007
0x00000008 3
0x00000009
0x0000000A
0x0000000B
0x0000000C 0x00000000
0x0000000D
0x0000000E
0x0000000F
ポインタに整数を足すと
int ary[3] = {7,5,3};int *p = ary;
printf(“%p”,ary);printf(“%p”,&ary[0]);printf(“%p”,p);//一緒
printf(“%p”,&ary[2]);printf(“%p”,p+2);//一緒
++p;printf(“%p”,&ary[1]);printf(“%p”,p);//一緒
アドレス メモリの内容
0x00000000 7
0x00000001
0x00000002
0x00000003
0x00000004 5
0x00000005
0x00000006
0x00000007
0x00000008 3
0x00000009
0x0000000A
0x0000000B
0x0000000C 0x00000000
0x0000000D
0x0000000E
0x0000000F
配列を関数の引数にする
//配列の先頭のポインタが渡される!void func( int a[]){ printf("%p\n", a );//出力:0x000000ff a[2] = 0;}
///
int ary[] = {1,2,3};printf("%p", ary );//出力:0x000000fffunc(ary);//ポインタを渡したので、関数の書き換えが反映されるprintf("%d\n",ary[2]);//出力:0
配列を関数の引数にする(応用)
//配列の先頭のポインタを渡すので、これでもいいvoid func2(int *a){ *(a+2) = 0; //a[2] = 0;//1次元の場合、これでもよい。}
///int ary[] = {1,2,3};func(ary);printf("%d\n",ary[2]);//0
2次元配列について
//一番左の要素数は省略可void func(int ary[][3]){ ary[1][1] = 2;}
int main(){ int ary[][3] = { {0,1,2}, {3,4,5} }; func(ary);}
2次元配列についての罠
void func(int ary[][3]){ ary[1][1] = 2;}
//ポインタでも扱いは同様と思ってるとvoid func(int *ary){ ary[1][1] = 2;//エラー!}
2次元配列についての罠
● 要素数がある程度分からないと要素の参照に困る
char hoge[][3] = {{'a','b','c'}, {'d','e','f'}};
//配列の最初を参照すればOKhoge[0][0] = 'e';
//配列の最初から3バイト先を//参照しなくてはいけないhoge[1][0] = 'f';
先ほどのポインタは、何バイト先を読み込めばいいか分からなかった。よってエラー
アドレス メモリの内容
0x00000000
0x00000001 'a' ( [0][0] )
0x00000002 'b' ( [0][1] )
0x00000003 'c' ( [0][2] )
0x00000004 'd' ( [1][0] )
0x00000005 'e' ( [1][2] )
0x00000006 'f' ( [1][3] )
0x00000007
0x00000008
0x00000009
0x0000000A
0x0000000B
0x0000000C
0x0000000D
0x0000000E
文字列
● 文字列はchar型の配列として扱うことができる● 文字列の最後にはNULL文字がある。
char str1[5] = "abcd";char str2[5] = {'a','b','c','d','\0'};
printf("%s\n",str1);printf("%s\n",str2);
a b c d \0
文字列
● 文字列はchar型の配列として扱うことができる● 文字列の最後にはNULL文字がある
char str1[5] = "abcd";char str2[5] = {'a','b','c','d','\0'};int i;for(i=0;i<5;++i){ printf("%c",str1[i]); //一緒 printf("%c",str2[i]); //一緒}//char型、文字列として出力する時、//ヌル文字は出力されない
文字列
● 文字列の最後にはNULL文字があるので・・・
char str1[5] = "abcd";char str1[2] = '\0';
printf("%s\n",str1);
//出力:ab
a b \0 d \0
今日の確認+α
char *str3 = "abcd";
メモリ上の何処かに{a,b,c,d,\0}の配列が作られる
その配列の先頭のアドレスがstr3に格納
{a,b,c,d,\0}の書き換えなどの操作は動作未定義
今日の確認+α+β
void func1( char str1[5] ){ ... };
void func2( char str2[] ){...};
void func3( char *str3 ){...};
どう違うか?
今日の確認+α+β
void func1( char str1[5] ){ ... };void func2( char str2[] ){...};void func3( char *str3 ){...};
アドレスを受け取るのはどれも同じ。要素数の指定(一番上)は二次元以上の配列に必要となる。
例: void func4( int ary2[][5] ){...}; void func5( int ary3[][3][5]){...};
一番左の要素数は省略可。