web-dev-qa-db-ja.com

C(99)とC ++(11)の互換性のない違いは何ですか?

この質問は Herb Sutterによる投稿 への返信によってトリガーされ、C99コンパイラをサポート/作成しないというMSの決定を説明しましたが、C(99)とにかくC++(11)標準にある機能。

1つ コメンターが返信

(...)Cは重要であり、少なくとも少し注意する必要があります。

有効なCであるが有効なC++ではない既存のコードがたくさんあります。そのコードは書き直されそうにありません(...)

私はMS C++でのみプログラミングしているので、「純粋な」Cをよく知りません。つまり、使用しているC++言語の詳細がC(99)と私はいくつかのC99コードがそのままC++コンパイラで機能しない手掛かりをほとんど持っていません。

C99のみのrestrictキーワードについて知っていることに注意してください非常に狭いアプリケーションを持っているように見えると可変長配列(それらがどれほど広まっているか、重要であるかはわかりません)。

また、重要な意味上の違いや問題があるかどうか、つまりC(99)コンパイルするコードC++(11)ではC++コンパイラとCコンパイラでは動作が異なります。


クイックリンク:回答からの外部リソース:

54
Martin Ba

CとC++の共通のサブセット(クリーンCと呼ばれることもあります)(C90ではない)から始める場合は、3種類の非互換性を考慮する必要があります。

  1. 合法的なCを違法にするC++の追加機能C++

    この例は、Cで識別子として使用できるC++キーワード、またはCで暗黙的であるがC++で明示的なキャストが必要な変換です。

    これがおそらく、MicrosoftがまだCフロントエンドを出荷している主な理由です。そうでなければ、C++としてコンパイルできないレガシーコードを書き直す必要があります。

  2. C++の一部ではない追加のC機能

    C++がフォークされた後、C言語は進化を止めませんでした。いくつかの例は、指定された初期化子およびrestrictである可変長配列です。これらの機能は非常に便利ですが、C++標準には含まれていません。

  3. CとC++の両方で使用できるが、セマンティクスが異なる機能

    この例は、constオブジェクトまたはinline関数のリンケージです。

C99とC++ 98の間の非互換性のリスト はこちら にあります(これはMatによってすでに言及されています)。

C++ 11とC11はいくつかの面で近づいてきました(可変長マクロはC++で使用可能になり、可変長配列はオプションのC言語機能になりました)、非互換性のリストも増えています(たとえば、Cとauto type-specifier in C++)。

余談ですが、MicrosoftはC(最近のものではありません)を放棄する決定を下しましたが、私が知る限り、オープンソースコミュニティの誰も実際にそれについて何かをするための対策を講じていません。 C-to-C++コンパイラーを介して最新のCの多くの機能を提供することは非常に可能です。特に、 それらの一部を実装するのは簡単です 。これは実際には、C99をサポートするComeau C/C++を使用して現在可能です。

ただし、それは本当に差し迫った問題ではありません。個人的には、WindowsでGCCとClangを使用することにかなり慣れており、MSVCの代わりにPelles CやIntelのコンパイラなどの独自の代替手段があります。

26
Christoph

古くから存在している非互換性(C90以前)がたくさんありますが、C99とC11には本当に素敵な機能がたくさんあります。これらはすべて私の頭の上にあります。

_// Valid C
int *array = malloc(sizeof(*array) * n);

// Valid C and valid C++, extra typing, it's always extra typing...
int *array = (int *) malloc(sizeof(*array) * n);

// Valid C++
int *array = new int[n];
_

C99は素晴らしいですし、どこでもCプログラマはそれを使うべきです

C99の新機能は、一般的なプログラミングにとって非常に優れています。 VLAとrestrictは(私の意見では)一般的な使用を対象としていませんが、主にFORTRANと数値プログラマーをCに移行させることを目的としています(ただし、restrictはオートベクトライザーを支援します)。 restrictを使用する準拠プログラムは、ファイルの先頭に_#define restrict_を付けた場合でもまったく同じように動作します(ただし、速度が遅くなる可能性があります)ので、大した問題ではありません。実際のところ、VLAは非常にまれです。

柔軟な配列メンバーは適切な場合があります。これらは可変長配列と同じではないことに注意してください!人々はこのトリックを何年も使用していますが、公式サポートによりタイピングが減り、コンパイル時に定数を作成できるようになります。 (以前の方法では、サイズ1の配列を使用していましたが、割り当てサイズの計算は本当に面倒です。)

_struct lenstr {
    unsigned length;
    char data[];
};
// compile time constant
const struct lenstr hello = { 12, "hello, world" };
_

指定された初期化子。タイピングの手間を省きます。

_struct my_struct { int a; char *b; int c; const char *d; };
struct my_struct x = {
    .a = 15,
    .d = "hello"
    // implicitly sets b = NULL and c = 0
};
int hex_digits[256] = { ['0'] = 0, ['1'] = 1, ['2'] = 2, /* etc */ ['f'] = 15 };
_

inlineキーワードの動作は異なります。そのユニットにextern宣言を追加することで、インラインで宣言された関数の非インラインバージョンを取得する変換ユニットを選択できます。

複合リテラル

_struct point { float x; float y; };
struct point xy_from_polar(float r, float angle)
{
    return (struct point) { cosf(angle) * r, sinf(angle) * r };
}
_

snprintf関数は、おそらくCで最も有用なライブラリ関数のトップ10に含まれています。C++にないだけでなく、MSVCランタイムは__snprintf_という関数しか提供しません。文字列へのNULターミネータの追加は保証されていません。snprintfはC++ 11に含まれていますが、MSVC Cランタイムにはまだありません。)

匿名の構造と 組合(C11、ただし永遠にGCC拡張)(C++ 03には匿名の共用体があり、CモードではMSVCはサポートされていません):

_struct my_value {
    int type;
    union {
        int as_int;
        double as_double;
    }; // no field name!
};
_

ご覧のとおり、これらの機能の多くは入力を大幅に節約する(複合リテラル)か、プログラムをデバッグしやすく(柔軟な配列メンバー)、間違いを回避しやすくします(指定された初期化子/構造体フィールドの初期化を忘れる)。これらは大幅な変更ではありません。

セマンティックの違いについては、エイリアシングルールが異なると確信していますが、最近のほとんどのコンパイラーは十分に寛容であり、どのようにテストケースを構築してデモンストレーションを行うかわかりません。誰もが求めるCとC++の違いは、古いsizeof('a')式です。これは、C++の場合は常に1ですが、32ビットCシステムでは通常4です。しかし、sizeof('a')が何であるかは誰も気にしません。ただし、C99標準には、既存の慣行を成文化するいくつかの保証があります。

次のコードを見てください。余分なストレージを無駄にせずに、Cで共用体型を定義するための一般的なトリックを使用します。 Ithinkこれは意味的に有効なC99であり、Ithinkこれは意味的に疑わしいC++、しかし私は間違っているかもしれません。

_#define TAG_FUNKY_TOWN 5
struct object { int tag; };
struct funky_town { int tag; char *string; int i; };
void my_function(void)
{
    struct object *p = other_function();
    if (p->tag == TAG_FUNKY_TOWN) {
        struct funky_town *ft = (struct funky_town *) p;
        puts(ft->string);
    }
}
_

残念ですが MSVCコードジェネレーターは素晴らしいですが、C99フロントエンドがないのは残念です。

29
Dietrich Epp

C++では、共用体の1つのメンバーを設定し、別のメンバーの値にアクセスすることは未定義の動作ですが、C99では未定義ではありません。

ウィキペディア page にリストされている他の多くの違いがあります。

2
bames53

C++ 11標準 の「C.1 C++とISO C」について触れます。そのドキュメントには、それぞれの違いとその開発への影響の打撃がありました。

2
emsr