第14問の答え

答え

malloc()が失敗するケースが考慮されていない!
  Triangle *triangle = malloc(sizeof(Triangle));
  triangle->base = 6.0;
  triangle->height = 4.0;
  ……
malloc()の戻り値は毎回チェックするのが正解!
  Triangle *triangle = malloc(sizeof(Triangle));
  if (triangle) {
    triangle->base = 6.0;
    triangle->height = 4.0;
    ……

解説

メモリーは有限なので、malloc()はメモリーの確保に失敗する場合があります。実際に失敗するかどうかはプログラムを実行してみないと分からないので、こういうエラーケースのことを「実行時エラー(ランタイムエラー)」といいます。実行時エラーは、毎回チェックするのが鉄則です。

malloc()は失敗すると0(NULL)を返すので、戻り値をif文でチェックすればよいでしょう。

  Triangle *triangle = malloc(sizeof(Triangle));
  if (triangle) {
    ……
ずるいですよ!三角形のくだり関係ないじゃないですかー。
あら、そうでもないかもしれないわよ?

実行時エラーを漏れなくチェックするというのは、なかなか面倒くさい作業です。何かほかのこと、例えば三角形の面積の計算式などに気を取られていると、つい忘れてしまうかもしれません。

それでも、こうしたチェックは必要なものです。プログラムを作るときは、いつもエラーケースのことを頭の片隅に入れておきましょう。

もしかして、「パッと見ただけで解ける人もいる」って、このことだったんですか?
そうね。いつもエラーケースに気を配っていると、「このmalloc()危ないなぁ」って分かるようになるのよ。
なるほどー。

なお、チェックが必要な箇所をなるべく減らすというのも、一つの考え方です。今回のプログラムの場合は、次のようにしてmalloc()の使用を避ける方法も考えられます。

int main(void) {
  Triangle triangle = {
    .base = 6.0,
    .height = 4.0
  };

  printf("area = %f\n", TriangleArea(&triangle));

  return EXIT_SUCCESS;
}
ここがポイント!
実行時エラーは毎回チェックしよう!(または、チェックが不要な方法で置き換えよう!)

修正後のプログラム

main.c
#include <stdio.h>
#include <stdlib.h>

typedef struct {
  double base;
  double height;
} Triangle;

double TriangleArea(const Triangle *triangle) {
  return triangle->base * triangle->height / 2.0;
}

int main(void) {
  Triangle *triangle = malloc(sizeof(Triangle));
  if (triangle) {
    triangle->base = 6.0;
    triangle->height = 4.0;

    printf("area = %f\n", TriangleArea(triangle));

    free(triangle);
    triangle = 0;
  }

  return EXIT_SUCCESS;
}
実行結果
area = 12.000000