數據類型的做用:編譯器預算對象(變量)分配的內存空間大小c++
int a; // 告訴編譯器分配4個字節的內存
typedef
起別名sizeof()
測類型大小void
數據類型(無類型,萬能類型)程序員
若是函數沒有返回值,必須用void
修飾數組
// 對函數返回的限定 void fun(int a);
若是函數沒有參數,參數能夠用void
修飾函數
// 對函數參數的限定 int fun(void);
不能定義void
類型的普通變量spa
void a; // error,不能肯定分配內存空間的大小
萬能指針操作系統
void * p; // ok, 萬能指針,指針類型都是4個字節,函數參數,函數有返回值 //1. void* 能夠指向任何類型的數據,被稱爲萬能指針 void test03() { int a = 10; void* p = NULL; p=&a; printf("a:%d\n",*(int*)p); char c = 'a'; p=&c; printf("c:%c\n",*(char*)p); } //2. void* 經常使用於數據類型的封裝 void test04() { void * memcpy(void * _Dst, const void * _Src, size_t _Size); }
sizeof
操做符注意事項3d
sizeof
返回的佔用空間大小是爲這個變量開闢的大小,而不僅是它用到的空間。因此對結構體用的時候,大多狀況下就得考慮字節對齊的問題了;sizeof
返回的數據結果類型是unsigned int
;要注意數組名和指針變量的區別。一般狀況下,咱們總以爲數組名和指針變量差不 多,可是在用sizeof
的時候差異很大:指針
sizeof
返回的是整個數組的大小示例代碼:code
//1. sizeof基本用法 void test01() { int a = 10; printf("len:%d\n", sizeof(a)); printf("len:%d\n", sizeof(int)); printf("len:%d\n", sizeof a); } //2. sizeof 結果類型 void test02() { unsigned int a = 10; if (a - 11 < 0) { printf("結果小於0\n"); } else { printf("結果大於0\n"); } int b = 5; if (sizeof(b) - 10 < 0) { printf("結果小於0\n"); } else { printf("結果大於0\n"); } } //3. sizeof 碰到數組 void TestArray(int arr[]) { printf("TestArray arr size:%d\n",sizeof(arr)); } void test03() { int arr[] = { 10, 20, 30, 40, 50 }; printf("array size: %d\n",sizeof(arr)); //數組名在某些狀況下等價於指針 int* pArr = arr; printf("arr[2]:%d\n",pArr[2]); printf("array size: %d\n", sizeof(pArr)); //數組作函數函數參數,將退化爲指針,在函數內部再也不返回數組大小 TestArray(arr); }
既能讀又能寫的內存對象,稱爲變量;對象
若一旦初始化後不能修改的對象則稱爲常量。
變量定義的形式:類型 標識符,標識符,…,標識符
int a = 0
;修改變量的兩種方式:
void test() { int a = 10; //1. 直接修改 a = 20; printf("直接修改,a:%d\n",a); //2. 間接修改 int* p = &a; *p = 30; printf("間接修改,a:%d\n", a); }
程序運行以前:
C程序編譯過程1)預處理:宏定義展開、頭文件展開、條件編譯,這裏並不會檢查語法
2)編譯:檢查語法,將預處理後文件編譯生成彙編文件
3)彙編:將彙編文件生成目標文件(二進制文件)
4)連接:將目標文件連接爲可執行程序
可執行程序內部已經分好3段信息,分別爲代碼區text
、數據區data
和未初始化數據區bss
3個部分(有些人直接把data
和bss
合起來叫作靜態區或全局區)。
整體來說說,程序源代碼被編譯以後主要分紅兩種段:程序指令(代碼區)和程序數據(數據區)。代碼段屬於程序指令,而數據域段和.bss段屬於程序數據。
程序在加載到內存前,代碼區和全局區(data和bss)的大小就是固定的,程序運行期間不能改變。而後,運行可執行程序,操做系統把物理硬盤程序load(加載)到內存,除了根據可執行程序的信息分出代碼區(text)、數據區(data)和未初始化數據區(bss)以外,還額外增長了棧區、堆區。
總的來講,內存分區模型:
代碼區:可執行代碼段,不可修改。未初始化數據區(BSS):全局未初始化,靜態未初始化數據,數據的生存週期爲整個程序運行過程 。
全局初始化數據區/靜態數據區(data segment):全局初始化,靜態初始化數據,文字常量(只讀),數據的生存週期爲整個程序運行過程。
棧區(stack):先進後出的內存結構,由編譯器自動分配釋放 ,存放函數的參數值、返回值、局部變量等
堆區(heap):容量要遠遠大於棧,用於動態內存分配。堆在內存中位於BSS區和棧區之間。通常由程序員分配和釋放,若程序員不釋放,程序結束時由操做系統回收。
棧區
由系統進行的內存管理,主要存放函數的參數以及局部變量,函數完成後系統自動釋放。
#char* func() { char p[] = "hello world!"; //在棧區存儲 亂碼 printf("%s\n", p); return p; } void test() { char* p = NULL; p=func(); printf("%s\n",p); }
堆區
手工申請,手工釋放
char* func() { char* str = malloc(100); strcpy(str, "hello world!"); printf("%s\n",str); return str; } void test01() { char* p = NULL; p=func(); printf("%s\n",p); } void allocateSpace(char* p) { p=malloc(100); strcpy(p, "hello world!"); printf("%s\n", p); } void test02() { char* p = NULL; allocateSpace(p); printf("%s\n", p); }
堆分配內存API:
void *calloc(size_t nmemb, size_t size); /* * 功能: * 在內存動態存儲區中分配nmemb塊長度爲size字節的連續區域。calloc自動將分配的內存置0。 * 參數: * nmemb:所需內存單元數量 * size:每一個內存單元的大小(單位:字節) * 返回值: * 成功:分配空間的起始地址 * 失敗:NULL */
全局/靜態區
全局靜態區內的變量在編譯階段已經分配好內存空間並初始化。這塊內存在程序運行期間一直存在,它主要存儲全局變量、靜態變量和常量。
int v1 = 10; //全局/靜態區 const int v2 = 20; //常量,一旦初始化,不可修改 static int v3 = 20; //全局/靜態區 char *p1; //全局/靜態區,編譯器默認初始化爲NULL //那麼全局static int 和 全局int變量有什麼區別? void test() { static int v4 = 20; //全局/靜態區 } // 加深理解 char* func() { static char arr[] = "hello world!"; //在靜態區存儲 可讀可寫 arr[2] = 'c'; char* p = "hello world!"; //全局/靜態區-字符串常量區 //p[2] = 'c'; //只讀,不可修改 printf("%d\n",arr); printf("%d\n",p); printf("%s\n", arr); return arr; } void test() { char* p = func(); printf("%s\n",p); }
總結:
數據區包括:堆,棧,全局/靜態存儲區。
全局/靜態存儲區包括:常量區,全局區、靜態區。
常量區包括:字符串常量區、常變量區。
代碼區:存放程序編譯後的二進制代碼,不可尋址區。
能夠說,C/C++內存分區其實只有兩個,即代碼區和數據區。
int func(int a,int b) { int t_a = a; int t_b = b; return t_a + t_b; } int main() { int ret = 0; ret = func(10, 20); return EXIT_SUCCESS; }
//1. 棧的生長方向 void test01() { int a = 10; int b = 20; int c = 30; int d = 40; printf("a = %d\n", &a); printf("b = %d\n", &b); printf("c = %d\n", &c); printf("d = %d\n", &d); //a的地址大於b的地址,故而生長方向向下 } //2. 內存生長方向(小端模式) void test02() { //高位字節 -> 地位字節 int num = 0xaabbccdd; unsigned char* p = # //從首地址開始的第一個字節 printf("%x\n",*p); printf("%x\n", *(p + 1)); printf("%x\n", *(p + 2)); printf("%x\n", *(p + 3)); }