?1、前言 ?
在很大程度上,C++是C的超集,這意味著一個有效的C程序也是一個有效的C++程序。
?C和C++的主要區別是,C++支持許多附加特性。但是,C++中有許多規則與C稍有不同。這些不同使得C程序作為C++程序編譯時可能以不同的方式運行或根本不能運行。
?本節著重討論這些區別。如果使用C++的編譯器編譯C程序,就知道這些不同之處。雖然C和C++的區別對本書的示例影響很小,但如果把C代碼作為C++程序編譯的話,會導致產生錯誤的消息。C99標準的發布使得問題更加復雜,因為有些情況下使得C更接近C++。 ? ?
?例如,C99標準允許在代碼中的任意處進行聲明,而且可以識別//注釋指示符。在其他方面,C99使其與C++的差異變大。
例如,新增了變長數組和關鍵字restrict。C11縮小了與C++的差異。
例如,引進了char16_t類型,新增了關鍵字_Alignas,新增了alignas宏與C++的關鍵字匹配。C11仍處于起步階段,許多編譯器開發商甚至都沒有完全支持C99。 ? ? ? ?
?我們要了解C90、C99、C11之間的區別,還要了解C++11與這些標準之間的區別,以及每個標準與C標準之間的區別。這部分主要討論C99、C11和C++之間的區別。當然,C++也正在發展,因此,C和C++的異同也在不斷變化。 ?
2、函數原型 ?
在C++中,函數原型必不可少,但是在C中是可選的。這一區別在聲明一個函數時讓函數名后面的圓括號為空,就可以看出來。在C中,空圓括號說明這是前置原型,但是在C++中則說明該函數沒有參數。也就是說,在C++中,intslice();和int slice(void);相同。例如,下面舊風格的代碼在C中可以接受,但是在C++中會產生錯誤: ?
在C中,編譯器假定用戶使用舊風格聲明函數。在C++中,編譯器假定slice()與slice(void)相同,且未聲明slice(int,int)函數。 ?另外,C++允許用戶聲明多個同名函數,只要它們的參數列表不同即可。
?3、char常量
?C把char常量視為int類型,而C++將其視為char類型。例如,考慮下面的語句:
?在C中,常量'A'被儲存在int大小的內存塊中,更精確地說,字符編碼被儲存為一個int類型的值。相同的數值也儲存在變量ch中,但是在ch中該值只占內存的1字節。 ?在C++中,'A'和ch都占用1字節。它們的區別不會影響本書中的示例。但是,有些C程序利用Char常量被視為int類型這一特性,用字符來表示整數值。例如,如果一個系統中的int是4字節,就可以這樣編寫C代碼: ?
'ABCD'表示一個4字節的int類型值,其中第1個字節儲存A的字符編碼,第2個字節儲存B的字符編碼,以此類推。注意,'ABCD'和"ABCD"不同。前者只是書寫int類型值的一種方式,而后者是一個字符串,它對應一個5字節內存塊的地址。考慮下面的代碼: ?
該例說明,如果把'ABCD'視為int類型,它是一個4字節的整數值。但是,如果將其視為char類型,程序只使用最后一個字節。在我們的系統中,嘗試用%s轉換說明打印'ABCD'會導致程序崩潰,因為'ABCD'的數值(1094861636) 已超出該類型可表示的范圍。 ?可以這樣使用的原因是C提供了一種方法可單獨設置int類型中的每個字節,因為每個字符都對應一個字節。但是,由于要依賴特定的字符編碼,所以更好的方法是使用十六進制的整型常量,因為每兩位十六進制數對應一個字節。第15章詳細介紹過相關內容(C的早期版本不提供十六進制記法,這也許是多字符常量技術首先得到發展的原因)。 ?
4、const限定符 ?
在C中,全局的const具有外部鏈接,但是在C++中,具有內部鏈接。也就是說,下面C++的聲明: ?
假設這兩條聲明都在所有函數的外部。C++規則的意圖是為了在頭文件更加方便地使用const。如果const變量是內部鏈接,每個包含該頭文件的文件都會獲得一份const變量的備份。如果const變量是外部鏈接,就必須在一個文件中進行定義式聲明,然后在其他文件中使用關鍵字extern進行引用式聲明。
?順帶一提,C++可以使用關鍵字extern使一個const值具有外部鏈接。所以兩種語言都可以創建內部鏈接和外部鏈接的const變量。它們的區別在于默認使用哪種鏈接。 ?
另外,在C++中,可以用const來聲明普通數組的大小: ?
?5、結構和聯合 ?
聲明一個有標記的結構或聯合后,就可以在C++中使用這個標記作為類型名: ?
結果是結構名會與變量名沖突。例如,下面的程序可作為C程序編譯,但是作為C++程序編譯時會失敗。因為C++把printf()語句中的duo解釋成結構類型而不是外部變量: ?
在C和C++中,都可以在一個結構的內部聲明另一個結構: ?
在C中,隨后可以使用任意使用這些結構,但是在C++中使用嵌套結構時要使用一個特殊的符號: ?
?6、枚舉
?C++使用枚舉比C嚴格。特別是,只能把enum常量賦給enum變量,然后把變量與其他值作比較。不經過顯式強制類型轉換,不能把int類型值賦給enum變量,而且也不能遞增一個enum變量。下面的代碼說明了這些問題: ?
另外,在C++中,不使用關鍵字enum也可以聲明枚舉變量: ?
與結構和聯合的情況類似,如果一個變量和enum類型的同名會導致名稱沖突。 ?
7、指向void的指針
?C++可以把任意類型的指針賦給指向void的指針,這點與C相同。但是不同的是,只有使用顯式強制類型轉換才能把指向void的指針賦給其他類型的指針。下面的代碼說明了這一點: ?
C++與C的另一個區別是,C++可以把派生類對象的地址賦給基類指針,但是在C中沒有這里涉及的特性。 ?
?8、布爾類型
?在C++中,布爾類型是bool,而且ture和false都是關鍵字。在C中,布爾類型是_Bool,但是要包含stdbool.h頭文件才可以使用bool、true和false。
?9、可選拼寫
?在C++中,可以用or來代替||,還有一些其他的可選拼寫,它們都是關鍵字。在C99和C11中,這些可選拼寫都被定義為宏,要包含iso646.h才能使用它們。
?10、寬字符支持
?在C++中,wchar_t是內置類型,而且wchar_t是關鍵字。在C99和C11中,wchar_t類型被定義在多個頭文件中(stddef.h、stdlib.h、wchar.h、wctype.h)。與此類似,char16_t和char32_t都是C++11的關鍵字,但是在C11中它們都定義在uchar.h頭文件中。 ?C++通過iostream頭文件提供寬字符I/O支持(wchar_t、char16_t和char32_t),而C99通過wchar.h頭文件提供一種完全不同的I/O支持包。 ?
11、復數類型
?C++在complex頭文件中提供一個復數類來支持復數類型。C有內置的復數類型,并通過complex.h頭文件來支持。這兩種方法區別很大,不兼容。C更關心數值計算社區提出的需求。 ?
12、內聯函數
?C99支持了C++的內聯函數特性。但是,C99的實現更加靈活。在C++中,內聯函數默認是內部鏈接。在C++中,如果一個內聯函數多次出現在多個文件中,該函數的定義必須相同,而且要使用相同的語言記號。例如,不允許在一個文件的定義中使用int類型形參,而在另一個文件的定義中使用int32_t類型形參。即使用typedef把int32_t定義為int也不能這樣做。但是在C中可以這樣做。C允許混合使用內聯定義和外部定義,而C++不允許。 ?
13、C++11中沒有的C99/C11特性
?雖然在過去C或多或少可以看作是C++的子集,但是C99標準增加了一些C++沒有的新特性。下面列出了一些只有C99/C11中才有的特性: ?
?指定初始化器; ? ? ?
受限指針(Restricted pointer)?(即,restric指針); ?
?變長數組; ? ?
伸縮型數組成員;
?帶可變數量參數的宏。
在很大程度上,C++是C的超集,這意味著一個有效的C程序也是一個有效的C++程序。
?C和C++的主要區別是,C++支持許多附加特性。但是,C++中有許多規則與C稍有不同。這些不同使得C程序作為C++程序編譯時可能以不同的方式運行或根本不能運行。
?本節著重討論這些區別。如果使用C++的編譯器編譯C程序,就知道這些不同之處。雖然C和C++的區別對本書的示例影響很小,但如果把C代碼作為C++程序編譯的話,會導致產生錯誤的消息。C99標準的發布使得問題更加復雜,因為有些情況下使得C更接近C++。 ? ?
?例如,C99標準允許在代碼中的任意處進行聲明,而且可以識別//注釋指示符。在其他方面,C99使其與C++的差異變大。
例如,新增了變長數組和關鍵字restrict。C11縮小了與C++的差異。
例如,引進了char16_t類型,新增了關鍵字_Alignas,新增了alignas宏與C++的關鍵字匹配。C11仍處于起步階段,許多編譯器開發商甚至都沒有完全支持C99。 ? ? ? ?
?我們要了解C90、C99、C11之間的區別,還要了解C++11與這些標準之間的區別,以及每個標準與C標準之間的區別。這部分主要討論C99、C11和C++之間的區別。當然,C++也正在發展,因此,C和C++的異同也在不斷變化。 ?
2、函數原型 ?
在C++中,函數原型必不可少,但是在C中是可選的。這一區別在聲明一個函數時讓函數名后面的圓括號為空,就可以看出來。在C中,空圓括號說明這是前置原型,但是在C++中則說明該函數沒有參數。也就是說,在C++中,intslice();和int slice(void);相同。例如,下面舊風格的代碼在C中可以接受,但是在C++中會產生錯誤: ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
int slice();
int main()
{
...
slice(20, 50);
...
}
int slice(int a, int b)
{
...
}
?在C中,編譯器假定用戶使用舊風格聲明函數。在C++中,編譯器假定slice()與slice(void)相同,且未聲明slice(int,int)函數。 ?另外,C++允許用戶聲明多個同名函數,只要它們的參數列表不同即可。
?3、char常量
?C把char常量視為int類型,而C++將其視為char類型。例如,考慮下面的語句:
- ?
char ch = 'A';
?在C中,常量'A'被儲存在int大小的內存塊中,更精確地說,字符編碼被儲存為一個int類型的值。相同的數值也儲存在變量ch中,但是在ch中該值只占內存的1字節。 ?在C++中,'A'和ch都占用1字節。它們的區別不會影響本書中的示例。但是,有些C程序利用Char常量被視為int類型這一特性,用字符來表示整數值。例如,如果一個系統中的int是4字節,就可以這樣編寫C代碼: ?
- ?
int x = 'ABCD'; /*對于int是4字節的系統,該語句出現在C程序中沒問題,但是出現在C++程序中會出錯 */
?'ABCD'表示一個4字節的int類型值,其中第1個字節儲存A的字符編碼,第2個字節儲存B的字符編碼,以此類推。注意,'ABCD'和"ABCD"不同。前者只是書寫int類型值的一種方式,而后者是一個字符串,它對應一個5字節內存塊的地址。考慮下面的代碼: ?
- ?
- ?
- ?
int x = 'ABCD';
char c = 'ABCD';
printf("%d %d %c %c ", x, 'ABCD', c, 'ABCD');
?
在我們的系統中,得到的輸出如下:
?
- ?
1094861636 1094861636 D D
?該例說明,如果把'ABCD'視為int類型,它是一個4字節的整數值。但是,如果將其視為char類型,程序只使用最后一個字節。在我們的系統中,嘗試用%s轉換說明打印'ABCD'會導致程序崩潰,因為'ABCD'的數值(1094861636) 已超出該類型可表示的范圍。 ?可以這樣使用的原因是C提供了一種方法可單獨設置int類型中的每個字節,因為每個字符都對應一個字節。但是,由于要依賴特定的字符編碼,所以更好的方法是使用十六進制的整型常量,因為每兩位十六進制數對應一個字節。第15章詳細介紹過相關內容(C的早期版本不提供十六進制記法,這也許是多字符常量技術首先得到發展的原因)。 ?
4、const限定符 ?
在C中,全局的const具有外部鏈接,但是在C++中,具有內部鏈接。也就是說,下面C++的聲明: ?
- ?
const double PI = 3.14159;
?相當于下面C中的聲明: ?
- ?
static const double PI = 3.14159;
?假設這兩條聲明都在所有函數的外部。C++規則的意圖是為了在頭文件更加方便地使用const。如果const變量是內部鏈接,每個包含該頭文件的文件都會獲得一份const變量的備份。如果const變量是外部鏈接,就必須在一個文件中進行定義式聲明,然后在其他文件中使用關鍵字extern進行引用式聲明。
?順帶一提,C++可以使用關鍵字extern使一個const值具有外部鏈接。所以兩種語言都可以創建內部鏈接和外部鏈接的const變量。它們的區別在于默認使用哪種鏈接。 ?
另外,在C++中,可以用const來聲明普通數組的大小: ?
- ?
- ?
const int ARSIZE = 100;
double loons[ARSIZE]; /* 在C++中,與double loons[100];相同 */
?當然,也可以在C99中使用相同的聲明,不過這樣的聲明會創建一個變長數組。在C++中,可以使用const值來初始化其他const變量,但是在C中不能這樣做: ?
- ?
const double RATE = 0.06; // C++和C都可以const double STEP = 24.5; // C++和C都可以const?double?LEVEL?=?RATE?*?STEP;????//?C++可以,C不可以
?5、結構和聯合 ?
聲明一個有標記的結構或聯合后,就可以在C++中使用這個標記作為類型名: ?
- ?
struct duo{ int a; int b;};struct duo m; /* C和C++都可以 */duo n; /* C不可以,C++可以*/
?結果是結構名會與變量名沖突。例如,下面的程序可作為C程序編譯,但是作為C++程序編譯時會失敗。因為C++把printf()語句中的duo解釋成結構類型而不是外部變量: ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
#include
float duo = 100.3;
int main(void)
{
struct duo { int a; int b;};
struct duo y = { 2, 4};
printf ("%f ", duo); /* 在C中沒問題,但是在C++不行 */
return 0;
}
?在C和C++中,都可以在一個結構的內部聲明另一個結構: ?
- ?
- ?
- ?
- ?
- ?
struct box
{
struct point {int x; int y; } upperleft;
struct point lowerright;
};
? ?在C中,隨后可以使用任意使用這些結構,但是在C++中使用嵌套結構時要使用一個特殊的符號: ?
- ?
- ?
- ?
struct box ad; /* C和 C++都可以 */
struct point dot; /* C可以,C++不行 */
box::point dot; /* C不行,C++可以 */
?6、枚舉
?C++使用枚舉比C嚴格。特別是,只能把enum常量賦給enum變量,然后把變量與其他值作比較。不經過顯式強制類型轉換,不能把int類型值賦給enum變量,而且也不能遞增一個enum變量。下面的代碼說明了這些問題: ?
- ?
- ?
- ?
- ?
- ?
- ?
enum sample {sage, thyme, salt, pepper};
enum sample season;
season = sage; /* C和C++都可以 */
season = 2; /* 在C中會發出警告,在C++中是一個錯誤 */
season = (enum sample) 3; /* C和C++都可以*/
season++;???????????????????/*?C可以,在C++中是一個錯誤?*/
?另外,在C++中,不使用關鍵字enum也可以聲明枚舉變量: ?
- ?
- ?
enum sample {sage, thyme, salt, pepper};
sample?season;????/*?C++可以,在C中不可以?*/
?與結構和聯合的情況類似,如果一個變量和enum類型的同名會導致名稱沖突。 ?
7、指向void的指針
?C++可以把任意類型的指針賦給指向void的指針,這點與C相同。但是不同的是,只有使用顯式強制類型轉換才能把指向void的指針賦給其他類型的指針。下面的代碼說明了這一點: ?
- ?
- ?
- ?
- ?
- ?
- ?
int ar[5] = {4, 5, 6,7, 8};
int * pi;
void * pv;
pv = ar; /* C和C++都可以 */
pi = pv; /* C可以,C++不可以 */
pi?=?(int?*?)?pv;???/*?C和C++都可以?*/
?C++與C的另一個區別是,C++可以把派生類對象的地址賦給基類指針,但是在C中沒有這里涉及的特性。 ?
?8、布爾類型
?在C++中,布爾類型是bool,而且ture和false都是關鍵字。在C中,布爾類型是_Bool,但是要包含stdbool.h頭文件才可以使用bool、true和false。
?9、可選拼寫
?在C++中,可以用or來代替||,還有一些其他的可選拼寫,它們都是關鍵字。在C99和C11中,這些可選拼寫都被定義為宏,要包含iso646.h才能使用它們。
?10、寬字符支持
?在C++中,wchar_t是內置類型,而且wchar_t是關鍵字。在C99和C11中,wchar_t類型被定義在多個頭文件中(stddef.h、stdlib.h、wchar.h、wctype.h)。與此類似,char16_t和char32_t都是C++11的關鍵字,但是在C11中它們都定義在uchar.h頭文件中。 ?C++通過iostream頭文件提供寬字符I/O支持(wchar_t、char16_t和char32_t),而C99通過wchar.h頭文件提供一種完全不同的I/O支持包。 ?
11、復數類型
?C++在complex頭文件中提供一個復數類來支持復數類型。C有內置的復數類型,并通過complex.h頭文件來支持。這兩種方法區別很大,不兼容。C更關心數值計算社區提出的需求。 ?
12、內聯函數
?C99支持了C++的內聯函數特性。但是,C99的實現更加靈活。在C++中,內聯函數默認是內部鏈接。在C++中,如果一個內聯函數多次出現在多個文件中,該函數的定義必須相同,而且要使用相同的語言記號。例如,不允許在一個文件的定義中使用int類型形參,而在另一個文件的定義中使用int32_t類型形參。即使用typedef把int32_t定義為int也不能這樣做。但是在C中可以這樣做。C允許混合使用內聯定義和外部定義,而C++不允許。 ?
13、C++11中沒有的C99/C11特性
?雖然在過去C或多或少可以看作是C++的子集,但是C99標準增加了一些C++沒有的新特性。下面列出了一些只有C99/C11中才有的特性: ?
?指定初始化器; ? ? ?
受限指針(Restricted pointer)?(即,restric指針); ?
?變長數組; ? ?
伸縮型數組成員;
?帶可變數量參數的宏。
審核編輯:湯梓紅
評論