Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
C言語ポインタ講座
@kakira9618
2017/11/12
この資料について
 この資料は、@kakira9618が所属している
サークルでの講座『C言語ポインタ講座』
で使用していた資料を、一部修正・加筆し
て、一般公開したものです。
 質問、指摘などは@kakira9618まで。
– https...
この講座の対象
3
1 ポインタの基本的な使い方を知っている
ポインタ変数の宣言の仕方と使い方
ポインタを使ったプログラムが一応書ける
2 動作原理、記法がよく分かっていない
ポインタ変数が派生型である理由を説明できない
配列の要素へのアクセス...
ポインタのポイント
これらを把握すればポインタは理解できる!
4
1 原理
ポインタがどのようにしてメモリ上の情報を
読んでいるかを理解する。
2 記法
ポインタの記法を整理する。
3 使い方
ポインタを使うと効率よく書ける場面を把握。
お約束
 この資料では、以下の環境を仮定します
– sizeof(int) = 4
– sizeof(short int) = 2
– sizeof(float) = 4
– sizeof(double) = 8
– sizeof(ポインタ型...
注意
 この資料中に掲載しているプログラムは
ポインタの文法や原理、使い方の解説をす
るために掲載しているものです。
 そのため、実務のプログラミングでそのま
ま使うと怒られるようなコード、もしくは
他の方法を使ったほうが良いようなコード
...
目次
7
記法
使い方
原理
ポインタの指す”幅”を意識する
ポインタはただ場所を保存するだけじゃない!
8
質問です
9
Q. C言語におけるポインタ変数とは?
質問です
10
Q. C言語におけるポインタ変数とは?
アドレスを保存する変数!
確かにその通り。
だけど、理解するにはもう一歩。
アドレスとポインタ変数の違い
アドレス
 ポインタ変数の値
 メモリ上の位置を示す
番号のこと
– 0x12345678とか
ポインタ変数
 アドレスを格納する変数
– 格納するアドレスを自由に
変更できる
11
int *p = &in...
さらに質問
12
Q. &演算子(1項演算子)で計算されるものは?
さらに質問
13
Q. 式中の&演算子(1項演算子)で計算されるものは?
その変数のアドレス!
その変数のアドレス、ですね。
だけど、理解するにはもう一歩。
う~ん... 論理積かな?
&演算子
14
 &を変数の前につけると、
が得られる
変数の先頭アドレス
図解
 int value;
 &value
15
value
value
ここのアドレスが得られる
= 1byte
参照
 参照するときは、ポインタ変数の前に*を
つけます。
 これで、pの記憶していたアドレスに書い
てある内容を取り出すわけですが・・・
16
int *p = &intValue;
printf(“%d”, *p);
重要な考察
 int *p = &intValue; では、pがintValueの
先頭アドレスで初期化される。
 *pで、pのアドレスの内容を取り出す
値を取り出すとき、
どこから取り出せば良いのかは分かる。
どこまで取り出せばよいのかは...
図解
 int *p = &value;
 *p
18
valuep
p
どこまで読めば良いの??= 1byte
p
変数pに のアドレスが入っていることを意味注意: =
重要な考察
19
&のときに「valueの」という
情報も保存してるのでは?
→まちがい。pはアドレスしか保存していません。
ポインタ自身の型から推測?
→せいかい!
重要な考察
 int *p; としたとき、
 *pとすると、pに記憶されているアドレス
から、int型分だけ読み込む
20
pは int型への ポインタ変数
図解
 int *p = &value;
 *p
21
valuep
p int型分
「int型分」はpの宣言「int *p」から。
= 1byte
ポインタ演算
 p++; や、p--; とするとポインタに保存さ
れているアドレスが1単位動く
 アドレスがいつも1byte動くわけではない
 ポインタ変数の宣言から、参照元の型を
見て、その型の大きさを1単位とする。
22
図解
 int型へのポインタpの場合:
23
p int型分
p int型分
p++; int型分だけずれる
= 1byte
図解
 short int型へのポインタpの場合:
24
p short int
型分
p
p++;
short int型分だけずれる
short int
型分
= 1byte
ここまでのまとめ
 ポインタはアドレスを保存する変数
 値を参照するときに、自身の型から参照すべ
き幅を計算
 ポインタ演算(++, --など)をするときも、
自身の型から増やすべき値を計算。
 アドレスにも型があり、同じように計算する...
メモリへの情報格納方法
同じビット列でも格納方法・型が違えば違う値!
26
データの格納順(バイトオーダー)
 情報をどういう順番で格納していくか
 大きく分けて2つ
27
1 ビックエンディアン
上位バイトを上位バイト、
下位バイトを下位バイトに格納
2 リトルエンディアン
上位バイトを下位バイト、
下位バイトを...
ビックエンディアン
 0x12345678(0xは16進数を表す)を
ビックエンディアンで格納するとき:
28
0 x 1 2 3 4 5 6 7 8
0x12 0x34 0x56 0x78
上位 下位
格納
ソース上
メモリ上
= 1byte
ビックエンディアン
 0x12345678(0xは16進数を表す)を
ビックエンディアンで取り出すとき:
29
0 x 1 2 3 4 5 6 7 8
0x12 0x34 0x56 0x78
上位 下位
取出
ソース上
メモリ上
= 1byte
リトルエンディアン
 0x12345678(0xは16進数を表す)を
リトルエンディアンで格納するとき:
30
0 x 1 2 3 4 5 6 7 8
0x78 0x56 0x34 0x12
上位 下位
格納
ソース上
メモリ上
= 1byte
リトルエンディアン
 0x12345678(0xは16進数を表す)を
リトルエンディアンで取り出すとき:
31
0 x 1 2 3 4 5 6 7 8
0x78 0x56 0x34 0x12
上位 下位
取出
ソース上
メモリ上
= 1byte
float(double)型の格納方法
 IEEE 754の規格に基づく。
 32bitを3つの領域に分ける
– 符号部分(1bit):+なら0, -なら1
– 指数部分(8bit) :基数部分を2の何乗するか
– 基数部分(23bit) ...
float (double)型の格納方法
33
0 x C 0 F 8 0 0 0 0
=1bit
0xC 0x0 0xF 0x8 0x0
1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0
※簡単化のため、バイトオ...
float (double)型の格納方法
34
0 x C 0 F 8 0 0 0 0
0xC 0x0 0xF 0x8 0x0
1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0
符
号
指数 基数
=1bit
float (double)型の格納方法
35
0 x C 0 F 8 0 0 0 0
=1bit
0xC 0x0 0xF 0x8 0x0
1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0
符
号
指数
(129)...
型と数値
36
0 x C 0 F 8 0 0 0 0
0xC 0x0 0xF 0x8 0x0
1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0
 このビット列をunsigned int型だと思うと
– ビックエ...
型と数値
 このビット列をint型だと思うと
– ビックエンディアンなら
• 0xC0F80000 = -1057488896 という整数
– リトルエンディアンなら
• 0x0000F8C0 = 63680という整数
37
0 x C 0 ...
型と数値
 このビット列をfloat型だと思うと
– ビックエンディアンなら
• 0xC0F80000 = -7.75という数値
– リトルエンディアンなら
• 0x0000F8C0 = 0.0という数値
38
0 x C 0 F 8 0 0...
型と数値
 0xC0F80000が格納されている領域の
先頭アドレスがポインタに代入されている
39
unsigned int a = 0xC0F80000;
unsigned int *p1 = &a;
int *p2 = (int *)&...
型と数値
 0xC0F80000が格納されている領域の
先頭アドレスがポインタに代入されている
 このとき、保持しているポインタの型に
よって、読みだした結果が異なる!
40
unsigned int a = 0xC0F80000;
uns...
型
 つまり、C言語における型は
を表す
 アドレスのみを保存するポインタ変数は、
指し示す先の型の情報が無いとメモリに
アクセスできない!
41
メモリ上のデータの格納方式
メモリ上のデータの読込方式
普通の変数と配列の(メモリ上の)違い
普通の変数
 別個に変数を定義
– int a, b, c;
 各変数が連続した領域に
置かれるとは限らない
配列
 配列で定義
– int a[3];
 各要素は連続した領域に
必ず置かれる。
42
図解
43
 int a, b, c;
a c b
 int a[3];
a[0] a[1] a[2]
必ず連続
連続とは限らない
= 1byte
ここまでのまとめ
 ビット列が同じでも、格納方法や型が違えば違う値!
44
0 x C 0 F 8 0 0 0 0
3237478400
63680 -7.75
-1057488896
0.0
ここまでのまとめ
 配列は連続した領域に置かれる
45
a[0] a[1] a[2]
必ず連続
int a[3];
= 1byte
配列のアクセスの原理を理解する
連続した領域+ポインタ=ランダムアクセス
46
覚えるべき文法(1)
 配列名だけを書いたとき、
– sizeofや&の対象ならば、配列全体を意味
– それ以外ならば、配列の先頭要素のアドレス
47
int a[5] = {1, 2, 3, 4, 5};
printf(“%un”, siz...
覚えるべき文法(2)
 配列名(sizeofや&の対象の場合を除く)を書いたと
きに得られるアドレスの型は、(配列要素の型)へ
のポインタ型。
48
int a[4]; なら、aの型は、int *
float a[2]; なら、aの型は、fl...
覚えるべき文法(3)
 配列の糖衣構文(シンタックスシュガー)
49
array[i] = *(array + i)
 この2つの表記は完全に同じ意味
 配列の宣言の時の[]は別物。
– この[]は、配列を使うときの[]。
配列アクセスを理解
50
 int a[3] = {1, 2, 3};
a[0] a[1]
必ず連続
1 2
a[2]
3
= 1byte
配列アクセスを理解
 a[2]
51
a[0] a[1]
必ず連続
1 2
a[2]
3
= 1byte
配列アクセスを理解
 *(a + 2)
52
a[0] a[1]
必ず連続
1 2
a[2]
3
= 1byte
配列アクセスを理解
 *(a + 2)
53
a[0] a[1]
必ず連続
1 2
int *
幅はint
a int
a[2]
3
= 1byte
配列アクセスを理解
 *(a + 2)
54
a[0] a[1]
1 2
int *
幅はint
a + 2
a[2]
3
int
+2すると、
sizeof(int) * 2 = 8 byte分進む
8byte
= 1byte
配列アクセスを理解
 *(a + 2)
55
a[0] a[1]
1 2
a + 2
a[2]
3
int
この領域をint型で
読み取り
⇒3が読みだされる
= 1byte
ポインタを使っても同じ!
 int a[3] = {1, 2, 3};
 int *p = a; //ポインタ変数にアドレス代入
 printf(“%dn”, p[2]);
56
a[0] a[1]
1 2
p + 2
a[2]
3
in...
動的配列
(実行時に要素数を指定できる配列)
 #include <stdlib.h>をして使える、malloc
– memory allocation (メモリ確保)
 malloc(確保したいバイト数);
– とすると、連続した確保した...
動的配列(図解)
58
 malloc(8);
とりあえず8byte分だけ確保
返り値(アドレス)
= 1byte
動的配列(図解)
59
 int *p = (int *)malloc(8);
返り値をpへ
p
p[0] p[1]
配列の記法で
領域を扱える
= 1byte
動的配列(図解)
60
 short int *p = (short int *)malloc(8);
返り値をpへ
p
p[0] p[1]
配列の記法で
領域を扱える
p[2] p[3]
= 1byte
動的配列
 環境によってintやshort intなどの型の大き
さは変わる
 引数には sizeof(型)*欲しい要素数を入れる
61
int *p = (int *)malloc(sizeof(int) * 2);
呪文(?)の完成!
配列の配列
 二次元配列の正体は、配列の配列
– つまり、配列の要素として配列を含んでいる
 char a[2][3]; のとき、aはchar型の配列(要
素数3)を要素とする、要素数2の配列。
 aのアドレスの型は、 char型の配列(...
配列の配列(図解)
 char a[2][3] = {{1,2,3}, {4,5,6}};
63
a
1 2 3 4 5 6
 a[1]
a+1
1 2 3 4 5 6
=*(a + 1)
*(a + 1)…char [3](配列)
= 1...
配列の配列(図解)
64
 *(a+1)はchar [3](配列)だが、次に+2という演算が控えているため
先頭要素へのアドレスに読み替えられる
a+1
1 2 3 4 5 6
*(a + 1)…char [3](配列)
*(a + 1)…c...
配列の配列(図解)
65
 a[1][2]
*(a+1)…char *
1 2 3 4 5 6
*(a+1)+2…char *
 a[1][2] = *(*(a + 1) + 2)
⇒6が読みだされる
= 1byte
配列へのポインタ
 途中で登場した、「char型の配列」へのポ
インタの定義がこちら。
 間違えても、char *p[5]; と勘違いしてはい
けない。
– こちらは、char *が5つ格納されている配列 66
char (*名前)[配列の...
ここまでのまとめ
 配列の要素は、連続した領域に置かれる
 配列名は、(基本的に)先頭要素へのアドレ
ス
– このアドレスの型は、配列の要素の型へのポイン
タ型
 このアドレスをポインタ演算したり、ポイン
タ変数に入れたりして配列っぽく使...
理解度チェック
 バイトオーダーはリトルエンディアンとします。
次のア~カの行で表示される数値は?(次ページに補足有)
68
int arr[] = {0x01234567, 0x89abcdef, 0xc0f80000};
int *p1 ...
理解度チェック(補足)
 バイトオーダーはリトルエンディアンとします。
次のア~カの行で表示される数値は?
69
int arr[] = {0x01234567, 0x89abcdef, 0xc0f80000};
int *p1 = arr;...
お約束(再掲)
 この資料では、以下の環境を仮定します
– sizeof(int) = 4
– sizeof(short int) = 2
– sizeof(float) = 4
– sizeof(double) = 8
– sizeof(ポ...
理解度チェック(ヒント)
 迷ったらメモリの図を書こう!
71
0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x??
arr[0] arr[1] arr[2] = 1byte
...
理解度チェック(答え)
 ア: 12
 イ: 4
 ウ: 0x89abcdef
 エ: 0x45
 オ: -7.75
 カ: 0xcdef0123
72
理解度チェック(解説:前提)
 int arr[] = {0x01234567, 0x89abcdef,
0xc0f80000};
 のメモリ上の姿を書くと……
73
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0...
理解度チェック(解説:ア)
 printf(“%un”, sizeof(arr));
74
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] a...
理解度チェック(解説:イ①)
 char *p2 = (char *)arr;
75
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] arr[...
理解度チェック(解説:イ②)
 printf(“%un”, sizeof(p2));
76
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] a...
理解度チェック(解説:ウ①)
 int *p1 = arr;
77
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] arr[2] = 1byt...
理解度チェック(解説:ウ②)
 p1[1] は *(p1 + 1)
78
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] arr[2] = 1...
理解度チェック(解説:エ)
 p2[1]は *(p2 + 1)
79
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] arr[2] = 1by...
理解度チェック(解説:オ①)
 float *p3 = (float *)arr;
80
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] ar...
理解度チェック(解説:オ②)
 p3[2] は *(p3 + 2)
81
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] arr[2] = 1...
理解度チェック(解説:オ③)
82
 0xc0f80000は2進数で……
1100 0000 1111 1000 0000 0000 0000 0000
0xc 0x0 0xf 0x8 0x0 0x0 0x0 0x0
 IEEE754で読む...
理解度チェック(解説:カ①)
 *(int *)(p2 + 2)
83
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] arr[2] = 1b...
理解度チェック(解説:カ②)
 *(int *)(p2 + 2)
84
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] arr[2] = 1b...
理解度チェック(解説:カ③)
 *(int *)(p2 + 2)
85
0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0
arr[0] arr[1] arr[2] = 1b...
関数の引数にポインタを渡す
間接的なアクセス方法
86
関数にポインタを渡す
 ここでは原理のみ。表記の問題等は後で
87
void someFunc(int *p) {
printf(“%dn”, *p);
}
int main(void) {
int value = 3;
int *pValu...
メモリマップ
 メモリは、使用用途によって領域が分かれている。
 静的領域…プログラムの開始から終了まで読書可
 定数領域…絶対に書き換えない。プログラムの開始
から終了まで使える。読込のみ可。
 スタック領域… 関数ごとの領域。その関...
メモリマップ(図解)
89
int global = 3;
void func2() {
printf(“Hellon”);
}
void func1(int a) {
int b = 1;
func2();
printf(“%d,%dn”, ...
メモリマップ(図解)
90
global 静的
“Hellon”
“%d,%dn”
定数
a
b
スタック(func2)
スタック(func1)
スタック(main)
メモリ
ずっと使える。
書き換え可
ずっと使える。
書き換え不可
関数が始ま...
関数に値を渡す
 関数に値を渡すと、その値は渡した先に
コピーされる
– ポインタの値だろうがコピーされます。
91
void someFunc(int *p) {
printf(“%dn”, *p);
}
int main(void) {
...
関数に値を渡す
 渡したものがポインタだと、関数外の領域
にアクセスできるようになる。
92
void someFunc(int *p) {
printf(“%dn”, *p);
}
int main(void) {
int value = ...
イメージ
 webにアップロードした写真のURLを友達
に教える
– URL自身はコピーされている
– 写真はコピーされない
– 友達も写真にアクセスできる。
93
http://xxx.com/yyy.jpg
http://xxx.com/...
ダメな参照
 呼び出し元と呼び出し先の関数で値をやり
取りする際の注意
 呼び出し先関数内の領域を指すポインタを
呼び出し元で使ってはいけない!!
– 呼び出し元で使うときには、呼び出し先の関
数の実行は終わっている
– メモリマップ的にア...
ダメな参照(例)
95
int *someFunc() {
int value = 3;
return &value;
}
int main(void) {
int *p = someFunc();
printf(“%dn”, *p);
ret...
ダメな参照(イメージ)
96
 webにアップロードした写真のURLを友達
に教える
 何故か教えた瞬間にうp主が写真を削除
①http://xxx.com/yyy.jpg
http://xxx.com/yyy.jpg
②削除 ③アクセス
...
良い参照
 関数内で作った値へのポインタを返したいときは、
その領域を動的確保します。
 動的確保された領域は、ヒープ領域に入るため、
明示的に開放しない限りメモリ上に残ります。
97
int *someFunc() {
int *p = ...
関数にポインタを渡すメリット
98
1 コピーコスト削減
アドレスしかコピーしないので、
コピーする量・時間が減る
2 データの一貫性保持
データを更新すればその変更が向こうにも伝わる
3 複数の値を返す関数
複数のアドレスを渡しておけば、
複...
ここまでのまとめ
 関数にポインタを渡すことができる
– 渡された値はポインタであろうとコピーされる
– 呼び出し先から呼び出し元の変数を参照できる
 呼び出し元から呼び出し先の変数の参照を
するときは注意。
– 動的確保した領域へのポイン...
文字列
文字の配列
100
文字と文字列
文字
 通常、‘A’や’b’とかをchar
型変数に格納。
– 実際に格納されるのは文字
に対するアスキーコード
(数値)
 1文字のみ表す
文字列
 「文字」が配列になった
もの
– char str[3] = {‘a’,...
文字列(図解)
102
 char str[3] = {‘a’, ‘b’, ‘0’};
str
‘a’ ‘b’ ‘0’
 printf(“%s”, str);
str
‘a’ ‘b’ ‘0’
strから’0’のアドレスまで表示
“ab”
=...
文字列(図解)
103
 char str[3] = {‘a’, ‘b’, ‘0’};
 printf(“%s”, str); p = str
‘a’ ‘b’ ‘0’
char *p = str;
while(*p != ‘0’) {
pr...
文字列とポインタ
 配列の先頭をポインタに格納すれば、
ポインタで文字列を扱うこともできる
104
char str[3] = {‘a’, ‘b’, ‘0’};
char *str_p = str;
printf(“%s”, str_p);
...
ややこしい表記
105
char str[3] = {‘a’, ‘b’, ‘0’};
char str[] = {‘a’, ‘b’, ‘0’};
char str[] = “ab”;
文字列のために
用意された文法
(シンタックス
シュガー)
...
ややこしい表記
106
char str[] = “ab”;
char *str = “ab”;
図解
 char str[] = “ab”;
 char *str = “ab”;
107
ローカル‘a’ ‘b’ ‘0’
str(コンパイル時定数)
定数‘a’ ‘b’ ‘0’
str
ローカル
= 1byte
char *の配列とcharの2次元配列
char *の配列
 char *a[] = {“ab”, “cde”};
 スタックに各要素(ポインタ)
– ポインタの指す場所は変更可能
 定数域にデータ
– ポインタの指すデータは変更不可能...
図解
 char *a[] = {“ab”, “cde”};
109
定数‘a’ ‘b’ ‘0’
a[0]
ローカル
a[1]
‘c’ ‘d’ ‘e’ ‘0’
a…char *へのポインタ
※a[0], a[1]はchar *
= 1byte
図解
 char a[2][4] = {“ab”, “cde”};
110
ローカル‘a’ ‘b’ ‘0’ ‘c’ ‘d’ ‘e’ ‘0’
a…char [4]へのポインタ
※a[0], a[1]はchar [4](配列)だが、
次の演算がs...
char *の配列とcharの2次元配列(共通点)
 共通点は、どちらもa[i]がchar *となること。
– しかもそのポインタは文字列の先頭を指す
– 両方とも、文字列の配列として利用できる
 さらに、a[i][j]と書くと、i個目の文...
ここまでのまとめ
 文字列はchar の配列である。
 文字列の最後に番兵として’0’を置く
 char str[] = {‘a’, ‘b’, ‘0’} は char str[] = “ab”;
と同じ意味
– これもシンタックスシュガー...
構造体へのポインタ
基本がわかっていれば怖くないけど、応用例が多い
113
構造体
 いろいろな型を並べて新しい型を作れる
114
typedef struct {
char name[10];
int age;
} person;
 person型はchar[10]とintを並べた型
構造体
 構造体を使う
115
typedef struct {
char name[10];
int age;
} person;
…
person john = {“John”, 21};
printf(“%s %dn”, john.na...
メモリ上の構造体
 構造体の宣言は、メモリ上のレイアウトを決めている
116
typedef struct {
char name[10];
int age;
} person;
name age
0 10index
= 1byte
メモリ上の構造体
 メンバ変数とindex(相対位置)を紐づけている
117
typedef struct {
char name[10]; //開始indexは0
int age; //開始indexは10
} person;
name a...
アライメント
 構造体は配列とは違い、必ずしもその中身が連続
するとは限らない
 OS・CPUが扱いやすい単位に揃えられてデータの
レイアウトが決まることがある
 扱いやすい単位をアライメントという。
 charのアライメントは1byt...
構造体へのsizeof
 レイアウトの結果定まった、構造体全体のサイズ
を返す。
– 正確には、この構造体を配列に入れて並べたときの
隣り合う2要素間のアドレスの差をbyteで返す。
 アライメントの関係で、sizeof(構造体)がsize...
構造体へのsizeof
120
sizeof(person) = 16
0 12index
 レイアウトの結果定まった、構造体全体のサイズ
を返す。
– 正確には、この構造体を配列に入れて並べたときの
隣り合う2要素間のアドレスの差をbyte...
構造体へのポインタ
 構造体へのポインタを定義して使う
 person *p = &john;
 printf(“%s %dn”, p->name, p->age);
 ポインタからメンバ変数にアクセスする時
は.ではなく->を使う。
...
構造体を関数に渡す
 構造体はデータの塊。これを関数に渡すと
データがコピーされる。
 巨大な構造体だとコピーするのに
時間とメモリ領域が消費される。
 ポインタを渡せば、一瞬でデータを渡せる。
 ただし、データを書き換えると
その変更...
構造体を関数に渡す
123
John
person *p = &john;
p
呼び出し元関数
func関数
120
= 1byte
構造体を関数に渡す
124
John
person *p = &john;
p
呼び出し元関数
func関数
func(p);
↑ 4バイトだけコピー
120
p
= 1byte
構造体を関数に渡す
125
John
person *p = &john;
p
呼び出し元関数
func関数
func(p);
p->age = 14;
12 書き換え
0
ageの
indexは12
p
= 1byte
構造体へのポインタまとめ
 複数の型を融合して新しい型を定義できる
 構造体の宣言はメモリ上のレイアウトを決
めている
 メンバ変数がメモリ上の相対位置を記憶
 アライメントに注意。
 巨大な構造体でも、ポインタを経由して他
の関数に...
関数ポインタ
とにかく記法が難しい。使い方は難しくない。
関数を持ち運べる便利な変数!
127
まずは関数ポインタ
128
 関数名も実はアドレス
 アドレスの型として実際に使うときの情報
つまり、「引数の型・順番」「返り値の型」
が分かれば良い。
char func(char *p, int n);
返り値の型と
引数の型・順番が同...
関数ポインタ
 関数を格納するためのポインタpfuncの宣言
 関数を持ち運べる便利な変数と考えると良い
129
char (*pfunc)(char *, int);
 何が変わったのか?
– かっこを左にもつけた
• ポインタであると...
関数ポインタの使い方
130
char func1(char *p, int n) {
return p[n];
}
char func2(char *p, int n) {
return *p + n;
}
int main(void) {
...
関数ポインタの配列
131
char (*pfunc[3])(char *, int);
 関数を格納するためのポインタ配列の宣言
 何が変わったのか
– 変数名の右に配列を表す[]がついた
関数ポインタの使い方
132
char func1(char *p, int n) {
return p[n];
}
char func2(char *p, int n) {
return *p + n;
}
int main(void) {
...
関数ポインタを引数に取る
 関数ポインタは関数の引数にもできます
– すごく記述が複雑になるが、落ち着いてみれ
ばOK
 関数の引数として関数を与えられる
– 関数内から、与えられた関数を呼べる
– コールバック関数
133
int fun...
関数ポインタを返り値にする
134
char (*func(int n))(char *, int) {
…
}
 返り値なのに、左右から挟み込む新発想!
– C言語の規格なので仕方ない。
 後で処理してほしい関数などを返せる
目次
135
記法
使い方
原理
表記は同じだけど意味が違う
混同しやすいので注意
136
*
 *は3つの意味を持つ。
137
1 ポインタの宣言の*
int *p; など、「自分はポインタである」ことを
示すための*。関数の引数の*もこっちの意味。
2 ポインタ参照の*
ポインタ(またはアドレス)の前につける*。
そのアドレスの...
[]
 []も3種類。
138
1 配列の定義の[]
配列を定義するときに使う[]。
配列の要素数を表す。
2 配列の要素の参照の[]
配列の要素を参照するときに使う[]。
*(array + i) = array[i]と言い換えられる。
3...
“”
 “”は2種類。
139
1 文字列リテラルの””
定数領域に格納される文字のデータを表す””
ほとんどの””はこちらに分類される。
型としては、char []となる。
2 charの配列を初期化するための””
char str[] =...
配列名
140
1 配列の先頭要素へのポインタ
ほとんどの配列名はこちらに分類される。
type型の配列の場合、配列名を書くとtype *型となる
2 配列全体
sizeof(配列)または、&配列で使われたとき。
前者は配列全体の大きさ(byt...
表記は違うけど意味が同じ
こっちも混同しやすいので注意
141
関数の引数
 関数引数の宣言
142
void func(int *p) {
}
void func(int p[]) {
}
関数の引数
 どうして?
 ⇒関数に渡すときに、元が配列であるという情報
はどのみち消える。呼び出し元が配列であったこ
とを明示したい!
143
void func(int *p) {
…p[i]…
}
void func(int p[])...
関数の引数
 関数引数の宣言
144
void func(int **p) {
}
void func(int *p[]) {
}
Q. sizeof(p)は?
⇒ 4
関数の引数
 関数引数の宣言
145
void func(int (*p)[3]) {
}
void func(int p[][3]) {
}
Q. sizeof(p)は?
⇒ 4
配列のルール
146
a[i]
*(a + i)
もちろん、
参照時の[]
(式の中の[])
文字の配列
147
char str[3] = {‘a’, ‘b’, ‘0’};
char str[] = {‘a’, ‘b’, ‘0’};
char str[] = “ab”;
文字列のた
めに用意さ
れた文法
配列のルール
const
 int i = 1; // 普通の変数
 const int ic = 1; // 書換不可能
 const int *p1 = &ic; // p1は書換可能。p1
の指す先は書換不可能
 int * const p2 ...
複雑な記法
英訳→和訳
149
複雑な記法
 char (*p)[3];
 char (*func)(char *, int);
 char (*func(int n))(char *, int) {
 …
 複雑な記法が多い。
 どうにかして解読できないか?
1...
まず英訳
151
1 中心の名前を見つける
変数や変数の名前。
真ん中にあることが多い。
見つかったら(名前) is をつける。
2 )に突き当たるまで右に読む
この時、[にあたったら array[要素数] of をつける
(にあたったらfun...
char (*p)[3]
152
1 中心の名前を見つける
p
p is
char (*p)[3];
なう
char (*p)[3]
153
2 )に突き当たるまで右に読む
すでに突き当たっている
char (*p)[3];
p is
なう
char (*p)[3]
154
3 突き当たった)に対応する(まで戻る
戻るときに、*に当たるので、
pointer to をつける
char (*p)[3];
p is pointer to
なう
char (*p)[3]
155
2 )に突き当たるまで右に読む
[が見える。array[3] of をつける
char (*p)[3];
p is pointer to array[3] of
なう
char (*p)[3]
156
3 突き当たった)に対応する(まで戻る
一番最後に)がいると思って、
まだ読んでいないところまで左に戻ると
char.
char (*p)[3];
p is pointer to array[3] of cha...
char (*(*p)())[5];
157
1 中心の名前を見つける
p
p is
char (*(*p)())[5];
なう
char (*(*p)())[5];
158
p is
char (*(*p)())[5];
なう
2 )に突き当たるまで右に読む
すでに突き当たっている
char (*(*p)())[5];
159
p is pointer to
char (*(*p)())[5];
なう
3 突き当たった)に対応する(まで戻る
戻るときに、*に当たるので、
pointer to をつける
char (*(*p)())[5];
160
p is pointer to function(void) returning
char (*(*p)())[5];
なう
2 )に突き当たるまで右に読む
途中で(に出会う。
function(v...
char (*(*p)())[5];
161
p is pointer to function(void) returning
char (*(*p)())[5];
なう
2 )に突き当たるまで右に読む
突き当たった
char (*(*p)())[5];
162
p is pointer to function(void) returning
pointer to
char (*(*p)())[5];
なう
3 突き当たった)に対応する(まで戻る
戻るときに...
char (*(*p)())[5];
163
p is pointer to function(void) returning
pointer to array[5] of
char (*(*p)())[5];
なう
2 )に突き当たるまで右に...
char (*(*p)())[5];
164
p is pointer to function(void) returning
pointer to array[5] of char.
char (*(*p)())[5];
なう
3 突き当たっ...
char (*(*p)())[5];
165
p is pointer to function(void) returning
pointer to array[5] of char.
char (*(*p)())[5];
p は「char[5...
目次
166
記法
使い方
原理
ポインタの使い方(1)
 代表的な使い方
167
1 関数間で構造体を渡す
データの塊を渡す。
ポインタを通してデータにアクセス
2 構造体をつなげる
構造体が他の構造体を知ることが
できる。リスト構造とかもこれ
3 文字列を扱う
先頭アドレ...
ポインタの使い方(2)
 その他の使い方
168
1 動的配列
動的に確保した領域の先頭を指すポインタ
C++では配列だけではなく、
オブジェクトも動的にとれる
2 コールバック処理の実現
関数内の一部処理を外部に決めてもらう
3 メモリをい...
関数間で構造体を渡す
 構造体(データの塊)を渡すには、アドレ
スだけ渡せば十分なことが多い。
169
typedef struct {
char datatype;
int data[10000];
} Bigdata;
void data...
構造体をつなげる
 リスト構造など。定義中の構造体へのポイン
タを、その構造体のメンバ変数に追加する。
170
struct node_{
int value;
struct node_ *p;
};
typedef struct node_...
文字列を扱う
 文字列は文字の配列(の終端に’0’が付加さ
れたもの)
 文字列の先頭文字へのアドレスを捕まえてお
けば、文字列全体を持っていることになる
171
void func(char *p) {
(pを使った処理)
}
int m...
動的配列
 呼び出し元で配列の大きさを決定できる
 型宣言・キャストの仕方によってその領域を使う
ときの型を決められる
172
int *p = (int *)malloc(sizeof(int) * 2);
p
p[0] p[1]
cha...
コールバック処理の実現
 処理の一部を呼び出し側に決定させる
 qsort関数など(要素の大小の定義は外部で定義する)
 イベント駆動プログラミングにも最適
173
typedef struct {
int w, h;
} Rect;
i...
メモリをいじる
 char *は1byte単位でのメモリ操作が可能
 組み込み等のプログラミングでは良く出てくる
174
char *p = (char *)0x12345678; // 整数をアドレスに変換
*p = 0xff;
…
 ...
参考文献
 『 C言語ポインタ完全制覇』,前橋 和弥, 技
術評論社, 2001
 『プログラミング言語C 第2版 ANSI規格準
拠』, B.W. カーニハン (著), D.M. リッチー
(著), 石田 晴久 (翻訳), 共立出版, 1...
Upcoming SlideShare
Loading in …5
×

C言語ポインタ講座 (Lecture of Pointer in C)

サークルのC言語ポインタ講座の外部公開用資料です。
2017/11/12 改訂&演習問題追加

This is an document of pointer in C.
English version is not available.

  • Be the first to comment

C言語ポインタ講座 (Lecture of Pointer in C)

  1. 1. C言語ポインタ講座 @kakira9618 2017/11/12
  2. 2. この資料について  この資料は、@kakira9618が所属している サークルでの講座『C言語ポインタ講座』 で使用していた資料を、一部修正・加筆し て、一般公開したものです。  質問、指摘などは@kakira9618まで。 – https://twitter.com/kakira9618 2
  3. 3. この講座の対象 3 1 ポインタの基本的な使い方を知っている ポインタ変数の宣言の仕方と使い方 ポインタを使ったプログラムが一応書ける 2 動作原理、記法がよく分かっていない ポインタ変数が派生型である理由を説明できない 配列の要素へのアクセスの原理を説明できない int (*p)[3]; って何? という人 3 良い使い方がわからない で、どういうときに使うと良いの?という人
  4. 4. ポインタのポイント これらを把握すればポインタは理解できる! 4 1 原理 ポインタがどのようにしてメモリ上の情報を 読んでいるかを理解する。 2 記法 ポインタの記法を整理する。 3 使い方 ポインタを使うと効率よく書ける場面を把握。
  5. 5. お約束  この資料では、以下の環境を仮定します – sizeof(int) = 4 – sizeof(short int) = 2 – sizeof(float) = 4 – sizeof(double) = 8 – sizeof(ポインタ型) = 4 – 1byte = 8bit – C言語の規約はANSI C – float型・double型の表現方法は IEEE754 – 文字コードはASCII – とくに指示がない限りバイトオーダーは リトルエンディアン 5
  6. 6. 注意  この資料中に掲載しているプログラムは ポインタの文法や原理、使い方の解説をす るために掲載しているものです。  そのため、実務のプログラミングでそのま ま使うと怒られるようなコード、もしくは 他の方法を使ったほうが良いようなコード 等が入っていたりしますがご了承ください。 6
  7. 7. 目次 7 記法 使い方 原理
  8. 8. ポインタの指す”幅”を意識する ポインタはただ場所を保存するだけじゃない! 8
  9. 9. 質問です 9 Q. C言語におけるポインタ変数とは?
  10. 10. 質問です 10 Q. C言語におけるポインタ変数とは? アドレスを保存する変数! 確かにその通り。 だけど、理解するにはもう一歩。
  11. 11. アドレスとポインタ変数の違い アドレス  ポインタ変数の値  メモリ上の位置を示す 番号のこと – 0x12345678とか ポインタ変数  アドレスを格納する変数 – 格納するアドレスを自由に 変更できる 11 int *p = &intValue ; アドレスポインタ変数
  12. 12. さらに質問 12 Q. &演算子(1項演算子)で計算されるものは?
  13. 13. さらに質問 13 Q. 式中の&演算子(1項演算子)で計算されるものは? その変数のアドレス! その変数のアドレス、ですね。 だけど、理解するにはもう一歩。 う~ん... 論理積かな?
  14. 14. &演算子 14  &を変数の前につけると、 が得られる 変数の先頭アドレス
  15. 15. 図解  int value;  &value 15 value value ここのアドレスが得られる = 1byte
  16. 16. 参照  参照するときは、ポインタ変数の前に*を つけます。  これで、pの記憶していたアドレスに書い てある内容を取り出すわけですが・・・ 16 int *p = &intValue; printf(“%d”, *p);
  17. 17. 重要な考察  int *p = &intValue; では、pがintValueの 先頭アドレスで初期化される。  *pで、pのアドレスの内容を取り出す 値を取り出すとき、 どこから取り出せば良いのかは分かる。 どこまで取り出せばよいのかは?? 17
  18. 18. 図解  int *p = &value;  *p 18 valuep p どこまで読めば良いの??= 1byte p 変数pに のアドレスが入っていることを意味注意: =
  19. 19. 重要な考察 19 &のときに「valueの」という 情報も保存してるのでは? →まちがい。pはアドレスしか保存していません。 ポインタ自身の型から推測? →せいかい!
  20. 20. 重要な考察  int *p; としたとき、  *pとすると、pに記憶されているアドレス から、int型分だけ読み込む 20 pは int型への ポインタ変数
  21. 21. 図解  int *p = &value;  *p 21 valuep p int型分 「int型分」はpの宣言「int *p」から。 = 1byte
  22. 22. ポインタ演算  p++; や、p--; とするとポインタに保存さ れているアドレスが1単位動く  アドレスがいつも1byte動くわけではない  ポインタ変数の宣言から、参照元の型を 見て、その型の大きさを1単位とする。 22
  23. 23. 図解  int型へのポインタpの場合: 23 p int型分 p int型分 p++; int型分だけずれる = 1byte
  24. 24. 図解  short int型へのポインタpの場合: 24 p short int 型分 p p++; short int型分だけずれる short int 型分 = 1byte
  25. 25. ここまでのまとめ  ポインタはアドレスを保存する変数  値を参照するときに、自身の型から参照すべ き幅を計算  ポインタ演算(++, --など)をするときも、 自身の型から増やすべき値を計算。  アドレスにも型があり、同じように計算する。 25 ポインタやアドレスの指す先には ”幅”があるイメージ
  26. 26. メモリへの情報格納方法 同じビット列でも格納方法・型が違えば違う値! 26
  27. 27. データの格納順(バイトオーダー)  情報をどういう順番で格納していくか  大きく分けて2つ 27 1 ビックエンディアン 上位バイトを上位バイト、 下位バイトを下位バイトに格納 2 リトルエンディアン 上位バイトを下位バイト、 下位バイトを上位バイトに格納 このような格納順をバイトオーダーという 素直 ひねくれ
  28. 28. ビックエンディアン  0x12345678(0xは16進数を表す)を ビックエンディアンで格納するとき: 28 0 x 1 2 3 4 5 6 7 8 0x12 0x34 0x56 0x78 上位 下位 格納 ソース上 メモリ上 = 1byte
  29. 29. ビックエンディアン  0x12345678(0xは16進数を表す)を ビックエンディアンで取り出すとき: 29 0 x 1 2 3 4 5 6 7 8 0x12 0x34 0x56 0x78 上位 下位 取出 ソース上 メモリ上 = 1byte
  30. 30. リトルエンディアン  0x12345678(0xは16進数を表す)を リトルエンディアンで格納するとき: 30 0 x 1 2 3 4 5 6 7 8 0x78 0x56 0x34 0x12 上位 下位 格納 ソース上 メモリ上 = 1byte
  31. 31. リトルエンディアン  0x12345678(0xは16進数を表す)を リトルエンディアンで取り出すとき: 31 0 x 1 2 3 4 5 6 7 8 0x78 0x56 0x34 0x12 上位 下位 取出 ソース上 メモリ上 = 1byte
  32. 32. float(double)型の格納方法  IEEE 754の規格に基づく。  32bitを3つの領域に分ける – 符号部分(1bit):+なら0, -なら1 – 指数部分(8bit) :基数部分を2の何乗するか – 基数部分(23bit) :基となる数字の並び  (値) = (符号) * 1.(基数) * 2 32 (指数-127) 符号 1bit 指数 8bit 基数 23bit
  33. 33. float (double)型の格納方法 33 0 x C 0 F 8 0 0 0 0 =1bit 0xC 0x0 0xF 0x8 0x0 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 ※簡単化のため、バイトオーダーは ビックエンディアンとする 続きは全て0→
  34. 34. float (double)型の格納方法 34 0 x C 0 F 8 0 0 0 0 0xC 0x0 0xF 0x8 0x0 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 符 号 指数 基数 =1bit
  35. 35. float (double)型の格納方法 35 0 x C 0 F 8 0 0 0 0 =1bit 0xC 0x0 0xF 0x8 0x0 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 符 号 指数 (129) 基数 符号が1なので「-1」 指数は129 – 127 = 2 基数は1.11110000000000000… ⇒-1 * 2 * 1.1111 = -111.11 = -7.752
  36. 36. 型と数値 36 0 x C 0 F 8 0 0 0 0 0xC 0x0 0xF 0x8 0x0 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0  このビット列をunsigned int型だと思うと – ビックエンディアンなら • 0xC0F80000 = 3237478400という整数 – リトルエンディアンなら • 0x0000F8C0 = 63680という整数 =1bit
  37. 37. 型と数値  このビット列をint型だと思うと – ビックエンディアンなら • 0xC0F80000 = -1057488896 という整数 – リトルエンディアンなら • 0x0000F8C0 = 63680という整数 37 0 x C 0 F 8 0 0 0 0 0xC 0x0 0xF 0x8 0x0 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 =1bit
  38. 38. 型と数値  このビット列をfloat型だと思うと – ビックエンディアンなら • 0xC0F80000 = -7.75という数値 – リトルエンディアンなら • 0x0000F8C0 = 0.0という数値 38 0 x C 0 F 8 0 0 0 0 0xC 0x0 0xF 0x8 0x0 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 =1bit
  39. 39. 型と数値  0xC0F80000が格納されている領域の 先頭アドレスがポインタに代入されている 39 unsigned int a = 0xC0F80000; unsigned int *p1 = &a; int *p2 = (int *)&a; float *p3 = (float *)&a; ※ &aの左の(int *)などはキャストと呼ばれる。 キャストにより型の変換をすることができる。 アドレスに対してキャストを行った場合、 アドレスの値は変わらないが、アドレスの型のみが変わる。
  40. 40. 型と数値  0xC0F80000が格納されている領域の 先頭アドレスがポインタに代入されている  このとき、保持しているポインタの型に よって、読みだした結果が異なる! 40 unsigned int a = 0xC0F80000; unsigned int *p1 = &a; int *p2 = (int *)&a; float *p3 = (float *)&a; // 3237478400, -1057488896, -7.750000 printf(“%u, %d, %fn”, *p1, *p2, *p3);
  41. 41. 型  つまり、C言語における型は を表す  アドレスのみを保存するポインタ変数は、 指し示す先の型の情報が無いとメモリに アクセスできない! 41 メモリ上のデータの格納方式 メモリ上のデータの読込方式
  42. 42. 普通の変数と配列の(メモリ上の)違い 普通の変数  別個に変数を定義 – int a, b, c;  各変数が連続した領域に 置かれるとは限らない 配列  配列で定義 – int a[3];  各要素は連続した領域に 必ず置かれる。 42
  43. 43. 図解 43  int a, b, c; a c b  int a[3]; a[0] a[1] a[2] 必ず連続 連続とは限らない = 1byte
  44. 44. ここまでのまとめ  ビット列が同じでも、格納方法や型が違えば違う値! 44 0 x C 0 F 8 0 0 0 0 3237478400 63680 -7.75 -1057488896 0.0
  45. 45. ここまでのまとめ  配列は連続した領域に置かれる 45 a[0] a[1] a[2] 必ず連続 int a[3]; = 1byte
  46. 46. 配列のアクセスの原理を理解する 連続した領域+ポインタ=ランダムアクセス 46
  47. 47. 覚えるべき文法(1)  配列名だけを書いたとき、 – sizeofや&の対象ならば、配列全体を意味 – それ以外ならば、配列の先頭要素のアドレス 47 int a[5] = {1, 2, 3, 4, 5}; printf(“%un”, sizeof(a)); someFunction(&a); int (*p)[5] = &a; int *p1 = a; int *p2 = a + 1; someFunction(a); printf(“%pn”, a + 1) 配列全体 配列の先頭要素へのアドレス
  48. 48. 覚えるべき文法(2)  配列名(sizeofや&の対象の場合を除く)を書いたと きに得られるアドレスの型は、(配列要素の型)へ のポインタ型。 48 int a[4]; なら、aの型は、int * float a[2]; なら、aの型は、float * char a[6]; なら、aの型は、char *
  49. 49. 覚えるべき文法(3)  配列の糖衣構文(シンタックスシュガー) 49 array[i] = *(array + i)  この2つの表記は完全に同じ意味  配列の宣言の時の[]は別物。 – この[]は、配列を使うときの[]。
  50. 50. 配列アクセスを理解 50  int a[3] = {1, 2, 3}; a[0] a[1] 必ず連続 1 2 a[2] 3 = 1byte
  51. 51. 配列アクセスを理解  a[2] 51 a[0] a[1] 必ず連続 1 2 a[2] 3 = 1byte
  52. 52. 配列アクセスを理解  *(a + 2) 52 a[0] a[1] 必ず連続 1 2 a[2] 3 = 1byte
  53. 53. 配列アクセスを理解  *(a + 2) 53 a[0] a[1] 必ず連続 1 2 int * 幅はint a int a[2] 3 = 1byte
  54. 54. 配列アクセスを理解  *(a + 2) 54 a[0] a[1] 1 2 int * 幅はint a + 2 a[2] 3 int +2すると、 sizeof(int) * 2 = 8 byte分進む 8byte = 1byte
  55. 55. 配列アクセスを理解  *(a + 2) 55 a[0] a[1] 1 2 a + 2 a[2] 3 int この領域をint型で 読み取り ⇒3が読みだされる = 1byte
  56. 56. ポインタを使っても同じ!  int a[3] = {1, 2, 3};  int *p = a; //ポインタ変数にアドレス代入  printf(“%dn”, p[2]); 56 a[0] a[1] 1 2 p + 2 a[2] 3 int*(p + 2)= = 1byte
  57. 57. 動的配列 (実行時に要素数を指定できる配列)  #include <stdlib.h>をして使える、malloc – memory allocation (メモリ確保)  malloc(確保したいバイト数); – とすると、連続した確保したいバイト数分の 領域が確保される。 – 返り値は、確保した領域の先頭アドレス (指している型の情報無し。つまりvoid *) – 仕方がないので返り値はキャストして使う 57
  58. 58. 動的配列(図解) 58  malloc(8); とりあえず8byte分だけ確保 返り値(アドレス) = 1byte
  59. 59. 動的配列(図解) 59  int *p = (int *)malloc(8); 返り値をpへ p p[0] p[1] 配列の記法で 領域を扱える = 1byte
  60. 60. 動的配列(図解) 60  short int *p = (short int *)malloc(8); 返り値をpへ p p[0] p[1] 配列の記法で 領域を扱える p[2] p[3] = 1byte
  61. 61. 動的配列  環境によってintやshort intなどの型の大き さは変わる  引数には sizeof(型)*欲しい要素数を入れる 61 int *p = (int *)malloc(sizeof(int) * 2); 呪文(?)の完成!
  62. 62. 配列の配列  二次元配列の正体は、配列の配列 – つまり、配列の要素として配列を含んでいる  char a[2][3]; のとき、aはchar型の配列(要 素数3)を要素とする、要素数2の配列。  aのアドレスの型は、 char型の配列(要素 数3)へのポインタ型 62
  63. 63. 配列の配列(図解)  char a[2][3] = {{1,2,3}, {4,5,6}}; 63 a 1 2 3 4 5 6  a[1] a+1 1 2 3 4 5 6 =*(a + 1) *(a + 1)…char [3](配列) = 1byte
  64. 64. 配列の配列(図解) 64  *(a+1)はchar [3](配列)だが、次に+2という演算が控えているため 先頭要素へのアドレスに読み替えられる a+1 1 2 3 4 5 6 *(a + 1)…char [3](配列) *(a + 1)…char * (先頭要素へのポインタ型) *(a+1) 1 2 3 4 5 6 = 1byte
  65. 65. 配列の配列(図解) 65  a[1][2] *(a+1)…char * 1 2 3 4 5 6 *(a+1)+2…char *  a[1][2] = *(*(a + 1) + 2) ⇒6が読みだされる = 1byte
  66. 66. 配列へのポインタ  途中で登場した、「char型の配列」へのポ インタの定義がこちら。  間違えても、char *p[5]; と勘違いしてはい けない。 – こちらは、char *が5つ格納されている配列 66 char (*名前)[配列の要素数]; (例) char a[5]; を指すポインタは、char (*p)[5]; char (*p)[5] = a;
  67. 67. ここまでのまとめ  配列の要素は、連続した領域に置かれる  配列名は、(基本的に)先頭要素へのアドレ ス – このアドレスの型は、配列の要素の型へのポイン タ型  このアドレスをポインタ演算したり、ポイン タ変数に入れたりして配列っぽく使用。 – このとき、*(array + i) = array[i]である。  二次元配列は、配列の配列であり、ポインタ 演算の規則をうまく使って要素にアクセスし ている。 67
  68. 68. 理解度チェック  バイトオーダーはリトルエンディアンとします。 次のア~カの行で表示される数値は?(次ページに補足有) 68 int arr[] = {0x01234567, 0x89abcdef, 0xc0f80000}; int *p1 = arr; char *p2 = (char *)arr; float *p3 = (float *)arr; printf(“%un”, sizeof(arr)); printf(“%un”, sizeof(p2)); printf(“%xn”, p1[1]); printf(“%xn”, p2[1]); printf(“%3.2fn”, p3[2]); printf(“%xn”, *(int *)(p2 + 2)); /* ア */ /* イ */ /* ウ */ /* エ */ /* オ */ /* カ */
  69. 69. 理解度チェック(補足)  バイトオーダーはリトルエンディアンとします。 次のア~カの行で表示される数値は? 69 int arr[] = {0x01234567, 0x89abcdef, 0xc0f80000}; int *p1 = arr; char *p2 = (char *)arr; float *p3 = (float *)arr; printf(“%un”, sizeof(arr)); printf(“%un”, sizeof(p2)); printf(“%xn”, p1[1]); printf(“%xn”, p2[1]); printf(“%3.2fn”, p3[2]); printf(“%xn”, *(int *)(p2 + 2)); /* ア */ /* イ */ /* ウ */ /* エ */ /* オ */ /* カ */ 0xで始まる値は16進数を表します sizeof演算子…演算対象のメモリ上での大きさをバイト単位で計算します %u…符号なし整数10進数として出力します %x…16進数として出力します %3.2f…全体で3桁、小数部分は2桁になるように小数点表示します
  70. 70. お約束(再掲)  この資料では、以下の環境を仮定します – sizeof(int) = 4 – sizeof(short int) = 2 – sizeof(float) = 4 – sizeof(double) = 8 – sizeof(ポインタ型) = 4 – 1byte = 8bit – C言語の規約はANSI C – float型・double型の表現方法は IEEE754 – 文字コードはASCII – とくに指示がない限りバイトオーダーは リトルエンディアン 70
  71. 71. 理解度チェック(ヒント)  迷ったらメモリの図を書こう! 71 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? 0x?? arr[0] arr[1] arr[2] = 1byte  バイトオーダーに注意。  arrだけでなく、p1, p2, p3もメモリ上に確保 される
  72. 72. 理解度チェック(答え)  ア: 12  イ: 4  ウ: 0x89abcdef  エ: 0x45  オ: -7.75  カ: 0xcdef0123 72
  73. 73. 理解度チェック(解説:前提)  int arr[] = {0x01234567, 0x89abcdef, 0xc0f80000};  のメモリ上の姿を書くと…… 73 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2]  要素はメモリ上で必ず連続して配置  各要素ごとにリトルエンディアンで格納 = 1byte
  74. 74. 理解度チェック(解説:ア)  printf(“%un”, sizeof(arr)); 74 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte  sizeof(配列)は配列全体の大きさをバイト単位で返す  arrは全体で12byteの配列 arr (12byte)
  75. 75. 理解度チェック(解説:イ①)  char *p2 = (char *)arr; 75 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte p2 ※このarrは先頭要素arr[0]のアドレスとなる。型はint *  arrはint *となるが、(char *)とキャストしているので、 問題なくポインタ変数p2にarr[0]のアドレスが入る
  76. 76. 理解度チェック(解説:イ②)  printf(“%un”, sizeof(p2)); 76 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte p2(ポインタ変数)の領域(4byte)を返す 4byte p2
  77. 77. 理解度チェック(解説:ウ①)  int *p1 = arr; 77 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte p1
  78. 78. 理解度チェック(解説:ウ②)  p1[1] は *(p1 + 1) 78 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte p1+1 この領域をintで読み取る  読み出すときもバイトオーダを意識する – 読み出すと 0x89abcdef となる
  79. 79. 理解度チェック(解説:エ)  p2[1]は *(p2 + 1) 79 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte p2+1  読み出すと、0x45。  バイトオーダーによって結果が変わる! – (ビックエンディアンの場合……0x23) この領域をcharで読み取る
  80. 80. 理解度チェック(解説:オ①)  float *p3 = (float *)arr; 80 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte p3
  81. 81. 理解度チェック(解説:オ②)  p3[2] は *(p3 + 2) 81 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte p3+2 この領域をfloatで読み取る  バイトオーダーを考えて読むと0xc0f80000
  82. 82. 理解度チェック(解説:オ③) 82  0xc0f80000は2進数で…… 1100 0000 1111 1000 0000 0000 0000 0000 0xc 0x0 0xf 0x8 0x0 0x0 0x0 0x0  IEEE754で読むと 1 10000001 11110000000000000000000 上位1ビット目…符号はマイナス 上位2-9ビット目…指数は129-127=2 その他…データは2進数で1.11110000…… -1 * (1.1111) * 2^(129-127) = -111.11 = -7.75 2進数 2進数
  83. 83. 理解度チェック(解説:カ①)  *(int *)(p2 + 2) 83 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte p2+2  p2+2は0x23の領域を指す
  84. 84. 理解度チェック(解説:カ②)  *(int *)(p2 + 2) 84 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte (int *)(p2+2)  int *にキャストしたことで、指す領域が広がる
  85. 85. 理解度チェック(解説:カ③)  *(int *)(p2 + 2) 85 0x67 0x45 0x23 0x01 0xef 0xcd 0xab 0x89 0x00 0x00 0xf8 0xc0 arr[0] arr[1] arr[2] = 1byte (int *)(p2+2)  バイトオーダーに気をつけて読み取ると 0xcdef0123となる。 この領域をintで読み取る
  86. 86. 関数の引数にポインタを渡す 間接的なアクセス方法 86
  87. 87. 関数にポインタを渡す  ここでは原理のみ。表記の問題等は後で 87 void someFunc(int *p) { printf(“%dn”, *p); } int main(void) { int value = 3; int *pValue = &value; someFunc(pValue); return 0; } 何が起こっているのか?
  88. 88. メモリマップ  メモリは、使用用途によって領域が分かれている。  静的領域…プログラムの開始から終了まで読書可  定数領域…絶対に書き換えない。プログラムの開始 から終了まで使える。読込のみ可。  スタック領域… 関数ごとの領域。その関数が実行中 の間使える。読書可。  ヒープ領域…mallocなどで使用。解放するまで読書可 88
  89. 89. メモリマップ(図解) 89 int global = 3; void func2() { printf(“Hellon”); } void func1(int a) { int b = 1; func2(); printf(“%d,%dn”, a , b); } int main(void) { func1(1); } global 静的 “Hellon” “%d,%dn” 定数 a b スタック(func2) スタック(func1) スタック(main) メモリ
  90. 90. メモリマップ(図解) 90 global 静的 “Hellon” “%d,%dn” 定数 a b スタック(func2) スタック(func1) スタック(main) メモリ ずっと使える。 書き換え可 ずっと使える。 書き換え不可 関数が始まってから 終わるまで使える。 書き換え可
  91. 91. 関数に値を渡す  関数に値を渡すと、その値は渡した先に コピーされる – ポインタの値だろうがコピーされます。 91 void someFunc(int *p) { printf(“%dn”, *p); } int main(void) { int value = 3; int *pValue = &value; someFunc(pValue); return 0; } value; pValue; p(pValueの値を コピー) スタック( main ) スタック( someFunc )
  92. 92. 関数に値を渡す  渡したものがポインタだと、関数外の領域 にアクセスできるようになる。 92 void someFunc(int *p) { printf(“%dn”, *p); } int main(void) { int value = 3; int *pValue = &value; someFunc(pValue); return 0; } value; pValue; p(pValueの値を コピー) ア ク セ ス スタック( main ) スタック( someFunc )
  93. 93. イメージ  webにアップロードした写真のURLを友達 に教える – URL自身はコピーされている – 写真はコピーされない – 友達も写真にアクセスできる。 93 http://xxx.com/yyy.jpg http://xxx.com/yyy.jpg アクセス アクセス
  94. 94. ダメな参照  呼び出し元と呼び出し先の関数で値をやり 取りする際の注意  呼び出し先関数内の領域を指すポインタを 呼び出し元で使ってはいけない!! – 呼び出し元で使うときには、呼び出し先の関 数の実行は終わっている – メモリマップ的にアウト。 – どうなるかわからない(動作未定義) 94
  95. 95. ダメな参照(例) 95 int *someFunc() { int value = 3; return &value; } int main(void) { int *p = someFunc(); printf(“%dn”, *p); return 0; } main someFunc p value main someFunc p value 使えないアドレス
  96. 96. ダメな参照(イメージ) 96  webにアップロードした写真のURLを友達 に教える  何故か教えた瞬間にうp主が写真を削除 ①http://xxx.com/yyy.jpg http://xxx.com/yyy.jpg ②削除 ③アクセス できない 404 NotFound
  97. 97. 良い参照  関数内で作った値へのポインタを返したいときは、 その領域を動的確保します。  動的確保された領域は、ヒープ領域に入るため、 明示的に開放しない限りメモリ上に残ります。 97 int *someFunc() { int *p = (int *)malloc(sizeof(int)); *p = 3; return p; } int main(void) { int *p = someFunc(); printf(“%dn”, *p); free(p); return 0; } ※mallocした領域は、 使い終わったら必ずfreeしましょう。
  98. 98. 関数にポインタを渡すメリット 98 1 コピーコスト削減 アドレスしかコピーしないので、 コピーする量・時間が減る 2 データの一貫性保持 データを更新すればその変更が向こうにも伝わる 3 複数の値を返す関数 複数のアドレスを渡しておけば、 複数の値を返す関数のようなものを作れる。
  99. 99. ここまでのまとめ  関数にポインタを渡すことができる – 渡された値はポインタであろうとコピーされる – 呼び出し先から呼び出し元の変数を参照できる  呼び出し元から呼び出し先の変数の参照を するときは注意。 – 動的確保した領域へのポインタを返せばOK  コピーコスト削減、データの一貫性を保つ、 複数の値を返せるなどの利点がある。 99
  100. 100. 文字列 文字の配列 100
  101. 101. 文字と文字列 文字  通常、‘A’や’b’とかをchar 型変数に格納。 – 実際に格納されるのは文字 に対するアスキーコード (数値)  1文字のみ表す 文字列  「文字」が配列になった もの – char str[3] = {‘a’, ‘b’, ‘0’};  文字配列の最後に番兵と して’0’を入れる – 文字列の終端が分かる 101
  102. 102. 文字列(図解) 102  char str[3] = {‘a’, ‘b’, ‘0’}; str ‘a’ ‘b’ ‘0’  printf(“%s”, str); str ‘a’ ‘b’ ‘0’ strから’0’のアドレスまで表示 “ab” = 1byte
  103. 103. 文字列(図解) 103  char str[3] = {‘a’, ‘b’, ‘0’};  printf(“%s”, str); p = str ‘a’ ‘b’ ‘0’ char *p = str; while(*p != ‘0’) { printf(“%c”, *p); p++; } p ‘a’ ‘b’ ‘0’ p ‘a’ ‘b’ ‘0’ p++ p++ ‘a’ ‘b’ 終了 = 1byte
  104. 104. 文字列とポインタ  配列の先頭をポインタに格納すれば、 ポインタで文字列を扱うこともできる 104 char str[3] = {‘a’, ‘b’, ‘0’}; char *str_p = str; printf(“%s”, str_p); str_p = str ‘a’ ‘b’ ‘0’ str_pは指している場所を 自由に場所を変更できる strの場所の変更は不可能 (strはコンパイル時定数)⇔= 1byte
  105. 105. ややこしい表記 105 char str[3] = {‘a’, ‘b’, ‘0’}; char str[] = {‘a’, ‘b’, ‘0’}; char str[] = “ab”; 文字列のために 用意された文法 (シンタックス シュガー) 配列のルール
  106. 106. ややこしい表記 106 char str[] = “ab”; char *str = “ab”;
  107. 107. 図解  char str[] = “ab”;  char *str = “ab”; 107 ローカル‘a’ ‘b’ ‘0’ str(コンパイル時定数) 定数‘a’ ‘b’ ‘0’ str ローカル = 1byte
  108. 108. char *の配列とcharの2次元配列 char *の配列  char *a[] = {“ab”, “cde”};  スタックに各要素(ポインタ) – ポインタの指す場所は変更可能  定数域にデータ – ポインタの指すデータは変更不可能  領域を無駄なく利用 char の二次元配列  char a[2][4] = {“ab”, “cde”};  スタックにデータ – データは書き換え可能 – aはスタック領域の配列 {“ab”, “cde”}の先頭を指すア ドレス定数  無駄な領域が発生 108 目次方式 表方式
  109. 109. 図解  char *a[] = {“ab”, “cde”}; 109 定数‘a’ ‘b’ ‘0’ a[0] ローカル a[1] ‘c’ ‘d’ ‘e’ ‘0’ a…char *へのポインタ ※a[0], a[1]はchar * = 1byte
  110. 110. 図解  char a[2][4] = {“ab”, “cde”}; 110 ローカル‘a’ ‘b’ ‘0’ ‘c’ ‘d’ ‘e’ ‘0’ a…char [4]へのポインタ ※a[0], a[1]はchar [4](配列)だが、 次の演算がsizeof, &以外の場合、 char *(先頭要素へのポインタ)になる = 1byte
  111. 111. char *の配列とcharの2次元配列(共通点)  共通点は、どちらもa[i]がchar *となること。 – しかもそのポインタは文字列の先頭を指す – 両方とも、文字列の配列として利用できる  さらに、a[i][j]と書くと、i個目の文字列のj 番目の文字を表せる 111 使い方はほぼ同じだが、メモリの構造は違う!
  112. 112. ここまでのまとめ  文字列はchar の配列である。  文字列の最後に番兵として’0’を置く  char str[] = {‘a’, ‘b’, ‘0’} は char str[] = “ab”; と同じ意味 – これもシンタックスシュガー – 文字列のために用意された文法  char str[][hoge];とchar *str[]は同じように使う ことができるが、メモリの構造は異なる 112
  113. 113. 構造体へのポインタ 基本がわかっていれば怖くないけど、応用例が多い 113
  114. 114. 構造体  いろいろな型を並べて新しい型を作れる 114 typedef struct { char name[10]; int age; } person;  person型はchar[10]とintを並べた型
  115. 115. 構造体  構造体を使う 115 typedef struct { char name[10]; int age; } person; … person john = {“John”, 21}; printf(“%s %dn”, john.name, john.age);  構造体の中の名前でアクセスできる
  116. 116. メモリ上の構造体  構造体の宣言は、メモリ上のレイアウトを決めている 116 typedef struct { char name[10]; int age; } person; name age 0 10index = 1byte
  117. 117. メモリ上の構造体  メンバ変数とindex(相対位置)を紐づけている 117 typedef struct { char name[10]; //開始indexは0 int age; //開始indexは10 } person; name age 0 10index = 1byte
  118. 118. アライメント  構造体は配列とは違い、必ずしもその中身が連続 するとは限らない  OS・CPUが扱いやすい単位に揃えられてデータの レイアウトが決まることがある  扱いやすい単位をアライメントという。  charのアライメントは1byte、intのアライメントは 4byteなど。(環境依存) 118 name age 0 12index 4の倍数 データがどのようにアラインされても、 メンバ変数でアクセスすれば問題ない = 1byte
  119. 119. 構造体へのsizeof  レイアウトの結果定まった、構造体全体のサイズ を返す。 – 正確には、この構造体を配列に入れて並べたときの 隣り合う2要素間のアドレスの差をbyteで返す。  アライメントの関係で、sizeof(構造体)がsizeof(メ ンバ変数)の総和と等しくなるとは限らない 119 0 10index sizeof(person) = 14 = 1byte
  120. 120. 構造体へのsizeof 120 sizeof(person) = 16 0 12index  レイアウトの結果定まった、構造体全体のサイズ を返す。 – 正確には、この構造体を配列に入れて並べたときの 隣り合う2要素間のアドレスの差をbyteで返す。  アライメントの関係で、sizeof(構造体)がsizeof(メ ンバ変数)の総和と等しくなるとは限らない = 1byte
  121. 121. 構造体へのポインタ  構造体へのポインタを定義して使う  person *p = &john;  printf(“%s %dn”, p->name, p->age);  ポインタからメンバ変数にアクセスする時 は.ではなく->を使う。 121 a->b = (*a).b これもシンタックスシュガー
  122. 122. 構造体を関数に渡す  構造体はデータの塊。これを関数に渡すと データがコピーされる。  巨大な構造体だとコピーするのに 時間とメモリ領域が消費される。  ポインタを渡せば、一瞬でデータを渡せる。  ただし、データを書き換えると その変更は呼び出し元に伝わる。 122
  123. 123. 構造体を関数に渡す 123 John person *p = &john; p 呼び出し元関数 func関数 120 = 1byte
  124. 124. 構造体を関数に渡す 124 John person *p = &john; p 呼び出し元関数 func関数 func(p); ↑ 4バイトだけコピー 120 p = 1byte
  125. 125. 構造体を関数に渡す 125 John person *p = &john; p 呼び出し元関数 func関数 func(p); p->age = 14; 12 書き換え 0 ageの indexは12 p = 1byte
  126. 126. 構造体へのポインタまとめ  複数の型を融合して新しい型を定義できる  構造体の宣言はメモリ上のレイアウトを決 めている  メンバ変数がメモリ上の相対位置を記憶  アライメントに注意。  巨大な構造体でも、ポインタを経由して他 の関数に素早く渡すことができる。 126
  127. 127. 関数ポインタ とにかく記法が難しい。使い方は難しくない。 関数を持ち運べる便利な変数! 127
  128. 128. まずは関数ポインタ 128  関数名も実はアドレス  アドレスの型として実際に使うときの情報 つまり、「引数の型・順番」「返り値の型」 が分かれば良い。 char func(char *p, int n); 返り値の型と 引数の型・順番が同じ なら同じ型
  129. 129. 関数ポインタ  関数を格納するためのポインタpfuncの宣言  関数を持ち運べる便利な変数と考えると良い 129 char (*pfunc)(char *, int);  何が変わったのか? – かっこを左にもつけた • ポインタであるということを示すため – 引数の名前は削除 • 型が重要だから
  130. 130. 関数ポインタの使い方 130 char func1(char *p, int n) { return p[n]; } char func2(char *p, int n) { return *p + n; } int main(void) { char (*pfunc)(char *, int); pfunc = func1; //pfunc = func2; printf(“%c”, pfunc(“aaa”, 1)); } どっちも型 が等しい 関数ポインタ pfuncの宣言 どちらのアドレ スでも代入可 呼び出しは いつも通り
  131. 131. 関数ポインタの配列 131 char (*pfunc[3])(char *, int);  関数を格納するためのポインタ配列の宣言  何が変わったのか – 変数名の右に配列を表す[]がついた
  132. 132. 関数ポインタの使い方 132 char func1(char *p, int n) { return p[n]; } char func2(char *p, int n) { return *p + n; } int main(void) { char (*pfunc[2])(char *, int); pfunc[0] = pfunc1; pfunc[1] = pfunc2; printf(“%c”, pfunc[0](“aaa”, 1)); } どっちも型 が等しい 関数ポインタの 配列pfuncの宣言 どちらのアドレ スでも代入可
  133. 133. 関数ポインタを引数に取る  関数ポインタは関数の引数にもできます – すごく記述が複雑になるが、落ち着いてみれ ばOK  関数の引数として関数を与えられる – 関数内から、与えられた関数を呼べる – コールバック関数 133 int func(int n, char (*pfunc)(char *, int)) { … }
  134. 134. 関数ポインタを返り値にする 134 char (*func(int n))(char *, int) { … }  返り値なのに、左右から挟み込む新発想! – C言語の規格なので仕方ない。  後で処理してほしい関数などを返せる
  135. 135. 目次 135 記法 使い方 原理
  136. 136. 表記は同じだけど意味が違う 混同しやすいので注意 136
  137. 137. *  *は3つの意味を持つ。 137 1 ポインタの宣言の* int *p; など、「自分はポインタである」ことを 示すための*。関数の引数の*もこっちの意味。 2 ポインタ参照の* ポインタ(またはアドレス)の前につける*。 そのアドレスの値を参照する。 3 掛け算の* 2 * 3とか。
  138. 138. []  []も3種類。 138 1 配列の定義の[] 配列を定義するときに使う[]。 配列の要素数を表す。 2 配列の要素の参照の[] 配列の要素を参照するときに使う[]。 *(array + i) = array[i]と言い換えられる。 3 関数の仮引数の[] これは、関数の引数にポインタ渡しするとき 見た目だけでも配列にするための[]。後述。
  139. 139. “”  “”は2種類。 139 1 文字列リテラルの”” 定数領域に格納される文字のデータを表す”” ほとんどの””はこちらに分類される。 型としては、char []となる。 2 charの配列を初期化するための”” char str[] = {‘a’, ‘b’, ‘0’}; と書くのが面倒なので char str[] = “ab”; と書けるようにした。 上の””とは関係はない。
  140. 140. 配列名 140 1 配列の先頭要素へのポインタ ほとんどの配列名はこちらに分類される。 type型の配列の場合、配列名を書くとtype *型となる 2 配列全体 sizeof(配列)または、&配列で使われたとき。 前者は配列全体の大きさ(byte)、 後者は配列全体に対するポインタを計算する。  配列名は2種類
  141. 141. 表記は違うけど意味が同じ こっちも混同しやすいので注意 141
  142. 142. 関数の引数  関数引数の宣言 142 void func(int *p) { } void func(int p[]) { }
  143. 143. 関数の引数  どうして?  ⇒関数に渡すときに、元が配列であるという情報 はどのみち消える。呼び出し元が配列であったこ とを明示したい! 143 void func(int *p) { …p[i]… } void func(int p[]) { …p[i]… }  実際に意味を考えるときは、ポインタ記法 に戻して考える。  Q. sizeof(p)は? ⇒ 4
  144. 144. 関数の引数  関数引数の宣言 144 void func(int **p) { } void func(int *p[]) { } Q. sizeof(p)は? ⇒ 4
  145. 145. 関数の引数  関数引数の宣言 145 void func(int (*p)[3]) { } void func(int p[][3]) { } Q. sizeof(p)は? ⇒ 4
  146. 146. 配列のルール 146 a[i] *(a + i) もちろん、 参照時の[] (式の中の[])
  147. 147. 文字の配列 147 char str[3] = {‘a’, ‘b’, ‘0’}; char str[] = {‘a’, ‘b’, ‘0’}; char str[] = “ab”; 文字列のた めに用意さ れた文法 配列のルール
  148. 148. const  int i = 1; // 普通の変数  const int ic = 1; // 書換不可能  const int *p1 = &ic; // p1は書換可能。p1 の指す先は書換不可能  int * const p2 = &i; // p2は書換不可能。p2 の指す先は書換可能  const int * const p3 = &ic; // p3もp3の指 す先も書換不可能 148
  149. 149. 複雑な記法 英訳→和訳 149
  150. 150. 複雑な記法  char (*p)[3];  char (*func)(char *, int);  char (*func(int n))(char *, int) {  …  複雑な記法が多い。  どうにかして解読できないか? 150
  151. 151. まず英訳 151 1 中心の名前を見つける 変数や変数の名前。 真ん中にあることが多い。 見つかったら(名前) is をつける。 2 )に突き当たるまで右に読む この時、[にあたったら array[要素数] of をつける (にあたったらfunction(引数リスト) returning をつける。 3 突き当たった)に対応する(まで戻る このとき*にあたったらpointer toをつける 終わったら②へ
  152. 152. char (*p)[3] 152 1 中心の名前を見つける p p is char (*p)[3]; なう
  153. 153. char (*p)[3] 153 2 )に突き当たるまで右に読む すでに突き当たっている char (*p)[3]; p is なう
  154. 154. char (*p)[3] 154 3 突き当たった)に対応する(まで戻る 戻るときに、*に当たるので、 pointer to をつける char (*p)[3]; p is pointer to なう
  155. 155. char (*p)[3] 155 2 )に突き当たるまで右に読む [が見える。array[3] of をつける char (*p)[3]; p is pointer to array[3] of なう
  156. 156. char (*p)[3] 156 3 突き当たった)に対応する(まで戻る 一番最後に)がいると思って、 まだ読んでいないところまで左に戻ると char. char (*p)[3]; p is pointer to array[3] of char p はchar[3]配列へのポインタ なう
  157. 157. char (*(*p)())[5]; 157 1 中心の名前を見つける p p is char (*(*p)())[5]; なう
  158. 158. char (*(*p)())[5]; 158 p is char (*(*p)())[5]; なう 2 )に突き当たるまで右に読む すでに突き当たっている
  159. 159. char (*(*p)())[5]; 159 p is pointer to char (*(*p)())[5]; なう 3 突き当たった)に対応する(まで戻る 戻るときに、*に当たるので、 pointer to をつける
  160. 160. char (*(*p)())[5]; 160 p is pointer to function(void) returning char (*(*p)())[5]; なう 2 )に突き当たるまで右に読む 途中で(に出会う。 function(void) returningをつけて次へ
  161. 161. char (*(*p)())[5]; 161 p is pointer to function(void) returning char (*(*p)())[5]; なう 2 )に突き当たるまで右に読む 突き当たった
  162. 162. char (*(*p)())[5]; 162 p is pointer to function(void) returning pointer to char (*(*p)())[5]; なう 3 突き当たった)に対応する(まで戻る 戻るときに、*に当たるので、 pointer to をつける
  163. 163. char (*(*p)())[5]; 163 p is pointer to function(void) returning pointer to array[5] of char (*(*p)())[5]; なう 2 )に突き当たるまで右に読む [が見える。array[5] of をつける
  164. 164. char (*(*p)())[5]; 164 p is pointer to function(void) returning pointer to array[5] of char. char (*(*p)())[5]; なう 3 突き当たった)に対応する(まで戻る 一番最後に)がいると思って、 まだ読んでいないところまで左に戻ると char.
  165. 165. char (*(*p)())[5]; 165 p is pointer to function(void) returning pointer to array[5] of char. char (*(*p)())[5]; p は「char[5]配列へのポインタ」 を返す関数へのポインタ
  166. 166. 目次 166 記法 使い方 原理
  167. 167. ポインタの使い方(1)  代表的な使い方 167 1 関数間で構造体を渡す データの塊を渡す。 ポインタを通してデータにアクセス 2 構造体をつなげる 構造体が他の構造体を知ることが できる。リスト構造とかもこれ 3 文字列を扱う 先頭アドレスだけで文字列を表す
  168. 168. ポインタの使い方(2)  その他の使い方 168 1 動的配列 動的に確保した領域の先頭を指すポインタ C++では配列だけではなく、 オブジェクトも動的にとれる 2 コールバック処理の実現 関数内の一部処理を外部に決めてもらう 3 メモリをいじる char *を使えば1バイトずつ読み書き可能
  169. 169. 関数間で構造体を渡す  構造体(データの塊)を渡すには、アドレ スだけ渡せば十分なことが多い。 169 typedef struct { char datatype; int data[10000]; } Bigdata; void dataProc(Bigdata *p) { p->data[hogehoge]…… } int main(void) { Bigdata p; …… dataProc(&p); return 0; } main dataProc 10000*sizeof(int)+1 byte 4 byte コピーの時間と容量を削減 = 1byte …
  170. 170. 構造体をつなげる  リスト構造など。定義中の構造体へのポイン タを、その構造体のメンバ変数に追加する。 170 struct node_{ int value; struct node_ *p; }; typedef struct node_ node; value p value p value p 要素同士のつながりをpで表現 要素の追加・削除の計算量が (配列と比べて)少ない = 1byte
  171. 171. 文字列を扱う  文字列は文字の配列(の終端に’0’が付加さ れたもの)  文字列の先頭文字へのアドレスを捕まえてお けば、文字列全体を持っていることになる 171 void func(char *p) { (pを使った処理) } int main(void) { char str[5] = “test”; char *p = “hello”; func(str); func(p); … } ‘t’ ‘e’ ‘s’ ‘t’ ‘h’ ‘e’ ‘l’ ‘l’ ‘0’ ‘o’ ‘0’ p func main 定数 = 1byte
  172. 172. 動的配列  呼び出し元で配列の大きさを決定できる  型宣言・キャストの仕方によってその領域を使う ときの型を決められる 172 int *p = (int *)malloc(sizeof(int) * 2); p p[0] p[1] char (*p)[3] = (char (*)[3])malloc(sizeof(char) * 6); p p[0] p[1] p[0][0] p[0][1] p[0][2] p[1][0] p[1][1] p[1][2] char [2][3]の2次元配列を動的確保したい場合: int [2] の配列を動的確保したい場合: = 1byte
  173. 173. コールバック処理の実現  処理の一部を呼び出し側に決定させる  qsort関数など(要素の大小の定義は外部で定義する)  イベント駆動プログラミングにも最適 173 typedef struct { int w, h; } Rect; int comp_by_area(const void *a, const void *b) { return (Rect *)a->w * (Rect *)a->h – (Rect *)b->w * (Rect *)b->h; } int main(void) { Rect data[] = {{3, 5}, {4, 4}, {2, 7}}; qsort(data, sizeof(data)/sizeof(data[0]), sizeof(Rect), comp_by_area); // data[] = {{2, 7}, {3, 5}, {4, 4}}; …… } aの指すオブジェクトがbの指すものより 大きければ正 等しければ0 そうでなければ負の値 を返すような関数を定義 qsortの定義によると定義 すべき関数の型はint (const void *a, const void *b)である。これは任意の 型のオブジェクトに対応で きるようにするため。 大小関係を定義した関数である comp_by_areaを使ってソート。 大小関係の比較はcomp_by_areaで、 比較順などの最適化はqsortで行う。
  174. 174. メモリをいじる  char *は1byte単位でのメモリ操作が可能  組み込み等のプログラミングでは良く出てくる 174 char *p = (char *)0x12345678; // 整数をアドレスに変換 *p = 0xff; …  OSなどの実行環境によってアクセスでき る領域が限られることがあるので注意
  175. 175. 参考文献  『 C言語ポインタ完全制覇』,前橋 和弥, 技 術評論社, 2001  『プログラミング言語C 第2版 ANSI規格準 拠』, B.W. カーニハン (著), D.M. リッチー (著), 石田 晴久 (翻訳), 共立出版, 1989  ポインタ虎の巻 http://www.nurs.or.jp/~sug/soft/tora/ 175

×