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言語編第10章)。これは関数プロトタイプとして機能しないことを意味しており、一般的にいって危険な使い方です。ですから、C言語では必ず void と明示するべきです。

なお、「void」という記述を、意味を変えずに省略できるのは、仮引数の場合だけです。戻り値の型指定の省略は、C言語と同様に、int型を指定したことになります。

戻り値の型の省略の意味

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

C言語でも、C99以降は省略できなくなっています。

#include <iostream>

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

int main()
{
    hello();
}

文字定数の大きさ

文字リテラルは、C言語では int型ですが(C言語編第19章参照)、C++ では char型です。そのため、大きさも異なります。

#include <iostream>

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

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

実行結果:

1
1

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

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

文字列定数の型

C言語の文字列リテラルは char の配列型ですが、C++ では const な char の配列型です。そのため、C言語でよく行われていた char* の変数へ代入する行為はコンパイルできません。

#include <iostream>

int main()
{
    char* str = "abcde";  // コンパイルエラー

    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

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

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

キャストに関しても、C++ では新しい構文が追加されており(第9章)、それを使う方が良いのですが、C言語と同じ構文でも目的は果たせます。

任意のポインタ型から void*型への変換は、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_INCLUDED
#define DATA_H_INCLUDED

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指定子は効果を持たなくなりました。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/7/10 C++編の更新に合わせて、修正を加えた。

'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 のトップページへ


はてなブックマーク Pocket に保存 Twitter でツイート Twitter をフォロー
Facebook でシェア Google+ で共有 LINE で送る rss1.0 取得ボタン RSS
管理者情報 プライバシーポリシー