答え
C++では、
malloc
の戻り値はそのままだと使えない!
Triangle *triangle = malloc(sizeof(Triangle));
こうやってキャストするのが正解!
Triangle *triangle = static_cast<Triangle *>(malloc(sizeof(Triangle)));
または、C言語方式のキャストでもOK!
Triangle *triangle = (Triangle *)malloc(sizeof(Triangle));
解説
まず、C++はC言語の上位互換ではないということを知っておきましょう。C言語のプログラムは大部分がC++としても正しいプログラムといえますが、例外もあるのです。
今回の問題は、void *
の扱いがC言語とC++とで異なることに起因しています。malloc()
の戻り値はvoid *
なので、そのままC++のプログラムとしてコンパイルすることはできません。
C言語では、こうなります。
- 任意の型のアドレスは、
void *
型のポインタに代入できる void *
型のアドレスは、任意の型のポインタに代入できる
これに対して、C++ではこうです。
- 任意の型のアドレスは、
void *
型のポインタに代入できる(C言語と同じ) void *
型のアドレスは、void *
型以外のポインタには代入できない(C言語と違う)
これは、危険な(タイプセーフではない)代入を抑制するためのルールです。とはいえ、コンパイラは開発者を信頼することになっているので、明示的にキャストすればコンパイルエラーは発生しなくなります。
C++のキャストには種類がありますが、ここではstatic_cast
を使うのが適切です。
Triangle *triangle = static_cast<Triangle *>(malloc(sizeof(Triangle)));
static_cast
はC++の文法よ。
キャストはC言語にもありますよね?
そうね。C言語と同じ方式のキャストを使ってもOKよ。
C++では、次のようにC言語方式のキャストも使用可能です。この書き方なら、同じソースファイルをC言語とC++どちらのコンパイラでコンパイルしてもエラーになりません。
Triangle *triangle = (Triangle *)malloc(sizeof(Triangle));
今はC言語を使っているけれど、いずれはC++に移行するつもりだということもあるでしょう。その場合は、あらかじめ(C言語方式で)キャストしておけば、移行がスムーズになります。
なお、C言語とC++のソースファイルが混在したままで開発を進めることも可能です。それぞれのコンパイラでコンパイルしたものを、リンクする方法があるためです。既存のソースファイルはあくまでC言語として扱い、C++から呼び出す方針にするのでもよいでしょう。
ここがポイント!
C言語のソースファイルをC++として使うには、手直しが必要になる場合がある!
修正後のプログラム
main.cpp
#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 = static_cast<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