Modern C++編【言語解説】 第2章 C言語との差異

先頭へ戻る

この章の概要

この章の概要です。

仮引数省略の意味

前の章でも触れましたが、C++ では関数の仮引数の void は省略できます。

#include <iostream>

void hello()
{
    std::cout << "Hello" << std::endl;
}

int main()
{
    hello();
}

実行結果:

Hello

C++ では、仮引数が空になっている場合の意味は、C言語で void と指定したときと同じになります。 また、void と明示的に書いても、やはり同じ意味になります。 ですから、C++ では面倒で無意味な入力を避けて、void は省略するのが一般的です。

一方、C言語で仮引数を空にすると、「引数は何でも良い」という意味になります。 これは関数プロトタイプとして機能しないことを意味しており、一般的にいって危険な使い方です。 ですから、C言語では必ず void と明示するべきです。

戻り値の型の省略の意味

C言語(C95以前) では、関数定義の際に戻り値の型の指定を省略すると、int を指定したものとみなされますが、 C++ では、関数定義の際に戻り値の型の指定を省略することはできません
C言語でも、C99以降は省略できなくなっています。

#include <iostream>

hello()  // コンパイルエラー
{
    std::cout << "Hello" << std::endl;
}

int main()
{
    hello();
}

文字定数の大きさ

文字定数(文字リテラル)は、C言語では int型として扱われますが(C言語編第20章参照)、 C++ では char型として扱われます。

#include <iostream>

int main()
{
    char a = 'A';

    std::cout << sizeof('A') << "\n"
              << sizeof(a) << std::endl;
}

実行結果:

1
1

このルール変更は、関数のオーバーロード(第17章)をうまく行うために必要だからです。 'A' が 65 という int型とみなされてしまうのでは不便になってしまいます。

ところで、上のプログラムで、std::cout を使って整数が出力できていることが分かると思いますが、 このように、型の判断までも自動的に行ってくれることが、printf関数と決定的に異なるところです。

文字列定数の型

C++ の文字列定数(文字列リテラル)の型は、const char[] です。 const が付いていますから、char*型の変数に代入することはできません。

C言語では、文字列定数の型は char[] であるため、char*型の変数に代入することができます。 しかし、そのポインタを経由して、文字列定数を書き換える行為には、動作の保証がありません。

#include <iostream>

int main()
{
    char* str = "abcde";
    str[2] = 'x';

    std::cout << str << std::endl;
}

VisualStudio、clang の動作

実際に、先ほどのサンプルプログラムを試すと、 VisualStudio 2017、clang 5.0.0 のいずれでも、コンパイルできてしまいますが、 当然正しく動作する保証はありません。


上記のように、言語の違いや、コンパイルの実情から混乱がありますが、 変数で受け取る必要があるときは、常に const を付けるように徹底しておけば間違いはありません。

struct、enum、unionキーワードの省略

C言語では、構造体タグを使って構造体変数を宣言する際、structキーワードが必要です(C言語編第26章参照)が、 C++ では省略できるようになりました。

struct Point2D {
    int  x;
    int  y;
};

int main()
{
    Point2D point;

    // 以下、省略
}

上のプログラムで、main関数の中で構造体変数 point を宣言する際に、structキーワードがありません。 C言語であれば、

struct Point2D point;

このように書かないといけません。

なお、このルールは、enum(C言語編第50章参照)や union(C言語編第55章参照)でも同様です。

void*型から任意のポインタ型への変換

C言語では、void*型から任意のポインタ型へ暗黙的に変換できますが、 C++ では明示的なキャストを必要とします。 逆に、任意のポインタ型から void*型への変換は、C言語でも C++ でも暗黙的に行えます。

#include <iostream>
#include <cstdlib>

int main()
{
    // キャストが必要
    int* p = (int*)std::malloc(sizeof(int));

    *p = 100;
    std::cout << *p << std::endl;

    std::free(p);
}

実行結果:

100

実際には、C++ では、このサンプルプログラムのような用途で、malloc関数を使うことはなく、 通常は、new演算子を使います(第16章)。

malloc関数の戻り値は void*型です。 C言語でこの関数を使う際、明示的にキャストせずとも、非void*型の変数で受け取ることができますが、 C++ ではキャストが必要になります。

なお、第19章で取り上げますが、C++ では新しいキャストの構文が追加されています。 本来は、新しい構文を使う方が良いですが、先ほどのサンプルプログラムのように、 C言語のキャストを使うことも可能です。

列挙型変数への整数の代入

C言語では、整数値を、列挙型の変数へ代入できますが、 C++ では明示的なキャストを必要とします。 逆に、列挙型の値から、整数型への変換は、C言語でも C++ でも暗黙的に行えます。

#include <iostream>

int main()
{
    enum Color {
        RED,
        GREEN,
        BLUE,
    };

    Color color = (Color)1;  // キャストが必要
    int v = BLUE;  // キャスト不要

    std::cout << color << "\n"
              << v << std::endl;
}

実行結果:

1
2

グローバルな const の結合規則

グローバルな const は、C言語では外部結合されますが、C++ では内部結合されます。
外部結合は、ある名前が、定義を行っている場所以外の翻訳単位(他のソースファイル)からでも使えることを意味し、 内部結合は使えないことを意味します。

C++ で、外部結合の constグローバル変数を作りたいときには、extern を付加するようにします。 また、extern を付加しない場合、必ず初期値を与える必要があります。

外部結合の定数を定義したいときには、ヘッダファイルに extern付きの定義を公開するようにします。 そして、どこかのソースファイルで1つだけ、extern無し・初期値付きの定義を書きます。 このとき、公開したヘッダファイルを先にインクルードして、extern付きの定義が見えるようにします。
例えば、次のような構成になります。

// data.cpp
#include "data.h"

const int DATA_VALUE = 100;
// data.h
#ifndef DATA_H
#define DATA_H

extern const int DATA_VALUE;

#endif
// main.cpp
#include <iostream>
#include "data.h"

int main()
{
    std::cout << DATA_VALUE << std::endl;
}

実行結果:

100

auto

C言語には、最早使い道のない古いキーワード auto がありますが(C言語編第22章)、 C++ では、新たな意味が与えられています。

C++ の auto の意味については、第19章で改めて解説します。

registerキーワードの非推奨化

C言語から存在している古いキーワード register(C言語編第22章)は、 C++ では使用が推奨されません。

C++17 (registerキーワードの削除)

C++17 では、registerキーワードは効果を持たなくなりました。 キーワード自体は予約語として残されています。


練習問題

問題① 次のプログラムは、文字と整数のどちらを出力しますか?

#include <iostream>

int main()
{
	std::cout << 'A' << std::endl;
}

問題② 次のプログラムを、C言語でも C++ でもコンパイルでき、同じ実行結果になるように書き換えて下さい。

#include <iostream>

enum Color {
	RED,
	GREEN,
	BLUE
};

int main()
{
	Color color = 0;

	color += 1;

	std::cout << color << std::endl;
}


解答ページはこちら

参考リンク

更新履歴

'2018/4/2 「VisualC++」という表現を「VisualStudio」に統一。

'2018/2/22 「サイズ」という表記について表現を統一。 型のサイズ(バイト数)を表しているところは「大きさ」、要素数を表しているところは「要素数」。

'2018/1/5 Xcode 8.3.3 を clang 5.0.0 に置き換え。

'2017/7/30 clang 3.7 (Xcode 7.3) を、Xcode 8.3.3 に置き換え。

'2017/7/4 新規作成。





前の章へ(第1章 Hello, Worldプログラム)

次の章へ(第3章 C言語の機能の強化)

Modern C++編のトップページへ

Programming Place Plus のトップページへ


このエントリーをはてなブックマークに追加
rss1.0 取得ボタン RSS