1. 程式人生 > >一些容易混淆的 C 和 C++ 的不相容特性

一些容易混淆的 C 和 C++ 的不相容特性

C 和 C++ 是兩種不同的程式語言, 特別的, C 並不是 C++ 的子集。 但二者又高度相關。 C++ 自誕生以來, 一直以能夠相容C作為自己的目標之一。 在兩種語言的不斷演化中, C 和 C++ 都互相從對方身上吸收了不少內容。 舉個例子, C99 標準開始支援 C++ 風格的//註釋, C++11 標準支援 C99 的 long long 整型, 等等。

C 和 C++ 的不相容大致可以分為三個方面:

  1. C++ 支援而 C 不支援的功能
  2. C 支援而 C++ 不支援的功能
  3. C 和 C++ 都支援, 但語法/語意細節不同的功能

第一類非常多, 比如各類 OOP 功能, template 功能。 第二類在 C99 推出時也有不少, 但隨著 C++11 的推出, 很多 C99 引入的特性也被加入 C++ 了。 而第三類, 是本文的重點, 因為這類特性是最容易混淆的。

以下討論僅針對標準的 C/C++, 不包括各種編譯器擴充套件。

const修飾符

C 和 C++ 都有一個重要的概念, 叫做常量表達式(constant expression), 特點是可以在編譯時就得到值, 而不需要執行時。 有些語法要求只能使用常量表達式, 比如陣列的長度, case 語句的表示式, 等等。

那麼, const 變數是否可以用作常量表達式呢? 答案在 C 和 C++ 中並不一樣, 比如下面這段程式碼:

C++
1234 voidfoo(){constintN=100;intarr[N];}

在 C++ 中, 這段程式碼是合法的, 因為 N 可以當做常量 100 一樣使用。 (在 C++11 中, 這裡還可以用 constexpr)。 但在 C89 中, 這段程式碼是非法的, 因為即使變數宣告為const, 它仍然不是常量表達式。

但可能有人會問, 我試過這段程式碼, 可以編譯的啊。 那是因為, C99中支援可變長度陣列(variable length array, 經常縮寫為VLA), arr

這裡被解析為一個VLA, 所以雖然這段程式碼在 C99 中變成合法的, 但 arr 仍然不是一個普通的(固定長度的)陣列, 因為 N 仍然不是常量表達式。

void *指標

void *指標在 C 語言中用作通用指標。 C++ 雖然仍然支援它, 但由於有更強大的泛型程式設計, void *的用處要少很多。

作為通用指標, void * 可以和其他任意型別的指標相互轉換, 但要注意, C 語言中這種型別轉換是隱式的(implicit conversion), 而在 C++ 中必須有顯式的型別轉換(explicit conversion)。

看下面的程式碼:

C++
123 void*ptr;int*a=ptr;int*b=(int*)ptr;

指標a的初始化在 C 語言中是合法的, 而在 C++ 中是非法的。 指標 b 的初始化在 C/C++ 中都是合法的。

這也是 C++ 比 C 的型別系統更強的一個例子。

思考題: malloc的返回值需要做型別轉換嗎? 也就是說:

C++
12 int*x=malloc(sizeof(*x));int*y=(int*)malloc(sizeof(*y));

應該用哪種呢?

auto 關鍵字

C++11 引入的 auto 關鍵字真是喜大普奔, 尤其是 STL 的迭代器型別, 改用 auto 之後, 簡直酸爽。 那麼, 你知道嗎, 下面這段程式碼:

C++
123 voidfoo(){autoa=42;}

在 C89 下也是可以編譯成功的。 是不是 C 語言也支援 auto 呢?

原來, auto 關鍵字在 C 語言中早就存在, 它用來修飾變數, 表示變數擁有自動儲存 (automatic storage), 和靜態儲存相反。 但是呢, 在函式內, 靜態儲存的變數需要用 static 關鍵字修飾, 其他變數預設都是自動儲存的, 所以 auto 這個關鍵字不用也可以, 結果就是,實際中基本沒有人會用它。 而 C++11 裡, 把 auto 關鍵字賦予了新的功能, 算是老樹煥發了新春。

所以上面的程式碼在 C 語言中, 相當於 a = 42;, 而在 C89 中, 由於有隱含的 int 型別, 也等同於 int a = 42;。 注意在 C99 中, 隱含的 int 型別已經不再合法了。

一些基本型別

下面程式碼的輸出是什麼?

C++
1 printf("%zu\n",sizeof('a'));

你可能猜到了, C 和 C++ 的答案不一樣。 C++ 的輸出為 1, 而 C 語言的輸出和機器有關, 很可能是 4sizeof(char) 的結果在兩種語言中是一致的, 按照定義, 其值為 1。 區別在於字元常量的型別。 C++ 語言的字元常量, 如 'a', 型別是 char, 而 C 語言中其型別為 int

另一個基本型別 bool,由於 C 語言很長時間以來是不提供直接支援的, 很多 C 程式碼採用了 #define 1 TRUE 之類的定義來模擬布林型別。 但是實際上, C99 已經提供了標準的布林型別, 為了相容老程式碼, 這個型別名稱選擇了 _Bool, 但在標頭檔案 stdbool.h 中, 提供了別名 bool 和巨集 true, false 來方便大家使用。 不過呢, 如果你執行下面的 C 程式碼:

C++
1234567 #include <stdio.h>#include <stdbool.h>intmain(){printf("%zu  %zu\n",sizeof(bool),sizeof(true));return0;}

很可能會發現兩者大小又不一致了。 那是因為, 即使有了標準的布林型別, truefalse 仍然只是整形常量 10, 而不像 C++ 中是真正的 bool 型別常量。

C 和 C++ 還有其他一些區別, 比如 const 全域性變數的作用範圍, inline 函式的定義範圍等等。 因為相對不容易弄錯, 這裡就不展開了, 你還能想到什麼特性可以加到這個名單之中呢?

延伸閱讀:

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式