CとC++の互換性

プログラミング言語の比較

C言語C ++ 言語は密接に関連していますが、多くの重要な違いがあります。C++言語は、初期の標準化前のC言語から派生したものとして始まり、当時のCコンパイラとソースコードとリンクの互換性がほぼ確保されるよう設​​計されました。[1] [2] このため、両言語の開発ツール(IDEコンパイラなど)は単一の製品に統合されることが多く、プログラマーはソース言語としてCまたはC++を指定できます。

しかし、CはC++のサブセットではなく[3]、複雑なCプログラムは変更を加えずにC++コードとしてコンパイルすることはできません。同様に、C++はCにはない多くの機能を導入しており、実際にはC++で書かれたコードのほとんどはCコードに準拠していません。しかし、この記事では、準拠しているCコードがC++の不正な形式になる原因、あるいは両言語では準拠/整形式であるにもかかわらずCとC++で異なる動作をするという原因となる相違点に焦点を当てます。

C++の創始者であるビャーネ・ストラウストルップは CとC++の相互運用性を最大限に高めるためには、両言語間の非互換性を可能な限り削減すべきだと提言している[4]。一方で、CとC++は異なる言語であるため、互換性は有用ではあるものの必須ではないと主張する者もいる。この立場によれば、非互換性を削減する努力は、それぞれの言語を個別に改良する試みを妨げるべきではない。1999年のC標準(C99)の公式な根拠は、「CとC++の最大共通サブセットを維持するという原則を支持しつつ、両者の区別を維持し、それぞれが独立して進化することを許容する」ことであり、その作成者たちは「C++を大規模で野心的な言語として受け入れる」と述べている[5] 。

C99の追加機能の中には、可変長配列、ネイティブ複素数型、restrict 型修飾子など、現在のC++標準ではサポートされていないものや、C++の機能と競合するものがいくつかあります。一方で、C99は//コメントや宣言とコードの混在といったC++の機能を組み込むことで、C89と比較していくつかの非互換性を軽減しました。[6]

C では有効だが C++ では有効ではない構造

C++はCよりも厳格な型付け規則(静的型システム[1]の暗黙的な違反なし)と初期化要件(スコープ内変数の初期化が破棄されないことをコンパイル時に強制)[7]を課すため、Cで有効なコードがC++では無効になる場合があります。これらの根拠は、ISO C++標準の附属書C.1に記載されています。[8]

  • よく見られる違いの一つは、C言語の方がポインタに関して型付けが弱いvoid*ことです。具体的には、C言語ではポインタをキャストなしで任意のポインタ型に代入できますが、C++ではできません。この慣用句はmalloc、メモリ割り当て[9]を使用するC言語コードや、POSIX pthreads APIやコールバックを含むその他のフレームワークへのコンテキストポインタの受け渡しでよく見られます。例えば、以下のコードはC言語では有効ですが、C++では有効ではありません。
    void * ptr ; // void* から int* への暗黙的な変換int * i = ptr ; 
    
       
    

    または同様に:

    int * j = malloc ( 5 * sizeof * j ); // void* から int* への暗黙的な変換       
    
    

    コードをCとC++の両方でコンパイルできるようにするには、次のように明示的なキャストを使用する必要があります(両方の言語でいくつかの注意点があります)。[10]

    void * ptr ; int * i = ( int * ) ptr ; int * j = ( int * ) malloc ( 5 * sizeof * j ); 
        
           
    
  • int ** C++ には、への代入は許可するconst int *const *が への安全でない代入は許可しないという修飾子を追加するポインタ代入に関するより複雑なルールがありますconst int **。一方、C ではどちらも許可されません (ただし、コンパイラは通常、警告のみを発します)。
  • C++は、C標準ライブラリ関数の一部を変更し、const 型修飾子付きのオーバーロード関数(例えばCのstrchr戻り値)を追加します。C++では、2つのオーバーロード関数と1つのchar *オーバーロード関数があるかのように動作します。C23ではジェネリック選択を使用して、Cの動作をC++の動作に近づけています。[11]const char *strchr(const char *)char *strchr(char *)
  • C++ は列挙型への変換に関してもより厳密です。C のように、int を暗黙的に列挙型に変換することはできません。また、C23より前では、列挙定数(enum列挙子) は C では常に 型でしたintが、C++ (および C23 以降) では異なる型であり、 とは異なるサイズになる場合がありますint[更新が必要]
  • C++ ではconst変数を初期化する必要がありますが、C では必要ありません。
  • C++ コンパイラは、次の C99 コードのように、goto または switch が初期化を横切ることを禁止します。
    void fn(void)
    {
        goto flack;
        int i = 1;
    flack:
        ;
    }
    
  • While syntactically valid, a longjmp() results in undefined behaviour in C++ if the jumped-over stack frames include objects with nontrivial destructors.[12] The C++ implementation is free to define the behaviour such that destructors would be called. However, this would preclude some uses of longjmp() which would otherwise be valid, such as implementation of threads or coroutines switching between separate call stacks with longjmp() — when jumping from the lower to the upper call stack in global address space, destructors would be called for every object in the lower call stack. No such issue exists in C.
  • C allows for multiple tentative definitions of a single global variable in a single translation unit, which is invalid as an ODR violation in C++.
    int n;
    int n = 10;
    
  • In C, declaring a new type with the same name as an existing struct, union or enum is valid, but it is invalid in C++, because in C, struct, union, and enum types must be indicated as such whenever the type is referenced whereas in C++, all declarations of such types carry the typedef implicitly.
    enum Bool { FALSE, TRUE };
    typedef int Bool;
    
  • Non-prototype (K&R-style) function declarations are invalid in C++; they are still valid in C until C23,[13][14] although they have been deemed obsolescent since C's original standardization in 1990. (The term "obsolescent" is a defined term in the ISO C standard, meaning a feature that "may be considered for withdrawal in future revisions" of the standard.) Similarly, implicit function declarations (using functions that have not been declared) are not allowed in C++, and have been invalid in C since 1999.
  • C23までのCでは、[15]パラメータなしの関数宣言(例:int foo();)は、パラメータが指定されていないことを意味します。したがって、このような関数を1つ以上の引数(例: )でfoo(42, "hello world")呼び出すことは正当です。対照的に、C++では、引数なしの関数プロトタイプは関数が引数を取らないことを意味し、このような関数を引数で呼び出すのは不正な形式です。Cで引数を取らない関数を宣言する正しい方法は、 のように「void」を使用することであり、int foo(void);これはC++でも有効です。空の関数プロトタイプはC99では非推奨の機能です(C89でも同様でした)。
  • C と C++ の両方でネストされた型を定義できますstructが、スコープの解釈が異なります。C++ では、ネストされた型はstruct外側のスコープ/名前空間内でのみ定義されますstructが、C では、内側の構造体は外側の構造体の外側でも定義されます。
  • C ではstruct、、unionおよびenum型を関数プロトタイプで宣言できますが、C++ では宣言できません。

C99およびC11では、複素数、可変長配列 (複素数と可変長配列は C11 ではオプションの拡張機能として指定されています)、柔軟な配列メンバーrestrictキーワード、配列パラメータ修飾子、複合リテラルなど、 C++20の時点では標準 C++ に組み込まれていないいくつかの追加機能が C に追加されました

  • float complexおよびプリミティブデータ型を用いた複素数演算は、 C99標準でキーワードおよび便利なマクロを介してdouble complex追加されました。C++では複素数クラスを用いて複素数演算を実行できますが、この2つの方法はコード互換性がありません。(ただし、 C++11以降の標準ではバイナリ互換性が求められています。)[16]_Complexcomplex
  • 可変長配列。この機能により、sizeof演算子はコンパイル時に実行されなくなる可能性があります。[17]
    void foo ( size_t x , int a [ * ]); // VLA宣言void foo ( size_t x , int a [ x ]) { printf ( "%zu \n " , sizeof a ); // sizeof(int*)と同じchar s [ x * 2 ]; printf ( "%zu \n " , sizeof s ); // x*2を出力する}      
        
    
           
           
           
    
    
  • 複数のメンバーを持つC99構造体の最後のメンバーは、長さが指定されていない配列の構文形式をとるフレキシブル配列メンバーになることができます。これは可変長配列と同様の目的を果たしますが、VLAは型定義に記述できず、VLAとは異なり、フレキシブル配列メンバーのサイズは定義されていません。ISO C++にはこのような機能はありません。例:
    構造体X { int n , m ; charバイト[]; }; 
    
          
         
    
    
  • C99で定義された型restrict 修飾子はC++03標準には含まれていませんでしたが、GNUコンパイラコレクション[18] Microsoft Visual C++Intel C++コンパイラなどの主流のコンパイラのほとんどは、拡張機能として同様の機能を提供しています。
  • 関数内の配列パラメータ修飾子は C ではサポートされていますが、C++ ではサポートされていません。
    int foo ( int a [ const ]); // int *const a と同等int bar ( char s [ static 5 ]); // s が少なくとも 5 文字の長さであることを注釈します       
        
    
  • C の複合リテラルの機能は、構文と意味に多少の違いはあるものの、C++11 のリスト初期化構文によって組み込み型とユーザー定義型の両方に一般化されています。
    struct X a = ( struct X ){ 4 , 6 }; // C++ではX{4, 6}となります。C99で使用されるC構文は、GCCおよびClang C++コンパイラの拡張機能としてサポートされています。foo ( & ( struct X ){ 4 , 6 }); // オブジェクトはスタックに割り当てられ、そのアドレス関数に渡すことができます。これはC++ではサポートされていません。       
              
    
    if ( memcmp ( d , ( int []){ 8 , 6 , 7 , 5 , 3 , 0 , 9 }, n ) == 0 ) {} // C++ では次のようになります: // digits = int[] を使用します。// if (memcmp(d, digits{8, 6, 7, 5, 3, 0, 9}, n) == 0) {}             
    
    
    
    
  • 配列の指定初期化子はC でのみ有効です。
    char s [ 20 ] = { [ 0 ] = 'a' , [ 8 ] = 'g' }; // Cでは許可されているが、C++では許可されていない            
    
  • C++では戻り値のない関数はnoreturn 属性を用いて注釈付けできますが、Cでは別のキーワードが使用されます。C23では、属性構文もサポートされています。[19]
  • C(C2Y以降)には、名前付きループと名前付きbreak文(Javaに類似)の機能があります。この機能はC++には追加されていません。[20]

C++は、新機能をサポートするために多数のキーワードを追加しています。そのため、これらのキーワードを識別子に使用したCコードはC++では無効になります。例えば、次のコードは有効なCコードですが、キーワードtemplatenew、 がclass予約語であるため、C++コンパイラによって拒否されます。

構造体テンプレート{ int new ;構造体テンプレート*クラス; }; 

     
      

CとC++で動作が異なる構成要素

C と C++ の両方で有効であるが、2 つの言語で異なる結果を生成する構文構造がいくつかあります。

  • などの文字リテラルは'a'、Cでは型int、C++では 型ですchar。つまり、 はsizeof 'a'通常、2つの言語で異なる結果を返します。C++では 、1Cでは となりますsizeof(int)。この型の違いによるもう1つの結果として、Cでは が符号付きか符号'a'なしかに関わらず、 は常に符号付き式になりますがchar、C++ではこれはコンパイラ実装に依存します。
  • constC++では、明示的に宣言されない限り、名前空間スコープの変数に内部リンケージを割り当てます。これは、ファイルスコープのエンティティすべてにデフォルトで割り当てられるexternCとは異なりますextern。実際には、これは同一のCとC++コード間で意味的な変化を暗黙的にもたらすのではなく、コンパイル時エラーまたはリンケージエラーにつながります。
  • C では、インライン関数を使用するには、非インラインバージョンがリンクされるようにするために、1 つの翻訳単位で extern キーワードを使用して関数のプロトタイプ宣言を手動で追加する必要がありますが、C++ ではこれを自動的に処理します。さらに詳しく説明すると、C ではinline関数の定義を 2 種類に区別します。通常の外部定義 (externが明示的に使用される) とインライン定義です。一方、C++ は、インライン関数に対してインライン定義のみを提供します。C では、インライン定義は内部 (つまり静的) 定義に似ており、同じプログラム内で 1 つの外部定義と、他の翻訳単位にある同じ関数の任意の数の内部定義とインライン定義 (すべて異なっていてもよい) と共存できます。これは関数のリンケージとは別の考慮事項ですが、独立したものではありません。C コンパイラには、同じ関数のインライン定義と外部定義の両方が参照可能である場合、どちらを使用するかを選択する裁量が与えられています。しかし、C++では、外部リンケージを持つ関数がinlineいずれかの翻訳単位で宣言されている場合、その関数が使用されるすべての翻訳単位でも宣言(および定義)され、その関数のすべての定義がODRに従って同一である必要があります。静的インライン関数は、CとC++で同じように動作します。
  • C (C99 以降) と C++ はどちらも定数とを持つブール型 を持っていますが、定義が異なります。 booltruefalse
    • C++ では、は組み込み型であり、予約キーワードboolです
    • C99では、新しいキーワード が_Bool組み込みブール型として導入されました。ヘッダーには、それぞれとして定義されるstdbool.hマクロbooltrueが提供されています。したがって、と は型を持ちますfalse_Bool10truefalseint
    • ただし、C23ではbool、、、truefalseキーワードであり、trueと はfalse型 を持ちますbool
  • C++には、単一のUTFコード単位をエンコードするための型char8_t、、char16_tがあります。C23にはこれらの型が含まれていますが、これらは独自の組み込み型ではなく、他の整数型へのtypedefとして定義されているため、その名前は予約語ではありません。char32_t
  • C では、型のビット フィールドintが符号付きか符号なしかは実装によって定義されますが、C++ では、常に基礎となる型と一致するように符号付きになります。

前のセクションとのその他の違いもいくつか利用することで、両方の言語でコンパイル可能でありながら動作が異なるコードを作成できます。例えば、次の関数はCとC++で異なる値を返します。

外部int T ;  

int size ( void ) { struct T { int i , j ; }; return sizeof ( T ); // C: return sizeof(int) // C++: return sizeof(struct T) } 

          
    
     
    
    

これは、Cではstruct構造体タグの前に が必要である(つまりsizeof(T)変数を参照する)のに対し、C++では省略可能である(つまりsizeof(T)暗黙の を参照するtypedef)ためです。宣言を関数内に配置すると結果が異なることに注意してくださいextern。関数スコープ内に同じ名前の識別子が存在すると、暗黙の がtypedefC++では有効になりません。その結果はCとC++で同じになります。また、上記の例の曖昧さは、演算sizeof子に括弧を使用していることによることにも注意してください。 を使用すると、は型ではなく式であるとsizeof T想定されるためT、この例はC++ではコンパイルされません。

CとC++コードのリンク

CとC++はソースコードの互換性をほぼ維持していますが、それぞれのコンパイラが生成するオブジェクトファイルには重要な違いがあり、CとC++のコードを混在させるとそれが顕著になります。特に以下の点が挙げられます。

これらの理由から、C++コードでC関数 を呼び出すにはfoo()、C++コードはプロトタイプ宣言 する必要があります。同様に、CコードでC++関数 を呼び出すには、 のC++コードはで宣言する必要がありますfoo()extern "C"bar()bar()extern "C"

CとC++の互換性を維持するためのヘッダーファイルの一般的な方法は、その宣言をextern "C"ヘッダーのスコープ内にすることです。 [22]

foo.h

// C++コンパイラの場合はCリンケージを使用する
#ifdef __cplusplus 
extern "C" { #endif  


// これらの関数は C リンケージを取得します
void foo (); struct bar { /* ここで実装 */ }; 
 
    

// これが C++ コンパイラの場合、C リンケージを終了します
#ifdef __cplusplus 
} 
#endif

CとC++のリンケージ規約と呼び出し規約の違いは、関数ポインタをextern "C"使用するコードにも微妙な影響を与える可能性があります。一部のコンパイラは、宣言された関数ポインタが宣言されていないC++関数を指している場合、動作しないコードを生成しますextern "C"[23]

たとえば、次のコード:

void my_function (); 
外部"C" void foo ( void ( * fn_ptr )( void ));    

ボイドバー 
{
   foo (私の関数);
}

Sun Microsystemsの C++ コンパイラを使用すると、次の警告が生成されます。

 $ CC -c test . cc   
 "test.cc" 6行目:警告(時代錯誤) : foo ( extern "C" void ( * )())呼び出しextern " C" void ( * )()仮引数 fn_ptrvoid ( * )()渡されています          
            
 

これは、my_function()が C リンケージおよび呼び出し規約で宣言されていないが、 C 関数 に渡されているためですfoo()

参照

参考文献

  1. ^ ab Stroustrup, Bjarne . 「C++プログラミング言語の概要」(『オブジェクト技術ハンドブック』(編集者:Saba Zamir))CRC Press LLC, Boca Raton. 1999. ISBN 0-8493-3135-8 (PDF) . p. 4. 2012年8月16日時点のオリジナルよりアーカイブ(PDF) 。 2009年8月12日閲覧
  2. ^ B.Stroustrup. 「CとC++:兄弟。C/C++ユーザージャーナル。2002年7月」(PDF) 。 2019年3月17日閲覧
  3. ^ 「Bjarne StroustrupのFAQ – CはC++のサブセットですか?」2019年9月22日閲覧
  4. ^ B. Stroustrup. 「CとC++:互換性の事例。C/C++ユーザーズジャーナル。2002年8月」(PDF)2012年7月22日時点のオリジナルよりアーカイブ(PDF) 。 2013年8月18日閲覧
  5. ^ 国際標準の根拠—プログラミング言語—C、Wayback Machineで2016年6月6日にアーカイブ、リビジョン5.10(2003年4月)。
  6. ^ 「C方言オプション - GNUコンパイラコレクション(GCC)の使用」gnu.org。2014年3月26日時点のオリジナルよりアーカイブ。
  7. ^ 「N4659: プログラミング言語C++の標準作業草案」(PDF) §付録C.1。2017年12月7日時点のオリジナルよりアーカイブ(PDF) 。(「明示的または暗黙的な初期化子を持つ宣言を飛び越えることは無効です(ブロック全体に入らない場合を除く)。…この単純なコンパイル時のルールにより、C++ では、初期化された変数がスコープ内にある場合、確実に初期化されていることが保証されます。」)
  8. ^ 「N4659: プログラミング言語C++の標準作業草案」(PDF) §付録C.1。2017年12月7日時点のオリジナルよりアーカイブ(PDF) 。
  9. ^ 「IBM Knowledge Center」。ibm.com
  10. ^ 「FAQ > mallocのキャスト - Cprogramming.com」。faq.cprogramming.com。2007年4月5日時点のオリジナルよりアーカイブ。
  11. ^ 「修飾子を保持する標準ライブラリ関数、v4」(PDF)
  12. ^ “longjmp - C++リファレンス”. www.cplusplus.com . 2018年5月19日時点のオリジナルよりアーカイブ。
  13. ^ 「WG14 N2432:識別子リストを含む関数定義のサポートを削除」(PDF)
  14. ^ 「2011 ISO C ドラフト規格」(PDF)
  15. ^ 「WG14 N 2841: プロトタイプのない関数宣言子はない」。
  16. ^ “std::complex - cppreference.com”. en.cppreference.com . 2017年7月15日時点のオリジナルよりアーカイブ。
  17. ^ “ISO CとISO C++の非互換性”. 2006年4月9日時点のオリジナルよりアーカイブ。
  18. ^ 制限付きポインター アーカイブ 2016年8月6日Wayback MachineよりGNU コンパイラコレクション (GCC) の使用
  19. ^ 「WG14-N2764 : noreturn 属性」(PDF) . open-std.org . 2021年6月21日. 2022年12月25日時点のオリジナルよりアーカイブ(PDF) .
  20. ^ 「情報技術 — プログラミング言語 — C」(PDF) . open-std.org . 2025年5月4日.
  21. ^ 「IBM Knowledge Center」。ibm.com
  22. ^ 「IBM Knowledge Center」。ibm.com
  23. ^ 「Oracleドキュメント」Docs.sun.com。2009年4月3日時点のオリジナルよりアーカイブ2013年8月18日閲覧。
  • C89 標準の観点から、文ごとに詳細に比較します。
  • ISO C と ISO C++ の非互換性、David R. Tribble (2001 年 8 月)。
  • Oracle (Sun Microsystems) C++ 移行ガイド、セクション 3.11、リンク スコープに関する Oracle/Sun コンパイラ ドキュメント。
  • Oracle: 同じプログラム内での C コードと C++ コードの混在、Steve Clamage (ANSI C++ 委員会委員長) による概要。

Retrieved from "https://en.wikipedia.org/w/index.php?title=Compatibility_of_C_and_C%2B%2B&oldid=1330287884"