構造体ポインタと->
構造体は値で渡すと丸ごとコピーされる
入門で書いた Student は、名前用の char name[32] と点数の int score を持っていました。これを関数に値で渡すと、36 バイト前後の中身がまるごとコピーされます。しかもコピーを書き換えているだけなので、関数の中で点数を直しても呼び出し元には反映されません。
前回までに使ってきたポインタ渡しは、ここでも同じように効きます。渡すのは番地ひとつだけで、しかも呼び出し元の実体をそのまま触れます。構造体こそポインタ渡しの本命だと思ってください。
-> はカッコを書かずに済ませるための記号
Student *p が構造体を指しているとき、その中の score を触るには「p が指す先」を取り出してからメンバを選びます。素直に書くとこうなります。
(*p).score = 80;丸カッコが要ります。* よりも . のほうが強く結び付くので、カッコを外した *p.score は「p.score が指す先」という別の意味になり、コンパイルエラーになります。
このカッコが毎回うるさいので、C には専用の記号が用意されています。
p->score = 80;-> は「指す先のメンバ」を一手で指します。(*p).score と p->score は完全に同じ意味で、コンパイル結果も同じです。実務の C コードはほぼ全部 -> で書かれているので、読めないと困ります。
ドットと矢印は、左側が何かで決まる
迷ったら記号の左側を見てください。左が構造体の実体ならドット、左がポインタなら矢印です。
Student st;
Student *p = &st;
st.score = 80; /* 左は実体なのでドット */
p->score = 90; /* 左はポインタなので矢印 */st と p は別の変数ですが、p は st を指しているので、p->score を書き換えると st.score も 90 になります。コピーではなく本体を触っているからです。この 1 行が、ポインタ渡しした関数の中で値を直せる理由そのものです。
メンバを scanf に渡すときの &
char name[32] は配列なので、名前そのものが先頭の番地になります。scanf に渡すときは & を付けません。一方 int score は普通の変数なので & が要ります。
scanf("%31s %d", p->name, &p->score);&p->score は「p->score が置かれている番地」という意味です。-> のほうが & より強く結び付くので、カッコなしでこの読み方になります。%31s の 31 は、終端の \0 を含めて 32 バイトに収めるための上限です。ここを %s にすると、長い名前を打たれた瞬間に隣の score を壊します。第 3 章でやった幅の指定は、構造体のメンバでもそのまま必要です。
要件
- add_point は受け取ったポインタが指す学生の score に delta を足す
- メンバの読み書きは -> を使う。(*p).score の形は使わない
- scanf の書式は %31s と %d を使い、名前の長さを制限する
- 出力は「名前 点数」を半角スペース区切りで1行
入出力例
main("佐藤 80") → "佐藤 85"
main("鈴木 90") → "鈴木 95"