はじめての C

C programming note*1
座標上の 対角線の 長さを 計算する プログラムを 考え、その中に 「構造体を メンバとして 含む 構造体」を 組み込んでいきます。
ここでは、その長さを 求める 対角線は、座標上の 左上と 右下の 2点を むすぶ 直線だと 仮定しておきます。
はじめに 基本となる 「座標の 1つの点を 表わす」 構造体を つくります。

struct {
int x;
int y;
};
タグ名が ついていないのは、この構造体は 別の 構造体の メンバに するつもりであり、また 「後の コードで 使う」 予定が ないからです。
typedef を つかって、構造体に POINT という 名前を つけておきます。
typedef struct {
int x;
int y;
} POINT;
次に、この 「2つの点の データを 一組に 管理する 構造体」を 定義することで、見通しの よい プログラムを つくっていきます。
つまり、構造体の メンバとして 先の構造体を 含むように するわけです。
typedef struct {
POINT p1;
POINT p2;
} RECT;
POINT と 同じく、typedef を つかって RECT という 名を つけておきます。
それぞれの メンバを 定義するのは、ふつうの 構造体での それと 同じです。
RECT r;
r.p1.x = 2;
r.p1.y = 4;
r.p2.x = 6;
r.p2.y = 1;
対角線の 長さを 求めるため、「与えられた 2点間の 距離を (計算して) 返す」 関数を つくっていきます。
#include 

double distance(RECT r)
{
int dx;
int dy;
double dis;

dx = r.p1.x - r.p2.x:
dy = r.p1.y - r.p2.y;
dis = sqrt(dx * dx + dy * dy);

return dis;
}

関数 sqrt() は、与えられた 引数の 値の 平方根を 返し、引数が マイナスだと エラーに なります。
dx と dy は 座標では 直角三角形の 直角を はさんだ 2辺の 長さと 同じですから、それぞれの 値を 2乗したものの 和の 平方根が、三角形の 他の 1辺 つまり 対角線の 長さに なるわけです。
K&R 2nd では、

sqrt は double の 引数を 仮定しているから、不注意に 他の 型が 渡されると 無意味な 結果を 出す (p56)

ので、キャストを 使って double型に 変更するよう 警告しています。
しかし、sqrt() は マイナスの 値で なければ、引数が int型で あっても 正常に 計算してくれるようです。
また、引数に とる 値 - dx と dy - が マイナスに なっていても、簡単に 対角線の 長さが 求められる 関数 hypot() も あるので、そちらを 使うことも できます。
hypot() は ユークリッド距離関数と 呼ばれ、引数には 直角三角形の 直角を はさんだ 2辺の 長さを とります。

double hypot(double x, double y);
この関数を 使えば、対角線の 長さは 次のように 求められます。
dis = hypot(dx, dy);
構造体の メンバ p1 と p2 - 座標上の 2点 - は その位置を それぞれ 左上と 右下に 仮定していますが、実際の データでは そうでない 逆の 組み合わせも 考えられます。
そこで、この仮定の 条件に つねに 合うように、正規化 normalize する 関数も 用意しておきます。
void normalize_rect(RECT *r)
{
int tmp;

if (r->p1.x > r->p2.x) {
tmp = r->p1.x;
r->p1.x = r->p2.x;
r->p2.x = tmp;
}

if (r->p1.y < r->p2.y) {
tmp = r->p1.y;
r->p1.y = r->p2.y;
r->p2.y = tmp;
}
}

ここでは、各メンバを 表わすのに アロー演算子 (->) が 使われていますが、それは

データを 直接 変更するために、(関数の) 引数には RECT型の ポインタを 渡すように なっている (p206)

からです。
テスト用の プログラムは 次の とおり、

/* calc_dist.c */
#include <stdio.h>
#include <math.h>

void normalize_rect();
double distance();

typedef struct {
int x;
int y;
} POINT;

typedef struct {
POINT p1;
POINT p2;
} RECT;

main()
{
double dis;
RECT r;
r.p1.x = 6;
r.p1.y = 1;
r.p2.x = 2;
r.p2.y = 4;

puts("original:");
printf("a == (%d, %d)\n", r.p1.x, r.p1.y);
printf("b == (%d, %d)\n", r.p2.x, r.p2.y);

puts("normalize:");
normalize_rect(&r);
printf("a == (%d, %d)\n", r.p1.x, r.p1.y);
printf("b == (%d, %d)\n", r.p2.x, r.p2.y);

dis = distance(r);
printf("distance a to b: %0.2f\n", dis);

return 0;
}

void normalize_rect(RECT *r)
{
int tmp;

if (r->p1.x > r->p2.x) {
tmp = r->p1.x;
r->p1.x = r->p2.x;
r->p2.x = tmp;
}

if (r->p1.y < r->p2.y) {
tmp = r->p1.y;
r->p1.y = r->p2.y;
r->p2.y = tmp;
}
}

double distance (RECT r)
{
int dx;
int dy;
double dis;

dx = r.p1.x - r.p2.x;
dy = r.p1.y - r.p2.y;
dis = hypot(dx, dy);

return dis;
}

なお、ヘッダファイルの math.h を インクルードして、数学関連の 関数を 使うときには、コンパイルする際に 1つ 注意することが あります。
それは、コマンドラインの 末尾に 必ず -lm オプションを つけておく、と いうことです。
$ cc -o calc_dist calc_dist.c -lm
http://www.linux.or.jp/JF/JFdocs/archive/GCC-FAQ/index.html#AEN220
(gcc 以外のコンパイラでも、これは 適用されるはずです)

*1:「作ってわかる Cプログラミング」