構造体ポインタと->

構造体は値で渡すと丸ごとコピーされる

入門で書いた Student は、名前用の char name[32] と点数の int score を持っていました。これを関数に値で渡すと、36 バイト前後の中身がまるごとコピーされます。しかもコピーを書き換えているだけなので、関数の中で点数を直しても呼び出し元には反映されません。

前回までに使ってきたポインタ渡しは、ここでも同じように効きます。渡すのは番地ひとつだけで、しかも呼び出し元の実体をそのまま触れます。構造体こそポインタ渡しの本命だと思ってください。

-> はカッコを書かずに済ませるための記号

Student *p が構造体を指しているとき、その中の score を触るには「p が指す先」を取り出してからメンバを選びます。素直に書くとこうなります。

(*p).score = 80;

丸カッコが要ります。* よりも . のほうが強く結び付くので、カッコを外した *p.score は「p.score が指す先」という別の意味になり、コンパイルエラーになります。

このカッコが毎回うるさいので、C には専用の記号が用意されています。

p->score = 80;

-> は「指す先のメンバ」を一手で指します。(*p).scorep->score は完全に同じ意味で、コンパイル結果も同じです。実務の C コードはほぼ全部 -> で書かれているので、読めないと困ります。

ドットと矢印は、左側が何かで決まる

迷ったら記号の左側を見てください。左が構造体の実体ならドット、左がポインタなら矢印です。

Student st; Student *p = &st; st.score = 80; /* 左は実体なのでドット */ p->score = 90; /* 左はポインタなので矢印 */

stp は別の変数ですが、pst を指しているので、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 章でやった幅の指定は、構造体のメンバでもそのまま必要です。

要件

  1. add_point は受け取ったポインタが指す学生の score に delta を足す
  2. メンバの読み書きは -> を使う。(*p).score の形は使わない
  3. scanf の書式は %31s と %d を使い、名前の長さを制限する
  4. 出力は「名前 点数」を半角スペース区切りで1行

入出力例

main("佐藤 80")"佐藤 85" main("鈴木 90")"鈴木 95"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.c
学習モード

メモ

構造体ポインタと->

⌘S で保存