SlideShare ist ein Scribd-Unternehmen logo
1 von 43
Downloaden Sie, um offline zu lesen
ポインタと配列と文字列

  HN:MARISHI



   牛乳おいしいよ>
やること
●   ポインタ
●   ポインタと配列
●   文字列
ポインタとは
●   メモリのアドレスを
    メモリのアドレス     アドレス         メモリの内容
                 0x00000000
    格納する変数       0x00000001
                 0x00000002
                 0x00000003
●   バグの温床        0x00000004
                 0x00000005
                 0x00000006
●   でもこれがないと     0x00000007

    C言語ははじまらない   0x00000008
                 0x00000009
                 0x0000000A
                 0x0000000B
                 0x0000000C
                 0x0000000D
                 0x0000000E
ポインタ・・・の前に
                          アドレス         メモリの内容
                          0x00000000
                          0x00000001 12
int hoge = 12;            0x00000002 (hoge)
char piyo = 'a';          0x00000003
double fuga = 0.123456;   0x00000004
                          0x00000005
                          0x00000006 'a'(piyo)
                          0x00000007 0.123456
32ビット環境では                 0x00000008 (fuga)
int型は4バイト                 0x00000009

char型は1バイト                0x0000000A
                          0x0000000B
double型は8バイト              0x0000000C
                          0x0000000D
                          0x0000000E
ポインタ関連の文法
                       アドレス         メモリの内容
                       0x00000000
                       0x00000001 12
                       0x00000002 (hoge)
int hoge = 12;         0x00000003
                       0x00000004
printf(“%p”,&hoge);   0x00000005
                       0x00000006
//結果:0x00000001        0x00000007
                       0x00000008
                       0x00000009
変数のアドレスを得る             0x0000000A
                       0x0000000B
&変数名                   0x0000000C
                       0x0000000D
                       0x0000000E
ポインタ関連の文法
                 アドレス         メモリの内容
                 0x00000000
int hoge = 12;   0x00000001 12
int *phoge;      0x00000002 (hoge)
                 0x00000003
                 0x00000004
phoge = &hoge;   0x00000005
                 0x00000006 0x00000001
                 0x00000007

ポインタの定義          0x00000008
                 0x00000009
型の名前 *変数の名前;     0x0000000A
                 0x0000000B
/*32ビット環境では      0x0000000C

ポインタは4バイト*/      0x0000000D
                 0x0000000E
ポインタ関連の文法
                         アドレス         メモリの内容
                         0x00000000

int hoge = 12;           0x00000001 12
                         0x00000002 (hoge)
int *phoge;              0x00000003
                         0x00000004
phoge = &hoge;           0x00000005
                         0x00000006 0x00000001
                         0x00000007
printf(“%d” , *phoge);   0x00000008
                         0x00000009
ポインタを通して間接的に             0x0000000A
                         0x0000000B
変数を見る                    0x0000000C
*変数名                     0x0000000D
                         0x0000000E
ダブルポインタの動作
                             アドレス         メモリの内容
int hoge = 22;
                             0x00000000 22
int *phoge;
                             0x00000001 (hoge)
int **pphoge;
                             0x00000002
phoge = &hoge;               0x00000003
pphoge = &phoge;             0x00000004 0x00000000
                             0x00000005 (phoge)
printf("%pn" , phoge );     0x00000006
printf("%pn" , *pphoge );   0x00000007
//結果:0x00000000              0x00000008 0x00000004
                             0x00000009 (pphoge)
printf("%pn" , &phoge );    0x0000000A
printf("%pn" , pphoge );    0x0000000B
//結果:0x00000004              0x0000000C
                             0x0000000D
printf(“%dn”,**pphoge);     0x0000000E
//結果:22
ポインタの例:スワップ
swap1(int a , int b)     int hoge = 5;
{                        int piyo = 10;
  int tmp = b;
  b = a;                 swap1(hoge,piyo);
  a = tmp;               //結果?
}
                         swap2(&hoge,&piyo);
swap2(int *a , int *b)   //結果?
{
  int tmp = *b;
  *b = a;
  *a = tmp;
}
スワップ
swap1(int a , int b)      アドレス         メモリの内容
{                         0x00000000 5

  int tmp = b;            0x00000001 (hoge)
                          0x00000002
  b = a;
                          0x00000003
  a = tmp;                0x00000004 10
}                         0x00000005 (piyo)
...                       0x00000006
                          0x00000007
int hoge = 5;             0x00000008

int piyo = 10;//←今ココ      0x00000009
                          0x0000000A
                          0x0000000B
swap1(hoge,piyo);         0x0000000C
printf(                   0x0000000D
“%d,%d”,hoge,piyo);       0x0000000E
                          0x0000000F
スワップ
swap1(int a , int b)//←今ココ   アドレス         メモリの内容
{                            0x00000000 5
  int tmp = b;               0x00000001 (hoge)
  b = a;                     0x00000002
  a = tmp;                   0x00000003
}                            0x00000004 10
                             0x00000005 (piyo)
...                          0x00000006
                             0x00000007
int hoge = 5;                0x00000008 5
int piyo = 10;               0x00000009 (a)
                             0x0000000A
swap1(hoge,piyo);//←ココの      0x0000000B
printf(“%d,%d”,hoge,piyo);
                             0x0000000C 10
                             0x0000000D (b)
                             0x0000000E
                             0x0000000F
スワップ
swap1(int a , int b)         アドレス         メモリの内容
{                            0x00000000 5
  int tmp = b;               0x00000001 (hoge)
  b = a;                     0x00000002
  a = tmp;//←今ココ             0x00000003
}                            0x00000004 10
                             0x00000005 (piyo)
...                          0x00000006
                             0x00000007
                             0x00000008 10
int hoge = 5;
                             0x00000009 (a)
int piyo = 10;
                             0x0000000A

swap1(hoge,piyo);//←ココの      0x0000000B
printf(“%d,%d”,hoge,piyo);   0x0000000C 5
                             0x0000000D (b)
                             0x0000000E
                             0x0000000F
スワップ
swap1(int a , int b)      アドレス         メモリの内容
{                         0x00000000 5
  int tmp = b;            0x00000001 (hoge)
  b = a;                  0x00000002
                          0x00000003
  a = tmp;
                          0x00000004 10
}
                          0x00000005 (piyo)
...                       0x00000006
                          0x00000007
int hoge = 5;             0x00000008
int piyo = 10;            0x00000009
                          0x0000000A
swap1(hoge,piyo);         0x0000000B
printf(                   0x0000000C
“%d,%d”,hoge,piyo);       0x0000000D
//↑今ココ                    0x0000000E
                          0x0000000F
スワップ
swap2(int *a , int *b)   アドレス         メモリの内容
{                        0x00000000 5

  int tmp = *b;          0x00000001 (hoge)
                         0x00000002
  *b = *a;
                         0x00000003
  *a = tmp;              0x00000004 10
}                        0x00000005 (piyo)
...                      0x00000006
                         0x00000007
int hoge = 5;            0x00000008

int piyo = 10;//←今ココ     0x00000009
                         0x0000000A
                         0x0000000B
swap1(&hoge,&piyo);      0x0000000C
printf(                  0x0000000D
“%d,%d”,hoge,piyo);      0x0000000E
                         0x0000000F
スワップ
swap2(int *a , int *b)//←今ココ   アドレス         メモリの内容
{                              0x00000000 5
  int tmp = *b;                0x00000001 (hoge)

  *b = *a;                     0x00000002
                               0x00000003
  *a = tmp;
                               0x00000004 10
}
                               0x00000005 (piyo)
...                            0x00000006
                               0x00000007
int hoge = 5;                  0x00000008 0x00000000
int piyo = 10;                 0x00000009 (a)
                               0x0000000A
swap1(&hoge,&piyo);//←ココ       0x0000000B
の                              0x0000000C 0x00000004
printf(“%d,%d”,hoge,piyo);     0x0000000D (b)
                               0x0000000E
                               0x0000000F
スワップ
swap2(int *a , int *b)       アドレス         メモリの内容
{                            0x00000000 10
                             0x00000001 (hoge)
  int tmp = *b;
                             0x00000002
  *b = *a;
                             0x00000003
  *a = tmp;//←今ココ            0x00000004 5
}                            0x00000005 (piyo)
...                          0x00000006
                             0x00000007
int hoge = 5;                0x00000008 0x00000000
                             0x00000009 (a)
int piyo = 10;
                             0x0000000A
                             0x0000000B
swap1(&hoge,&piyo);//←ココ     0x0000000C 0x00000004
の                            0x0000000D (b)
printf(“%d,%d”,hoge,piyo);   0x0000000E
                             0x0000000F
スワップ
swap2(int *a , int *b)       アドレス         メモリの内容
{                            0x00000000 10

  int tmp = *b;              0x00000001 (hoge)
                             0x00000002
  *b = *a;
                             0x00000003
  *a = tmp;                  0x00000004 5
}                            0x00000005 (piyo)
...                          0x00000006
                             0x00000007
int hoge = 5;                0x00000008

int piyo = 10;               0x00000009
                             0x0000000A
                             0x0000000B
swap1(&hoge,&piyo);          0x0000000C
printf(“%d,%d”,hoge,piyo);   0x0000000D
//↑今ココ                       0x0000000E
                             0x0000000F
ポインタを使うタイミング
●   動的メモリの管理
●   複数の構造体などから同じデータを参照したい時
●   関数の引数に構造体を利用する時
●   関数の戻り値が2つ以上欲しい時

    etc
動的メモリの確保
int num;
scanf(“%dn”,&num);
int *ary = (int*)malloc( sizeof(int) * n );
複数の「何か」から同じデータを参照
アドレスさえあれば、同じデータを簡単に共有できる。
     int apple_num;//0x00334455


0x00334455            0x00334455

 MARISHI                  kano
複数の「何か」から同じデータを参照
  乱用すると、意図しない値の操作が行われた時に、
  どのプログラムが間違ってるか分かり辛い
      int apple_num;//0x00334455
誰だ5個も食いやがった奴は!

                                0x00334455
0x00334455       0x00334455   *apple_num -= 5
  MARISHI           kano           xALTx




                                       ククク・・・
関数の引数に構造体を使うとき
typedef struct Human
{
   int age;
   int height;
   int weight;
} Human;

//構造体をまるごとコピーして重い。
void print_human(Human h)
{
  printf(“%dn”,sizeof(h) ); //結果:16(環境依存
}

//アドレスのみコピー
void print_human_size_p( Human *ph)
{
  printf(“%dn”,sizeof(ph) ); //結果:4(環境依存
}
関数の戻り値が複数欲しい時
void yanagisawa_info(int *age , int *weight)
{
    *age = 22;
    * weight = …//ヒミツ
}
やること
●   ポインタ
●   ポインタと配列
●   文字列
ポインタと配列
●   ポインタと配列は深い関係ある
●   配列で困ったときはポインタを思い出すと
    納得行くことがあったり無かったり無かったり
●   そして配列死ねよと思う(?)
ポインタと配列の衝撃の事実
●   配列へのアクセスにはポインタも利用できる

    int i;
    int ary[3] = {7,5,3};
    int *p = ary;

    for(i = 0 ; i < 3 ; ++i){
      printf(“%dn” , ary[i] ); //出力結果は
      printf(“%dn” , *(p+i) ); //一緒
    }
配列の先頭
                        アドレス         メモリの内容
int ary[3] = {7,5,3};   0x00000000 7

int *p = ary;           0x00000001
                        0x00000002
                        0x00000003

配列の先頭だけ記述               0x00000004 5
                        0x00000005
↓                       0x00000006
配列の先頭ポインタ               0x00000007
                        0x00000008 3
                        0x00000009
                        0x0000000A
                        0x0000000B
                        0x0000000C 0x00000000
                        0x0000000D
                        0x0000000E
                        0x0000000F
ポインタに整数を足すと
                        アドレス         メモリの内容
int ary[3] = {7,5,3};   0x00000000 7
int *p = ary;
                        0x00000001
                        0x00000002
printf(“%p”,ary);       0x00000003
printf(“%p”,&ary[0]);
                        0x00000004 5
printf(“%p”,p);
//一緒                    0x00000005
                        0x00000006
printf(“%p”,&ary[2]);   0x00000007
printf(“%p”,p+2);       0x00000008 3
//一緒                    0x00000009
                        0x0000000A
++p;                    0x0000000B
printf(“%p”,&ary[1]);
printf(“%p”,p);         0x0000000C 0x00000000
//一緒                    0x0000000D
                        0x0000000E
                        0x0000000F
配列を関数の引数にする
//配列の先頭のポインタが渡される!
void func( int a[])
{
  printf("%pn", a );//出力:0x000000ff
  a[2] = 0;
}

///

int ary[] = {1,2,3};
printf("%p", ary );//出力:0x000000ff
func(ary);
//ポインタを渡したので、関数の書き換えが反映される
printf("%dn",ary[2]);//出力:0
配列を関数の引数にする(応用)
//配列の先頭のポインタを渡すので、これでもいい
void func2(int *a)
{
  *(a+2) = 0;
  //a[2] = 0;//1次元の場合、これでもよい。
}

///
int ary[] = {1,2,3};
func(ary);
printf("%dn",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次元配列についての罠
                                       アドレス           メモリの内容
●   要素数がある程度分からないと                     0x00000000
    要素の参照に困る                           0x00000001 'a' ( [0][0] )
                                       0x00000002 'b' ( [0][1] )
    char hoge[][3] = {{'a','b','c'},   0x00000003 'c' ( [0][2] )
                     {'d','e','f'}};   0x00000004 'd' ( [1][0] )
                                       0x00000005 'e' ( [1][2] )
    //配列の最初を参照すればOK                    0x00000006 'f' ( [1][3] )
    hoge[0][0] = 'e';                  0x00000007
                                       0x00000008
    //配列の最初から3バイト先を                    0x00000009
    //参照しなくてはいけない                      0x0000000A
    hoge[1][0] = 'f';                  0x0000000B
                                       0x0000000C
    先ほどのポインタは、何バイト先を                   0x0000000D
    読み込めばいいか分からなかった。                   0x0000000E
    よってエラー
やること
●   ポインタ
●   ポインタと配列
●   文字列
文字列
●   文字列はchar型の配列として扱うことができる
●   文字列の最後にはNULL文字がある。

    char str1[5] = "abcd";
    char str2[5] = {'a','b','c','d','0'};

    printf("%sn",str1);
    printf("%sn",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("%sn",str1);
    //出力:ab

              a     b      0   d   0
今日の確認+α
char str1[5] = "abcd";
char str2[] = "abcd";
char *str3 = "abcd";

どう違うか?
今日の確認+α
char str1[5] = "abcd";
char str2[] = "abcd";

どちらも同じ。char型の配列、要素数5




          a    b     c   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]){...};

一番左の要素数は省略可。
最後に
●   ポインタと配列の深い関係についてやったけど、
    「ポインタと配列は一緒」とか訳のわからない
    事をいう大人にはならないように

●   printf("%cn" , "abcde"[3] );

    きもちわるいぃぃぃぃぃいいいい

Weitere ähnliche Inhalte

Andere mochten auch

Moodle - Intro
Moodle - IntroMoodle - Intro
Moodle - Introbillydekid
 
Defesa Campanha Central de Transplante
Defesa Campanha Central de TransplanteDefesa Campanha Central de Transplante
Defesa Campanha Central de Transplanterodrigo
 
Kunskapsbaren 2011 Stockholm - Scala och andra nya språk i JVM:en
Kunskapsbaren 2011 Stockholm - Scala och andra nya språk i JVM:enKunskapsbaren 2011 Stockholm - Scala och andra nya språk i JVM:en
Kunskapsbaren 2011 Stockholm - Scala och andra nya språk i JVM:enHiQInternational
 

Andere mochten auch (9)

Moodle - Intro
Moodle - IntroMoodle - Intro
Moodle - Intro
 
Verde mais verde
Verde mais verdeVerde mais verde
Verde mais verde
 
Defesa Campanha Central de Transplante
Defesa Campanha Central de TransplanteDefesa Campanha Central de Transplante
Defesa Campanha Central de Transplante
 
Kunskapsbaren 2011 Stockholm - Scala och andra nya språk i JVM:en
Kunskapsbaren 2011 Stockholm - Scala och andra nya språk i JVM:enKunskapsbaren 2011 Stockholm - Scala och andra nya språk i JVM:en
Kunskapsbaren 2011 Stockholm - Scala och andra nya språk i JVM:en
 
Jornadas navidenas
Jornadas navidenasJornadas navidenas
Jornadas navidenas
 
TODO EN LA RED
TODO EN LA REDTODO EN LA RED
TODO EN LA RED
 
Presentación once
Presentación oncePresentación once
Presentación once
 
El tenis
El tenisEl tenis
El tenis
 
Viatge a roma
Viatge a romaViatge a roma
Viatge a roma
 

Tora pointer3

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