從零開始學C++之從C到C++(一):const與#define、結構體對齊、函式過載name mangling、new/delete 等
http://blog.csdn.net/jnu_simba/article/details/9150415
一、bool 型別
邏輯型也稱布林型,其取值為true(邏輯真)和false(邏輯假),儲存位元組數在不同編譯系統中可能有所不同,VC++中為1個位元組。
宣告方式:bool result; result=true;
可以當作整數用(true一般為1,false為0)
把其它型別的值轉換為布林值時,非零值轉換為true,零值轉換為false,注意會發生截斷。
二、const 限定符
(1)、用const給字面常量起個名字(識別符號),這個識別符號就稱為識別符號常量;因為識別符號常量的宣告和使用形式很像變數,所以也稱常變數。
定義的一般形式:
const 資料型別 常量名=常量值; 資料型別 const 常量名=常量值;
例如: const float PI=3.14159f;
注意事項:
常變數在定義時必須初始化;
常變數初始化之後,不允許再被賦值;
正如我在這裡所說,其實加了關鍵字const只是提示編譯器這個變數是常量,如果我們在接下來的操作中試圖更改它,編譯器會報錯,而並不是真正的常量,事實上某些情形下通過指標也是可以更改的(編譯器報警告),什麼情況下完全不能修改呢,當A是加const限定且初始化的全域性變數,此時A位於.rodata段(linux 下)。此外const 用於修飾指標時可以參考這裡。
(2)、const 與 #define
const定義的常量與#define定義的符號常量的區別:
const定義的常量有型別,而#define定義的沒有型別,編譯可以對前者進行型別安全檢查,而後者僅僅只是做簡單替換
const定義的常量在編譯/執行時確定初值,而#define定義的常量是在預編譯時進行替換,不分配記憶體。
作用域不同,const定義的常變數的作用域為該變數的作用域範圍。而#define定義的常量作用域為它的定義點到程式結束,當然也可以在某個地方用#undef取消
#define定義的常量,容易產生副作用:
//Effective C++ 3rd的一個例子。
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))
int a = 5;
int b = 0;
CALL_WITH_MAX(++a, b); //a被累加二次
CALL_WITH_MAX(++a, b+10); //a被累加一次
在這裡,呼叫f之前,a的遞增次數竟然取決於“它被拿來和誰比較”
此外,定義常量還可以用enum,在c++ 中儘量用const、enum替換#define定義常量,用inline 替換帶引數的巨集定義;但 #define 在底層程式設計中是必不可少的,下面舉個例子:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include<iostream>
using namespace std; #define STR(a) #a #define CAT(a,b) a##b int main(void) { int xy = 100; cout << STR(ABCD) << endl; // #ABCD => "ABCD" cout << CAT(x, y) << endl; // x##y => xy return 0; } |
如果是完全的c++ 菜鳥,這裡還得稍微解釋一下細節,iostream 是c++標準庫的一個io流標頭檔案,跟C語言不太一樣的是一般沒有.h 字尾,using namespace 表示名稱空間,簡單理解就是統一的函式字首,類比pthread庫如pthread_mutex_init, pthread_mutex_lock 用c++ 方式來表示可能是 pthread::mutex::lock。 cout是輸出流物件,<<操作符在C語言中是左移位運算操作符,在這裡被過載成輸出操作符,之所以能並列輸出是因為如cout<<xxx 返回的是cout 的引用,以後還會再提。引數巨集定義的意義就很清楚了,檢視下輸出即可。
我們知道printf函式帶有可變引數,函式式巨集定義也可以帶可變引數,同樣是在引數列表中用...表示可變引數。例如:
1
2 3 4 5 6 |
#define showlist(...) printf(#__VA_ARGS__)
#define report(test, ...) ((test)?printf(#test):\ printf(__VA_ARGS__)) showlist(The first, second, and third items.); report(x > y, "x is %d but y is %d", x, y); |
1
2 |
printf("The first, second, and third items.");
((x > y) ? printf("x>y") : printf("x is %d but y is %d", x, y)); |
(三)、結構體對齊
什麼是記憶體對齊
編譯器為每個“資料單元”按排在某個合適的位置上。
C、C++語言非常靈活,它允許你干涉“記憶體對齊”
為什麼要對齊
效能原因:在對齊的地址上訪問資料快。
如何對齊
第一個資料成員放在offset為0的位置
其它成員對齊至min(sizeof(member),#pragma pack(n)所指定的值)的整數倍。
整個結構體也要對齊,結構體總大小對齊至各個min中最大值的整數倍。
舉個例子,
struct test
{char a;
double b;
char c;
};
根據規則1,a 在位置0;根據規則2,因為VC預設pack為8,可以通過專案-》屬性-》c/c++ -》程式碼生成-》結構體成員對齊選項修改,也可以使用#pragma pack(n) 來修改,#pragma pack() 取消修改,那麼b 佔據8~15;根據規則2,c在16;現在總共17個位元組,根據規則3,結構體總大小需對齊到8的整數倍,即總共是24個位元組。
如果將pack 修改為4,則總大小為16。在VC上pack 共有1,2,4,8,16 等5種選擇,而linux g++ 則只有1,2,4 可選,預設是4。
(四)、域運算子
C++中增加的作用域識別符號 ::
用於對與區域性變數同名的全域性變數進行訪問
用於表示類的成員,以後講到類的時候再詳談
(五)、new、delete 運算子
(1)、new operator
new運算子可以用於建立堆空間,成功返回首地址,失敗丟擲異常
語法:
指標變數=new 資料型別(值);
指標變數=new 資料型別[長度n];
例如:
int *p; p=new int(3);
char *pStr=new char[50];
(2)、delete operator
delete運算子 可以用於釋放堆空間
語法:
delete 指標變數;
delete [] 指標變數;
例如:
delete p;
delete [] pStr; // 類似 delete pStr[0] , delete pStr[1], ....
(3)、new 和 delete 執行的步驟
new operator
記憶體分配(operator new),類似malloc
呼叫建構函式,講到類再說
delete operator
呼叫解構函式,講到類再說
釋放記憶體(operator delete),類似free
實際上new 有三種用法,包括operator new、new operator、placement new,new operator 包含operator new,而placement new 則沒有記憶體分配而是直接呼叫建構函式,具體的差異以後再談。
(六)、函式過載、name managling 與extern "C"
(1)、函式過載
相同的作用域,如果兩個函式名稱相同,而引數不同,我們把它們稱為過載overload,函式過載又稱為函式的多型性(靜態)
函式過載不同形式:
形引數量不同
形參型別不同
形參的順序不同
形引數量和形參型別都不同
呼叫過載函式時,編譯器通過檢查實際引數的個數、型別和順序來確定相應的被呼叫函式。
函式的過載跟函式的覆蓋、函式的隱藏是不同的,這一點以後再講。
合法的過載例子:
int abs(int i); long abs(long l);double abs(double d);
非法的過載例子:
int abs(int i);
long abs(int i); void abs(int i);
//如果返回型別不同而函式名相同、形參也相同,則是不合法的,編譯器會報"語法錯誤"。
(2)、name mangling 與extern "C"
name managling這裡把它翻譯為名字改編,C++為了支援函式過載,需要將函式名根據引數的不同進行name managling以便區分。
extern “C” 可以實現C與C++混合程式設計,即對C語言寫的函式不進行改名,一般在C的標頭檔案中使用,如果標頭檔案被C程式碼包含並用C編譯器編譯,則__cplusplus 沒有定義,extern “C" 被略過,如果標頭檔案被C++程式碼包含並被C++編譯器編譯,存在__cplusplus 定義故extern "c" 提示編譯器不要對 {} 內函式進行改名。
#ifdef __cpluscplus
extern “C”
{
#endif
...
#ifdef __cpluscplus
}
#endif
實際上,編譯器對資料成員也會進行name mangling處理,目地是區分派生類和基類中可能的同名成員。
不同C++編譯器的name mangling 方案是不同的,這是造成不同編譯器之間存在二進位制連線相容性的主要原因之一。
(七)、帶預設形參值的函式
函式宣告或者定義的時候,可以給形參賦一些預設值,呼叫函式時,若沒有給出實參,則按指定的預設值進行工作。
* 函式沒有宣告時,在函式定義中指定形參的預設值
* 函式既有定義又有宣告時,宣告時指定後,定義後就不能再指定預設值
* 預設值的定義必須遵守從右到左的順序,如果某個形參沒有預設值,則它左邊的引數就不能有預設值。
void func1(int a, double b=4.5, int c=3); //合法
void func1(int a=1, double b, int c=3); //不合法
* 函式呼叫時,實參與形參按從左到右的順序進行匹配
* 過載的函式中如果形參帶有預設值時,可能產生二義性
1
2 3 4 5 6 7 8 |
int add(int x = 5, int y = 6);
int add(int x = 5, int y = 6, int z = 7); int main(void) { int sum; sum = add(10, 20); return 0; } |
參考:
C++ primer 第四版Effective C++ 3rd
C++程式設計規範
相關文章
- 從零開始學習C++之遞推C++
- 從零開始學習C++(0)C++
- 從預設解構函式學習c++,new,delete,記憶體洩漏,野指標函式C++delete記憶體指標
- 從零開始學習C++之if判斷語句C++
- 從零開始學習C++(1-1)C++
- C++之函式過載C++函式
- C/C++結構體對齊測試C++結構體
- C++中過載new和delete的使用C++delete
- 《從零開始學Swift》學習筆記(Day 39)——建構函式過載Swift筆記函式
- C++ struct結構體記憶體對齊C++Struct結構體記憶體
- C++程式設計從零開始之語句(轉)C++程式設計
- C/C++中的new/delete、構造/解構函式、dynamic_cast分析C++delete函式AST
- C++學習之路——第一天(結構體、C++程式從編寫到執行)C++結構體
- C++函式過載C++函式
- C/C++ 結構體位元組對齊詳解C++結構體
- C++之new、delete 與malloc、free的異同C++delete
- C/C++—— C++中函式重寫和函式過載C++函式
- <<從0到1學C++>> 第1篇 認識C++的函式和物件C++函式物件
- 開心檔之C++ 過載運算子和過載函式C++函式
- C++的函式過載C++函式
- C++ 從&到&&C++
- C malloc() free(), C++ new() delete()C++delete
- <<從0到1學C++>> 第3篇 從結構到類的演變C++
- 從C到C++C++
- c++ const 成員函式C++函式
- 從零開始學golang之RedBlackTree-DeleteGolangdelete
- C++ 函式過載和模板C++函式
- C++的函式的過載C++函式
- C++ 中的 const 物件與 const 成員函式C++物件函式
- 從C++到javaC++Java
- C++動態記憶體管理——new/deleteC++記憶體delete
- C++記憶體管理:new / delete 和 cookieC++記憶體deleteCookie
- C++ 記憶體對齊C++記憶體
- C++ 過載運算子和過載函式C++函式
- 《從零開始學Swift》學習筆記(Day 40)——解構函式Swift筆記函式
- C++ 函式過載,函式模板和函式模板過載,選擇哪一個?C++函式
- Arduino程式設計從零開始 使用C和C++(第2版)UI程式設計C++
- c++函式學習總結C++函式