見出し画像

C言語教室 第16回 - 構造体あれこれ

前回は構造体の基本を説明しましたが、構造体を使いこなすにはもう少し覚えることがあります。

まずは構造体へのポインタについてです。構造体を参照するときには、構造体ごとのポインタ型を使うことになります。構造体へのポインタからメンバ変数を読み書きするためには、”.”ではなくて”->”でポインタからメンバ変数を指し示します。

#include <stdio.h>
#include <math.h>

struct point {
  double x;
  double y;
};

double length(struct point *p1, struct point *p2) {
  double dx = p2->x - p1->x;
  double dy = p2->y - p1->y;
  return sqrt(dx * dx + dy * dy);
}

void main() {
  struct point pos1;
  struct point pos2;
  double len;

  pos1.x = 0.0;
  pos1.y = 0.0;
  pos2.x = 3.0;
  pos2.y = 4.0;

  len = length(&pos1, &pos2);

  printf(“distance between two points:(%f,%f)-(%f,%f)=%f\n”,
    pos1.x, pos1.y, pos2.x, pos2.y, len);
}

このコードを走らせると以下の出力が得られます。

distance between two points:(0.000000,0.000000)-(3.000000,4.000000)=5.000000

main関数とlength関数それぞれでの構造体メンバ変数へのアクセスの書き方が違うことがわかりましたか。構造体の場合、ひとつの変数のサイズが大きくなりがちなので、このように参照渡しにすることで、コピーではなくアドレスを渡すだけになり、ちょっとだけパフォーマンスが良くなったりします。パフォーマンス云々よりも、配列の例でも出てきた通り、参照渡しにすることで、呼び出した関数の中で、元々の構造体変数の値を変更することも出来ます。

注意が必要なのは、ポインタ変数にNULLなどの無効なアドレスが入っていると、”->”演算子で遠慮なく落ちます。サンプルコードではNULLチェックを入れていませんが、初期化忘れなどで無効なアドレスになっていないことを、よくよく確認してください。


次は代入です。カーニハン時代の古いC言語の説明には、構造体の値渡しが出来なかったり、コピーや代入ができないような記述があるかもしれませんが、今のC言語では単に = を使って代入ができます。

struct point p1;
struct point p2;

p1.x = 0.0;
p1.y = 0.0;
p2 = p1;

構造体変数の代入を行うと、変数に含まれているすべてのメンバ変数がコピーされます。また構造体へのポインタ変数は、他のポインタ変数と変わらないので、変数の中身ではなく、アドレスがコピーされます。指している先の構造体変数の中身がコピーされるわけではないのは、文字列の時と変わりません。

struct point p1;
struct point *pp;

p1.x = 0.0;
p1.y = 0.0;
pp = &p1;

これで、pp->x で p1.x を指すので、pp->x の値も 0.0 になり、pp->x に 1.0 を代入すれば p1.x も 1.0 になるわけです。この文の意味がわかればもう大丈夫です。


2つの構造体変数の内容が一致するかどうかを調べるには、メンバ変数ひとつひとつが一致しているか調べるしかありません。これも文字列と同じですね。

#include <stdio.h>

struct point {
  double x;
  double y;
};

int compare_point(struct point *p, struct point *q) {
  return (p->x == q->x) && (p->y == q->y);
}

void main() {
  struct point p1;
  struct point p2;
  int r;

  p1.x = 0.0;
  p1.y = 0.0;
  p2.x = 1.0;
  p2.y = 1.0;

  r = compare_point(&p1, &p2);

  printf(“Compare point(%f,%f) and (%f,%f):%d\n”, p1.x, p1.y, p2.x, p2.y,  r);
}

実行すると

Compare point(0.000000,0.000000) and (1.000000,1.000000):0

が出力されるはずです。


最後に構造体変数の初期化についてです。これも処理系によっては初期化が出来ないこともあるにはあるのですが、基本的には配列と同じように初期化することができます。

struct point p1 = {0.0, 0.0};
struct point p2 = {1.0, 1.0};

もちろん構造体の中に構造体があったり、配列になっている場合は、{} を入れ子にします。メンバ変数の数より値が足りない場合も、配列と同じで足りない部分には 0 が代入されます。

 struct point p1 = {0}; // すべてのメンバ変数が 0 で初期化される。

次回は構造体と似て非なる共用体についてです。


課題
商品コードと価格をメンバ変数に持つ構造体を定義して、この構造体の配列を作成し、適当な内容を設定してから、この構造体配列の内容を出力するような関数を書きなさい。

ヘッダ画像は、いらすとや さんより
https://www.irasutoya.com/2020/08/blog-post_245.html


いいなと思ったら応援しよう!