メンバの読み書き
ドットは「この中の」という意味
前回作った struct Student は、中に name と score という 2 つのメンバを抱えています。そのどちらを触るのかを指定する記号が ドット です。
struct Student s = {"田中", 82};
printf("%d\n", s.score); // 82 が出る
s.score = 90; // 書き換えもできるs.score は「s の中の score」と読みます。取り出すときも書き換えるときも同じ書き方で、ふつうの int 変数とまったく同じように扱えます。s.score + 5 のような計算も、if (s.score >= 80) のような判定もそのままできます。
入力を直接メンバに入れる
scanf の受け取り先にもメンバをそのまま書けます。ここで第7章の知識が効いてきます。
scanf("%31s", s.name); // & は付けない
scanf("%d", &s.score); // & を付ける違いの理由は前章のとおりです。s.name は char の 配列 なので、書いただけで先頭のアドレスを表します。もう住所なので & は要りません。一方 s.score はただの int の値なので、書き込んでもらうには & を付けて住所を渡す必要があります。
%31s の 31 は、name が 32 個分しかないので 31 文字までで打ち切る、という安全装置でした。構造体の中に入っても配列の性質は変わらないので、この用心もそのまま必要です。
構造体は代入でまるごとコピーできる
配列は a = b という代入ができませんでした。ところが構造体は、メンバに配列を含んでいても、まるごと代入できます。
struct Student backup = s; // 名前も点数もコピーされるこれは中身が丸ごと複製された 別の変数 です。あとから backup.score を変えても s.score は動きません。名前と点数をひとかたまりで扱えることの、いちばん分かりやすい利点です。
この性質は、次々回に構造体を関数へ渡すときの動きにも直結します。関数の引数も代入と同じくコピーが渡るからです。
メンバ名は他と重ならない
もうひとつ気楽な点があります。メンバの名前は構造体の中だけで通用するので、外にある変数と同じ名前でも衝突しません。int score; という変数が別にあっても、s.score は必ず構造体の中の点数を指します。逆に言うと、s. を書き忘れて score とだけ書いたときは、外側の別の変数を触ってしまい、コンパイルが通ったまま値が更新されない、という間違いが起こります。点数が変わらないときは、まずドットの書き忘れを疑ってください。
では、入力した学生の点数に加点する練習をしましょう。
要件
- struct Student を name(char 32個の配列) と score(int) で定義する
- scanf で名前と点数を読み、メンバに直接入れる
- 1行目は「田中さんの点数は82点です」の形で表示する
- 2行目は加点後の点数を「加点後は87点です」の形で表示する
入出力例
main("田中 82") → "田中さんの点数は82点です
加点後は87点です"
main("佐藤", "70") → "佐藤さんの点数は70点です
加点後は75点です"