19

Cの質問に投稿した回答にコメントがありました。元の質問では、コードは「移植可能」である必要があると述べていたため、コメント投稿者は、コードをC++コンパイラでコンパイルするように作成する必要があると提案しました。

これは「ポータブルC」の一般的な解釈ですか?その答えに対するさらなるコメントで述べたように、それは私にとって完全に驚くべきことです。移植性は完全に異なるものを意味すると考えており、合法的なC++でもあるCコードを書くことにはほとんどメリットがありません。

4

11 に答える 11

18

現在の C++ (1998) 標準には、C (1989) 標準が組み込まれています。型の安全性に関するいくつかの詳細は脇に置いておきます。つまり、「優れた」C89 は C++ コンパイラで正常にコンパイルされるはずです。

問題は、現在の C 標準が 1999 年の標準 (C99) であることです。これはまだ正式には C++ 標準の一部ではありません (AFAIK)(*)。つまり、C99 の「より優れた」機能 (long long int、stdint.h、...) の多くは、多くの C++ コンパイラでサポートされていますが、厳密には準拠していません。

「ポータブル」C はまったく別の意味であり、公式の ISO/ANSI 標準とはほとんど関係ありません。これは、コードがホスト環境を想定していないことを意味します。(int のサイズ、エンディアン、非標準関数、または errno 番号など。)

私がかつてクロスプラットフォームプロジェクトのために書いたコーディングスタイルガイドから:

クロスプラットフォーム DNA (仮定しないでください)

  • ネイティブのデータ型はありません。使用できるデータ型は、標準ライブラリで宣言されているものだけです。
  • char、short、int、および long は、float、double、および long double と同様に、それぞれ異なるサイズです。
  • int は 32 ビットではありません。
  • char は符号付きでも符号なしでもありません。
  • char は数値を保持できず、文字のみを保持します。
  • 短い型を長い型にキャストすると (int -> long)、CPU の整列規則が破られます。
  • int と int* はサイズが異なります。
  • int* と long* はサイズが異なります (他のデータ型へのポインターと同様)。
  • ネイティブのデータ型が存在しないことを覚えていますか?
  • 'a' - 'A' は 'z' - 'Z' と同じ結果になりません。
  • 'Z' - 'A' は 'z' - 'a' と同じ結果にはならず、25 にも等しくありません。
  • その値をテストすることを除いて、NULL ポインターでは何もできません。逆参照すると、システムがクラッシュします。
  • 符号付きと符号なしの両方の型を含む演算は機能しません。
  • データ型の整列規則はランダムに変更されます。
  • データ型の内部レイアウトはランダムに変化します。
  • オーバーフローとアンダーフローの特定の動作はランダムに変化します。
  • 関数呼び出し ABI はランダムに変化します。
  • オペランドはランダムな順序で評価されます。
  • このランダム性を回避できるのはコンパイラだけです。ランダム性は、CPU / OS / コンパイラの次のリリースで変更されます。
  • ポインターの場合、== と != は、まったく同じデータ型へのポインターに対してのみ機能します。
  • <, > は、同じ配列へのポインターに対してのみ機能します。これらは、明示的に宣言された unsigned の char に対してのみ機能します。
  • ネイティブのデータ型が存在しないことをまだ覚えていますか?
  • size_t (sizeof の戻り値の型) は、他のデータ型にキャストできません。
  • ptrdiff_t (1 つのポインターを別のポインターから減算した戻り値の型) は、他のデータ型にキャストできません。
  • wchar_t (「ワイド」文字の型で、その正確な性質は実装定義です) は、他のデータ型にキャストできません。
  • ..._t データ型は、他のデータ型にキャストできません

(*) : 執筆時点での事実です。C++11 では状況が少し変わりましたが、私の回答の要点は当てはまります。

于 2009-04-03T09:42:14.607 に答える
16

いいえ。私の回答なぜあなたのコードを人為的にCに制限するのですか?C++としてコンパイルされていない標準準拠のC99の例がいくつかあります。以前のCは違いが少なかったが、C ++はより強力な型付けと、(void)関数の引数リストの異なる扱いを持っている。

CをC++に「移植可能」にすることの利点があるかどうかについて-その回答で参照された特定のプロジェクトは、特性ベースの言語の仮想マシンであったため、C ++オブジェクトモデルに適合せず、多くの場合がありますここで、インタープリターのスタックをプルvoid*してから、組み込みオブジェクトタイプのレイアウトを表す構造体に変換します。コードをC++に「移植可能」にするには、多くのキャストを追加する必要がありますが、これは型の安全性には何の影響も及ぼしません。

于 2009-04-03T08:38:34.917 に答える
8

移植性とは、異なるコンパイラや異なるプラットフォームを使用してコンパイルされ、同じ動作をするようにコードを記述することを意味します(つまり、可能な限りISO標準で義務付けられている動作に依存します)。

別の言語のコンパイラを使用してコンパイルするのは(おそらく)便利ですが、それが移植性の意味するところではないと思います。C ++とCは現在ますます分岐しているため、これを実現するのは困難になります。

一方、Cコードを書くときは、たとえば「クラス」を識別子として使用することは避けます。

于 2009-04-03T08:40:59.653 に答える
3

いいえ、「移植可能」とは「C++ コンパイラでコンパイルする」という意味ではなく、一貫した定義済みの動作で「標準準拠の C コンパイラでコンパイルする」ことを意味します。

また、たとえば、C++ の互換性を維持するためだけに C99 の改善を犠牲にしないでください。

ただし、互換性を維持することがあなたの手を縛らない限り、「クラス」や「仮想」などの使用を避けることができれば、なおさらです。オープン ソースを作成している場合、誰かがあなたのコードを C++ に移植したいと思うかもしれません。あなたが雇用を求めている場合、あなたの会社/クライアントは将来いつか移植したいと思うかもしれません. 将来的には C++ に移植したくなるかもしれません。

キャンプファイヤーの周りにゴミを残さない「良いスチュワード」であることは、何をするにしても良いカルマです.

また、ヘッダーに非互換性を持たないようにしてください。コードが移植されていなくても、C++ からリンクする必要がある場合があるため、C++ の予約語を使用しないヘッダーを使用すると便利です。

于 2009-04-03T08:51:35.320 に答える
2

場合によります。C ++ユーザーに役立つ可能性のあることをしている場合は、それは良い考えかもしれません。C ++ユーザーが必要としないことをしているが、Cユーザーが便利だと思うかもしれないことをしている場合は、わざわざC++に準拠させる必要はありません。

多くの人が行うことを行うプログラムを作成している場合は、それをできるだけ広く使用できるようにすることを検討してください。Linuxカーネルへの追加を作成している場合は、C++の互換性をウィンドウの外に投げ出すことができます。これは必要ありません。

誰があなたのコードを使用する可能性があるかを推測してみてください。多くのC++ファンがあなたのコードを役立つと思う場合は、C++対応にすることを検討してください。ただし、ほとんどのC ++プログラマーがそれを必要としないと思われる場合(つまり、C ++ですでにかなり標準化されている機能)、気にしないでください。

于 2009-04-03T08:43:37.493 に答える
2

いいえ、好みの問題です。私は void ポインターをキャストするのが嫌いで、コードを混乱させてあまりメリットがありません。

char * p = (char *)malloc(100);

char * p = malloc(100);

また、「オブジェクト指向」の C ライブラリ モジュールを作成するときは、「this」をオブジェクト ポインターとして使用するのが本当に好きです。これは C++ キーワードであるため、C++ ではコンパイルされません (これらの種類のモジュールは C++ では無意味であるため、これは意図的なものです)。それらがstlおよびライブラリにそのまま存在することを考えると)。

于 2010-01-06T13:54:57.470 に答える
2

より厳密な型チェックを行うために、C++ コンパイラを使用して C コードをコンパイルすることは、間違いなく一般的な方法です。lint のような C 固有のツールもありますが、C++ コンパイラを使用する方が便利です。

C++ コンパイラを使用して C コードをコンパイルするということは、通常、インクルードを extern "C" ブロックで囲み、コンパイラに関数名を変更しないように指示する必要があることを意味します。ただし、これは正当な C 構文ではありません。事実上、C++ 構文を使用しており、C であると思われるコードが実際には C++ です。また、名前のない共用体を使用するように、「C++ の利便性」を使用する傾向が忍び寄り始めています。

コードを厳密に C に保つ必要がある場合は、注意が必要です。

于 2009-04-03T09:05:35.460 に答える
2

FWIW、プロジェクトが一定の規模と勢いを得ると、実際に C++ との互換性が得られる可能性は低くありません。たとえ C++ に直接移植されなくても、C++ ソースの作業/処理に関連する最新のツールは本当にたくさんあります。コード。

この意味で、C++ との互換性の欠如は、実際には、特定のことを行うために独自のツールを考え出す必要があることを意味する場合があります。一部のプラットフォーム、環境、およびプロジェクトで C++ よりも C を優先する理由を十分に理解していますが、それでも C++ との互換性は、長期的にはプロジェクトの設計を簡素化します

さらに、多くの C プロジェクトは最終的に非常に大きくなり、アクセス修飾子を持つクラスを使用した抽象化とカプセル化のサポートの改善など、C++ の機能から実際に恩恵を受ける可能性があります。

たとえば、Linux (カーネル) または gcc プロジェクトを見てください。どちらも基本的に「C のみ」ですが、両方の開発者コミュニティで、C++ への切り替えの潜在的な利点について定期的に議論されています。

実際、現在gcc ソースを有効な C++ 構文 (詳細はgcc-in-cxxを参照) に移植して、C++ コンパイラを使用してソースをコンパイルできるようにする( FSF ツリーで!) gcc の取り組みが進行中です。コード。

これは基本的に、長年の gcc ハッカーである Ian Lance Taylor によって開始されました。

最初は、これはより良いエラー チェックと互換性の向上を提供することのみを目的としています (つまり、このステップが完了したら、gcc をコンパイルするために必ずしも C コンパイラを使用する必要はありません。単に使用することもできます)。 C++ コンパイラ (たまたま「ただの」C++ 開発者である場合)。

しかし、最終的には、このブランチは、gcc の実装言語として C++ への移行を促進することを目的としています。これは、本当に革命的なステップであり、FSF の人々によって批判的に認識されているパラダイム シフトです。

一方、gcc がその内部構造によってすでにどれほど厳しく制限されているかは明らかであり、少なくともこの状況を改善するのに役立つものはすべて称賛されるべきです: gcc プロジェクトへの貢献を始めることは、不必要に複雑で退屈です。マクロと gcc 固有の拡張機能を使用して、C++ のより高度な機能の多くをエミュレートし始めている複雑な内部構造。

最終的な C++ への切り替えに備えて gcc コード ベースを準備することは、最も論理的なことです (ただし、実際にいつ行われるかに関係なく!)。競争力を維持し、興味深く、単純に関連性を維持するためには、実際に必要です。特に、llvm などの非常に有望な取り組みのおかげで、これらすべての粗雑で複雑なものはもたらされません。

C で非常に複雑なソフトウェアを作成することは、多くの場合もちろん可能ですが、そうすると不必要に複雑になります。多くのプロジェクトは、はるか昔に単純に C を使い果たしてしまいました。これは、C がもはや関係ないという意味ではなく、まったく逆です。しかし、特定のコード ベースと複雑さを考えると、C は必ずしもこの仕事に理想的なツールではありません。

実際、特定のプロジェクトにとって C++ が必ずしも理想的な言語であるとは限らないと主張することさえできますが、言語がカプセル化をネイティブにサポートし、これらの抽象化を強制する手段を提供する限り、人々はこれらの制限を回避できます。

非常に複雑なソフトウェアを非常に低レベルの言語で書くことが明らかに可能だからといって、必ずしもそうすべきだとか、そもそもそれが本当に効果的であるとは限りません。

ここで私が話しているのは、何十万行ものコードがあり、寿命が数十年の複雑なソフトウェアです。このようなシナリオでは、オプションがますます重要になります。

于 2009-05-20T09:16:12.113 に答える
1

なぜあなたはほとんど利益を見ないのですか?実行するのは非常に簡単で、将来コードをどのように使用したいかを誰が知っていますか。

于 2009-04-03T08:37:11.643 に答える
1

私の知る限り、古典的なテキストのすべてのコード C プログラミング言語、第 2 版は、GCC (g++) などの標準 C++ コンパイラを使用してコンパイルできます。あなたの C コードがその古典的なテキストに従っている標準に準拠していれば、十分であり、C++ コンパイラを使用して C コードをコンパイルする準備ができています。

いくつかのインライン アセンブラ コードを使用して大部分が C で記述されている Linux カーネル ソース コードの例を見てみましょう。「new」が変数名として使用されている可能性が最も低いため、Linux カーネル コードを C++ コンパイラでコンパイルするのは悪夢です。 Linux カーネル コード。C++ では変数名として「new」を使用できません。ここでは一例を挙げているだけです。Linux カーネルは移植性があり、Intel、ppc、sparc などのアーキテクチャで非常にうまくコンパイルおよび実行されることを覚えておいてください。これは、ソフトウェアの世界では移植性が異なる意味を持つことを示すためのものです。C++ コンパイラを使用して C コードをコンパイルする場合は、コード ベースを C から C++ に移行することになります。CプログラマーがC++をあまり好まないという最も明白な理由から、私はそれを2つの異なるプログラミング言語と見なしています。でもどっちも好き&どちらもよく使います。C コードは移植可能ですが、C コードを C++ コードに移行する際は、標準の手法に従って C++ コードを移植可能にする必要があります。読み進めて、標準的な手法をどこから取得できるかを確認してください。

C コードを C++ コードに移植するときは、細心の注意を払う必要があります。次の質問は、C コードの一部が移植可能で、問題なく正常に動作しているのに、わざわざそんなことをするのですか? 私は扱いやすさを受け入れることができません.LinuxカーネルはCの大きなコードソースが非常にうまく管理されています.

C と C++ の 2 つのプログラミング言語を常に異なるプログラミング言語と見なしますが、C++ は C をサポートしており、その基本的な概念は、下位互換性のためにその素晴らしい言語を常にサポートすることです。これらの 2 つの言語を別のツールと見なしていない場合は、人気のある醜い C/C++ プログラミング言語戦争の世界に陥り、自分自身を汚すことになります。

移植性を選択するときは、次のルールを使用します。

a) コード (C または C++) は、おそらくネイティブ C/C++ コンパイラを使用して、異なるアーキテクチャでコンパイルする必要がありますか? b) プログラムを実行し、コードの移植を計画するさまざまなアーキテクチャで C/C++ コンパイラを調査します。これに十分な時間を投資してください。c) 可能な限り、C コードと C++ コードを明確に分離するレイヤーを提供するようにします。C コードが移植可能である場合は、移植可能な C++ コーディング手法を使用して、その移植可能な C コードの周りに C++ ラッパーを再度記述するだけで済みます。

移植可能な C++ コードの書き方については、優れた C++ の書籍を参照してください。個人的には、Bjarne Stroustrup 自身による C++ プログラミング言語、Scott meyers による効果的な C++ シリーズ、および www.ddj.com にある人気の DDJ 記事をお勧めします。

PS: 私の投稿の Linux カーネルの例は、移植性がソフトウェア プログラミングで異なる意味を意味することを説明するためのものであり、Linux カーネルが C で記述され、C++ ではないことを批判するものではありません。

于 2009-04-03T09:13:15.660 に答える
1

いいえ、C++ でコンパイル可能であることは、ポータブルの一般的な解釈ではありません。非常に古いコードを扱う場合、K&R スタイルの宣言は移植性が高くなりますが、C++ でコンパイルすることはできません。

すでに指摘したように、C99 拡張機能を使用したい場合があります。ただし、すべての対象ユーザーを検討し、それらが拡張機能を利用できるようにすることをお勧めします。可変長配列などを使用しないでください。自由があるためですが、本当に正当化される場合に限ります。

はい、可能な限り C++ との互換性を維持することは良いことです。他の人は、C コードを C++ としてコンパイルする必要がある正当な理由があるかもしれません。たとえば、それを MFC アプリケーションに含めたい場合、単一のプロジェクトにコードを含めるだけでなく、別の DLL またはライブラリでプレーン C をビルドする必要があります。

C++ モードでコンパイラを実行すると、コンパイラによっては、異なる最適化を適用すると、微妙なバグが見つかる可能性があるという議論もあります。

于 2009-04-03T09:04:46.053 に答える