一些容易混淆的 C 和 C++ 的不相容特性
C 和 C++ 是兩種不同的程式語言, 特別的, C 並不是 C++ 的子集。 但二者又高度相關。 C++ 自誕生以來, 一直以能夠相容C作為自己的目標之一。 在兩種語言的不斷演化中, C 和 C++ 都互相從對方身上吸收了不少內容。 舉個例子, C99 標準開始支援 C++ 風格的//
註釋, C++11 標準支援 C99 的 long long 整型
, 等等。
C 和 C++ 的不相容大致可以分為三個方面:
- C++ 支援而 C 不支援的功能
- C 支援而 C++ 不支援的功能
- C 和 C++ 都支援, 但語法/語意細節不同的功能
第一類非常多, 比如各類 OOP 功能, template 功能。 第二類在 C99 推出時也有不少, 但隨著 C++11 的推出, 很多 C99 引入的特性也被加入 C++ 了。 而第三類, 是本文的重點, 因為這類特性是最容易混淆的。
以下討論僅針對標準的 C/C++, 不包括各種編譯器擴充套件。
const
修飾符
C 和 C++ 都有一個重要的概念, 叫做常量表達式(constant expression), 特點是可以在編譯時就得到值, 而不需要執行時。 有些語法要求只能使用常量表達式, 比如陣列的長度, case
語句的表示式, 等等。
那麼, const
變數是否可以用作常量表達式呢? 答案在 C 和 C++ 中並不一樣, 比如下面這段程式碼:
1234 | voidfoo(){constintN=100;intarr[N];} |
在 C++ 中, 這段程式碼是合法的, 因為 N
可以當做常量 100
一樣使用。 (在 C++11 中, 這裡還可以用 constexpr
)。 但在 C89 中, 這段程式碼是非法的, 因為即使變數宣告為const
, 它仍然不是常量表達式。
但可能有人會問, 我試過這段程式碼, 可以編譯的啊。 那是因為, C99中支援可變長度陣列(variable length array, 經常縮寫為VLA), arr
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
的返回值需要做型別轉換嗎? 也就是說:
12 | int*x=malloc(sizeof(*x));int*y=(int*)malloc(sizeof(*y)); |
應該用哪種呢?
auto
關鍵字
C++11 引入的 auto
關鍵字真是喜大普奔, 尤其是 STL 的迭代器型別, 改用 auto
之後, 簡直酸爽。 那麼, 你知道嗎, 下面這段程式碼:
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 語言的輸出和機器有關, 很可能是 4
。 sizeof(char)
的結果在兩種語言中是一致的, 按照定義, 其值為 1
。 區別在於字元常量的型別。 C++ 語言的字元常量, 如 'a'
, 型別是 char
, 而 C 語言中其型別為 int
。
另一個基本型別 bool
,由於 C 語言很長時間以來是不提供直接支援的, 很多 C 程式碼採用了 #define 1 TRUE
之類的定義來模擬布林型別。 但是實際上, C99 已經提供了標準的布林型別, 為了相容老程式碼, 這個型別名稱選擇了 _Bool
, 但在標頭檔案 stdbool.h
中, 提供了別名 bool
和巨集 true
, false
來方便大家使用。 不過呢, 如果你執行下面的 C 程式碼:
1234567 | #include <stdio.h>#include <stdbool.h>intmain(){printf("%zu %zu\n",sizeof(bool),sizeof(true));return0;} |
很可能會發現兩者大小又不一致了。 那是因為, 即使有了標準的布林型別, true
和 false
仍然只是整形常量 1
和 0
, 而不像 C++ 中是真正的 bool
型別常量。
C 和 C++ 還有其他一些區別, 比如 const
全域性變數的作用範圍, inline
函式的定義範圍等等。 因為相對不容易弄錯, 這裡就不展開了, 你還能想到什麼特性可以加到這個名單之中呢?
延伸閱讀:
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式