bp13

University of Electro-Communications
Human Interface section
基礎プログラミングおよび演習
第13回
担当:長谷川晶一5階522/520
14回は、期末テストをします。
期末テストは、この部屋で、Webや資料を見たり、
プログラム組んだりしながら回答していただきます。
ポインタとアドレス
 &変数名: 変数のアドレスを返す
 ポインタ:アドレスを入れるための変数
 * ポインタ名:ポインタのアドレスにある変数
int a;
int* p;
100 0 1 0 1 1 0 0 1
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
112
int* p
a = 2;
p = &a;
printf(”p=%d\n”, p);
printf(”*p=%d\n”, *p);
p=112
*p=2
実際のアドレスの値は、プログラムを
作ってみないと何になるかわかりません。
2
int a
ポインタと変数
int a;
int* p;
int* p;
ポインタ名
int型の変数を指す型
a = 2;
p = &a;
printf(”p=%d\n”, p);
printf(”*p=%d\n”, *p);
p = &a;
変数のアドレスを返す演算子
int型の変数を指すポインタ
*p
int型の変数を指すポインタ
ポインタが指す変数を取り出す演算子
ポインタが指す変数への代入
int a;
int* p;
p
104
a
112
メモリ
a
112
メモリ
10
a
112
メモリ
112
p = &a;
p
104
112
*p = 10;
p
104
ポインタと型
int a;
float f;
int* pi=&a;
float* pf=&f;
*pi = 10
*pf = 2.4;
*pi = *pf;
pf = &a;
pi = pf;
100 0 1 0 1 1 0 0 1
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
108
pi=112 : aを指す
pf=108 : fを指す
aに10を代入
fに2.4を代入
a = fで、 aは2になる。
エラー: pfは、float型変数
しか指せない。
エラー: piとpf は型が違う
float* pf
112
int* pi
float f
int a
ポインタは、アドレスを入れる変数だが、
そのアドレスにある(はずの)変数の型が同じでないと代入できない。
課題20 変数のアドレスを見てみる について
 mainの変数はスタック領域
というところからとります。
 たいていの処理系で、大きなアドレスから
-向きに変数を取っていきます。
 グローバル変数は、静的領域
というところからとります
 たいていの処理系で小さなアドレスから
+向きに変数を取っていきます。
char gc;
short gs;
int main(){
char c;
short s;
}
0x00000000 0 1 0 1 1 0 0 1
0x00000001
0x00012020
0x00012021
0x00012022
0xBFFFF994
0xBFFFF995
0xBFFFF996
0xBFFFF997
0xBFFFF998
0xBFFFFFFE
0xBFFFFFFF
0xC0000000
char gc
short gs
short s
char c
引数にポインタを使う
 ポインタを渡すと、main()の変数を square()で書き換えられる。
→return を使わずに、関数から値を返せる。
p=pd; pはdを指す。
void square(double* p){
*p = *p * *p ;
p
ここで、*p = dを
}
書き換えている
int main(){
double d=2.0;
double* pd = &d
square(pd) ;
printf(”%f\n”, d);
}
4
d
pd
呼び出し元のdが
書き換わる
関数の名前
機能と構造
洗濯ばさみ (clothes-pin)
機能:洗濯物を挟んで吊るすための物
構造:2つの棒がバネでつながっている。
望遠鏡 (telescope)
機能:遠くのものを見る
構造:筒に凸レンズと凹レンズが
はまっている。
→名前は機能を表す。
関数の名前
良い名前とは?
関数の名前も、機能を表すのが良い。
関数の機能:ランダムな質点を返す
struct Mass randMass(){
struct Mass m;
m.px = rand() % 60; m.py = rand() % 20;
m.vx = 20; m.vy = 20;
return m;
}
関数の構造(動作):
構造体mを作って、 m.px に rand()が返した
値を60で割った値を代入し …
関数の名前
良いプログラム→よい関数
 よい関数とは、どんな関数か
 機能がわかりやすい
→ 名前は重要
わかりやすい名前がつけられるような機能に
なるように、関数を設計する。
→ mainの中から、名前がつけられそうな部分
を切り出して、関数にする。
 名前をつけるときは、英単語を調べましょう。
 構造がわかりやすい
→ あまり長すぎるのは良くない。
あまり短いと関数にする意味が無い。
 数行~画面3枚分(90行くらい)が良い。
構造体のメンバーとポインタ
ポインタは、構造体のメンバーも指せます。
struct Mass {
double px; double py;
double vx; double vy;
};
int main(){
struct Mass m;
struct Mass* pm;
pm = &m;
double* pd;
pd = &m.vx;
}
pm
pd
m.px
m. py
m. vx
m. vy
m
ポインタと配列
ポインタをずらす
 ポインタに整数を足すと、足した分だけポインタがずれる。
 配列は、メモリ上に詰まって並んでいるので、
ひとつずらすと次の要素のアドレスになる。
char cs[3];
short ss[3];
int main(){
char* pc = &cs[0];
*pc = ’a’;
*(pc+1) = ’b’;
*(pc+2) = ’c’;
short* ps = &ss[0];
*ps = 0;
*(ps+1) = 1;
*(ps+2) = 2;
}
pc
pc+1
pc+2
ps
ps+1
ps+2
0x00000000 0 1 0 1 1 0 0 1
0x00000002
0x00000003
0x00000004
cs[0] ’a’
0x00000005
cs[1] ’b’
0x00000006
cs[2] ’c’
0x00000007
0x00000008
ss[0] 0
0x00000009
0x0000000A
ss[1] 1
0x0000000B
0x0000000C
ss[2] 2
0x0000000D
char* なポインタは1byteずつ
short*なポインタは2byteずつずれる
(int*, float* は4byte, double* は8byte)
ポインタと配列
関数で配列を渡す
 関数に配列を渡すことはできないが、
配列のアドレスを渡すことはできる。
// 文字列のコピー
void strcpy(char* pdest, char* psrc){
int i;
for(i=0;*(psrc+i); ++i){
*(pdest+i) = *(psrc+i);
}
*(pdest+i) = 0;
}
int main(){
char src[7]=”string”;
char dest[7];
char* psrc = &src[0];
char* pdest = &dest[0];
strcpy(pdest, psrc);
printf(src); printf(”\n”);
printf(dest); printf(”\n”);
}
&src[0]と書くのが面倒なので、
src を &src[0] と同じ値と決めました。
なので、
char src[7];
char* p = src; // p = &src[0];と同じ
とかけます。
ということは、
strcpy(&dest[0], &src[0]); の代わりに、
strcpy(dest, src); と書けます。
けれども、
配列が引数として渡されるのではないです。
配列の先頭のアドレスがポインタの引数に
渡されるだけです。
ポインタと配列
ポインタを配列っぽく使う
見た目と実態の違いに気をつけてください。
一見配列に見えますが、違うものです。
// 文字列のコピー
void strcpy(char* pdest, char* psrc){
int i;
for(i=0; psrc[i]; ++I){
pdesc[i] = psrc[i];
}
pdesc[i] = 0;
}
int main(){
char src[7]=”string”;
char dest[7];
strcpy(dest, src);
printf(src); printf(”\n”);
printf(dest); printf(”\n”);
}
一見、src, descは配列に見えますが、
配列ではなく、ポインタです。
src[i]は *(src+i)と同じです。
*(src+i)と書くのが面倒なので、
src[i]と書いても良いことにしました。
ポインタと配列
文字列は文字の配列
int main(){
char msg[] = ”string”;
char * p = msg;
p = ”abc”;
printf(”Hello world!\n”);
}
int printf(char* pstr, …)
p = &msg[0]
p
msg[0] : ’s’
msg[1] : ’t’
msg[2] : ’r’
msg[3] : ’i’
msg[4] : ’n’
msg[5] : ’g’
msg[6] : 0
’a’
’b’
’c’
0 名前のない配列
(グローバル変数)
’H’
’e’
’l’
’l’
:
名前のない配列
(グローバル変数)
よくわかんなければ、今はいいです。(これでやっと ”文字列“の説明ができました。)
ポインタと配列
課題21 文字列追加関数
文字列の後ろに文字列を追加する関数
void strcat(char* pdest, char* psrc);
を作って、使ってみてください。
// 文字列を後ろにくっつける
void strcat(char* pdest, char* psrc){
ここは考えてください。
}
int main(){
char dest[20]="abcd";
strcat(dest, "efgh");
printf(dest); printf("\n");
char dest2[20]="0123456789";
strcat(dest2, "ABCDEF");
printf(dest2); printf("\n");
}
abcdefgh
0123456789ABCDEF
配列は、十分長く。
足りないと事故がおきます。
もし、char dest[5];だと
はみ出してしまう。
ほかの変数や
最悪の場合
プログラムを
壊すことも
dest[0] ’a’
dest[1] ’b’
dest[2] ’c’
dest[3] ’d’
dest[4] ’e’
こんな dest[5] ’f’
変数は dest[6] ’g’
dest[7] ’h’
無い
dest[8] 0
ポインタと配列
キャスト
 型を変えることをキャストといいます。
int main(){
int a;
double d=10.4;
a = (int)d;
}
(型) 変数 と書くと、変数を(型)に書いた
型にキャストできます。
普通に、a=10になります。
 ポインタをキャストすると変なことができます。
int main(){
double d = 0.2;
double* pd = &d;
int* p = (int*) pd;
int a[2];
a[0] =*p;
a[1] =*(p+1);
printf(”%x %x\n”,
a[0], a[1])
}
pdを無理やり int* に変換
pd: 0x10
p:
0x10
p+1: 0x14
0x10
0x11
0x12
0x13
0x14
0x15
0x16
0x17
0x18
d
doubleの中がどうなってるか見ることができる。
sizeof 演算子
変数のサイズを返す演算子です。
struct S{
char c;
int i;
};
int main(){
printf("sizeof(char) = %d\n", sizeof(char));
printf("sizeof(int) = %d\n", sizeof(int));
printf("sizeof(double) = %d\n", sizeof(double));
double d;
printf("sizeof d = %d\n", sizeof d);
short a[10];
printf("sizeof a = %d\n", sizeof a);
printf("sizeof a[0] = %d\n", sizeof a[0]);
printf("length of a is %d\n", sizeof a / sizeof a[0]);
sizeof(char) = 1
sizeof(int) = 4
sizeof(double) = 8
sizeof d = 8
sizeof a = 20
sizeof a[0] = 2
length of a is 10
sizeof s = 8
10 0 0 0 20 20 20 20
struct S s;
printf("sizeof s = %d\n", sizeof s);
s.c = 0x10;
s.i = 0x20202020;
char* p = (char*)&s;
int i;
for(i=0; i<sizeof(s); ++i){
printf(" %x", *(p+i));
}
return 0;
}