最近のコメント

最近のトラックバック

ウェブページ

2017年9月
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

他のアカウント

無料ブログはココログ

« もし、俺の親父のようなタイプの人間が権力持ったら・・・ | トップページ | ヒトラーTシャツの何が問題なのか? − ヒトラーは戦争反対の平和演説を実際にした »

2017年6月30日 (金)

CとC++の違い−備忘

C++ は C言語の上位互換言語である、というのは迷信である。

非互換性について、忘れるといけないので知っていることを自分のためにメモしておく。

この記事は、思い出したり、何らかのことで分かったりした場合、断ることなく追加・修正する。

  • VisualStudio の Visual C++ では、ソースファイル名を拡張子を付けずに指定すると、デフォルトでC++の拡張子cppが自動的に付けられる。C言語のつもりでソースを書いても拡張子を指定しなければC++のソースとしてコンパイルされる。すると非互換性によっておかしなことになる可能性がある。VC++で純粋なC言語プログラムを書きたい場合は、ソースファイル名には明示的に拡張子「c」を付けて指定すること。

  • sizeof('A') は、C言語ではsizeof(int) と同じ値で、C++では1である。これは、'A' がC言語ではint型でC++ではchar型であることから生じる。

  • C言語では、void * 型のポインタは他の型のポインタに自動変換されるが、C++ではキャストが必要。C言語ではmalloc()関数の戻り値をキャストする必要はないが、C++ではキャストが必要になる。(可読性はまた別の話。)
    大きな影響として、C言語ではNULL マクロを ((void*)0) に展開できたが、C++ではこの展開はできない。よって、C++ では NULL は(ポインタと同じサイズの)整定数0 に展開されることがほとんど。

    • C++ではNULLなど忘れた方がよい。C++11以降なら nullptr を使え。そして、nullptr の実際の値が何であるかに依存しないコードを書け。(実際は値は0である処理系しか見たことがないが、C/C++の仕様ではそれは保障されていない。)例えば、ポインタ変数をオールビット0にすることで、これが空ポインタになる、などという仮定をするな。(例えば、ポインタを含む構造体をmemsetで0クリアすればポインタは空ポインタになる、という保障は仕様上ない。)

    • Visual C++では、ソースファイル名指定で拡張子を省略すると、C++としてコンパイルすることになるので NULL は0に展開される(ポインタサイズとintのサイズが同じ場合)。そのため、文字列終端の '\0'  に NULL マクロを使うなどの習慣ができてしまう傾向があるようだ(これを「NULL文字」とか呼ぶ習慣もあるのでこれをやってしまう人も多い)。NULLマクロはあくまで空ポインタを表すのでこれは不適当な使い方。ネットサーフィンで、main関数内で return NULL; というのを見たことがある。純粋なC言語プログラムを書きたい場合は、ソースファイル名に拡張子「.c」を明示すれば NULL は ((void*)0) に展開されるので、このような使い方には警告が出る。C++ソースを書くなら nullptr を使うべき。
  • const int xxxx =10; がトップレベルで書かれていた場合、C言語ではxxxxは外部リンケージでC++では内部リンケージとなる。複数のプログラム単位でこれが書かれていた場合、C言語では重複定義のリンケージエラーとなるが、C++では何の問題もない。

  • C/C++両方で、char型は符号付きか符号無しかは処理系依存(ついでに1バイトのビット数が8である保障も仕様にはない)。コンパイルオプションで切り替えることができる場合が多い。ただし、C言語では char 型はsigned char か unsigned char のどちらかと一致するが、C++ではchar型、signed char型、unsigned char 型はすべて異なった型。

  • const の意味はC言語とC++言語では異る。
  • C言語では main 関数の直接・間接的な再帰的呼び出しができるが、C++では禁止されている。main関数のアドレス取得もC++では禁止。

  • C言語では int f(); は関数fの前方参照だが、C++では引数0個のプロトタイプ宣言。
    もし、純粋なC++言語で書かれたヘッダーファイル foo.h に関数の前方参照が入っていた場合、C++ソース内で
    extern "C"{
    #include "foo.h"
    }
    とした場合、関数の前方参照はプロトタイプとして認識されるので、意味が変わることがある。

  • C言語では、ブロックの外側からブロック内部にgotoすることは許されているが、C++ではコンストラクタが動かないといけないクラスのインスタンスがブロック内の局所変数で宣言or定義されている場合、gotoは禁止される。
    多重ループの外から中にgotoなどそもそもやるべきではないのだけれど。
    ちなみに、switch文のcase もgotoと類似のものなので、この制限を受ける。この場合も、そもそも意味的にやってはいけない代物。
  • main 関数の argv は、C。言語では仕様に「argvの内容は変更可能でなければならない」とあるが、C++では、標準的な使い方の宣言で argv の型にconstが付いていないことで、同じことを表す。どっちにしても、argvが変更可能というのは素人には怖い。変更可能と書いてあるだけで argv の内部構造を知る標準的手段がない。argv[i] の文字列を長く変更したいときの最大バッファ長を知ることもできない。それ以前に、argv[0],argv[1],argv[2],... がこの順序でメモリに並んでいる保障もない。
  • C++言語には標準の型として bool 型があるが、元々のCにはない。C99でやっと<stdbool.h> をincludeすると bool型を使えるようになった(本体は _Bool 型)。C言語では boolは _Boolを表すマクロとして定義することが多く、C++言語では _Bool が bool を表すマクロとして定義することが多い。どちらにしても bool 型の変数はきちんと初期化しないと、true でも false でもないゴミが入ってしまって混乱することがあるので、必ず初期化しないと危険。
  • C言語では struct xxx や union uuu を作った場合、変数などの宣言や定義では struct, union は省略できない。C++では省略できる。従って C言語では
    struct xxx xxx; とxxxという変数を宣言・定義できるが(好ましくはない)、C++ではできない。
  • C言語では構造体の最初のメンバのアドレスと構造体自身のアドレスは一致するが、C++では「余計なこと」(Cではない機能を使う)をするとその保障はなくなる。
  • C/C++では標準の &&、||、「,」(コンマ演算子)のように評価順が決まっているものがあるが、これらの演算子のオーバーロードをすると評価順は決まらなくなる。
  • キーワードは識別子として使えないが、C++のキーワードは多いので注意。final など、キーワードではないが特別の意味を保つ名前もあるのでそれらも識別子として使わない方がいい。また、演算子の代替表現(and,or,not,xor,and_eq,or_eq,not_eq,xor_eq,compl,bitand,bitor)も使えない。C/C++両方で、下線で始まり次が大文字の名前、どの部分でも下線が連続している名前、下線で始まるグローバル変数は使ってはいけない。

« もし、俺の親父のようなタイプの人間が権力持ったら・・・ | トップページ | ヒトラーTシャツの何が問題なのか? − ヒトラーは戦争反対の平和演説を実際にした »

C/C++言語」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/525300/65474355

この記事へのトラックバック一覧です: CとC++の違い−備忘:

« もし、俺の親父のようなタイプの人間が権力持ったら・・・ | トップページ | ヒトラーTシャツの何が問題なのか? − ヒトラーは戦争反対の平和演説を実際にした »