C語言深度剖析——讀書筆記

1、 什麼是定義,什麼是聲明?

定義:編譯器創建一個對象,併爲這個對象分配一塊內存,給它取上一個名字。
聲明:1>告訴編譯器這個名字已經已經分配到一塊內存上了
2>告訴編譯器這個名字已經被預定了,別的地方不能再用它來作爲變量名或對象名。

2、 auto:

在缺省的情況下,編譯器默認所有的變量都是auto的,

3、 register:

register變量必須是單個的值,並且其長度應該小於或等於整形的長度,而且register變量可能不存放在內存中,所以不能用&—-取地址符來獲取register變量的地址。

4、 static:在C語言中有兩個作用

1> 修飾變量:變量又分爲局部變量全局變量但都存在內存的靜態區。
靜態全局變量:作用域僅限於變量被定義的文件中,其他文件即使用extern聲明也沒法使用它。或者說它的作用域從定義之處開始到文件結尾處結束。
靜態局部變量:在函 數體中定義,只能在這個函數中使用,由於是存在靜態區,所以函數運行結束該變量也不會被銷燬,下次使用仍能用到這個值。
2> 修飾函數:此處static不是指存儲方式,而是指函數的作用域僅限於本文件。
所以又稱內部函數。

5、 不同類型的數據之間的運算要注意精度擴展問題,一般低精度數據向高精度數據擴展。

6、 柔型數組:

結構體中的最後一個元素允許是未知大小的數組,這就叫做柔型數組的成員。
但結構中的柔性數組成員前面必須至少包含一個其他成員。
柔型數組成員允許結構體中包含一個大小可變的數組,
柔型數組成員只作爲一個符號地址存在,而且必須是結構體最後一個成員。
Sizeof返回的這種結構體大小不包括柔性數組的內存,
柔型數組成員不僅可以用於字符數組,還可以是元素爲其他類型形的數組。
包含柔型數組成員的結構用malloc函數進行內存的動態分配。並且分配的內存應大於結構的大小,以適應柔型數組的預期大小。

7、 signed char 範圍-128~127

unsigned char 範圍 0~255
-1 補碼 1111 1111
-2 補碼 1111 1110
-3 補碼 1111 1101
.
.
.
-127 補碼 1000 0001
-127絕對值爲127:0111 1111 取反加一 1000 0001
-128 補碼 1000 0000
-128絕對值爲128:1000 0000 取反加一 0111 1111+1=1000 0000
-129補碼 0111 1111
-129絕對值爲129:1000 0001 取反加一 0111 1110+1=0111 1111
.
.
.
-255補碼0000 0001
-255絕對值爲255:1111 1111 取反加一 0000 0000 +1=0000 0001
-256補碼0000 0000
-256絕對值爲256:1 0000 0000取反加一
0 1111 1111 +1=1 0000 0000
-257 補碼
-257絕對值257:1 0000 0001取反加一
0 1111 1110+1=0 1111 1111

8、

intmain {intj = -20;//11111111 11111111 11111111 11101100unsignedinti =10;//00000000 00000000 00000000 00001010unsignedk=i + j;//11111111 11111111 11111111 11110110(但是這是一個無符號數)=4292967286system("pause");return0; }

9、+0和-0在內存裏怎麼存儲?

-0 原碼:1000 0000
反碼:1111 1111
補碼:1 0000 0000
+0 原碼:0000 0000
反碼:1111 1111
補碼:1 0000 0000

10、case後面只能是整形或者字符型的常量,或者常量表達式。

11、for語句的控制表達式不能包含任何浮點類型的對象

舍入誤差和截取誤差會通過循環的迭代過程傳播,導致循環變量的顯著誤差

12、在C語言中,凡不加返回值類型限定的函數,就會被編譯器作爲返回整型值處理

13、在C++中,參數爲void的意思是這個函數不接受任何參數。

14、不能對void指針進行算法操作。

15、return語句不可以返回指向“棧內存”的“指針”,因爲該內存在函數結束時自動銷燬。

16、const在C和C++中的區別:

在C語言中:
1》 const修飾的是隻讀的變量,並不是一個常量(因此不可以做數組下標)
2》 節省空間,避免不必要的內存分配,同時提高效率
編譯器通常不爲普通const只讀變量分配內存空間,而是將它們保存在符號表中,這使得它成爲一個編譯期間的值,沒有了存儲與讀內存的操作,使得它的效率也很高。
3》 修飾一般變量,可以用在類型說明符前,也可以在類型說明符後面。
4》 修飾數組,用法同上
5》 修飾指針。離哪個近就修飾誰
6》 修飾函數的參數。不希望這個參數在函數體內被意外改變時用
7》 修飾函數的返回值,表示返回值不能改變。
Const和#define的比較:
1》 Const定義的只讀變量從彙編的角度來講,只給出了對應的內存地址,而不像是#define給出了立即數,所以const定義的只讀變量在程序運行過程中只有一份備份(因爲它是全局的只讀變量,存放在靜態區),而#define定義的宏常量在內存中有若干個備份。#define宏是在預編譯階段進行替換,而const修飾的只讀變量是在編譯時確定其值的。
2》 #define宏沒有類型,而const修飾的只讀變量有特定的類型。
在C++語言中:
1》 const修飾形參,一般和引用同時使用
2》 const修飾返回值
3》 const修飾類數據成員,必須在構造函數的初始化列表中初始化
4》 const修飾類成員函數,實際修飾隱含的this,表示在類中不可以對類的任何成員進行修改(將const加在函數後面)
5》 在const修飾的成員函數中要對類的某個數據成員進行修改,該數據成員定義聲明時必須加mutable
問題:
1》 const對象可以調用非const成員函數(不可以)和成員函數(可以)嗎?
2》 非const對象可以調用非const成員函數(y)和成員函數(y)嗎?
3》 Const成員函數內可以調用它的const成員函數(y)和非const成員函數(n)嗎?
4》 非Const成員函數內可以調用它的const成員函數(y)和非const成員函數(y)嗎?
Const在C和C++中最大的不同:
在C中const默認具有外部鏈接,而C++中則是內部鏈接。
內連接:也就是說它只能在定義它的文件中使用,連接時其他編譯單元看不見他。
外鏈接:就可能導致同一個變量在不同的CPP文件中都分配了地址

17、volatie—是易變不穩定的意思。

用它修飾的變量表示可以被某些編譯器未知的因素修改,可以保證對特殊地址的穩定訪問。

18、內存地址的最小單元爲1個字節,空結構體的大小爲1個字節。

19、在C++裏struct的成員默認是public,而class成員默認是private

20、在C++裏union的成員默認爲public,union主要用來壓縮空間,如果一些數據不可能在同一時間同時被用到,則可以使用union。

21、大小端:對於一個由2字節組成的16位整數,在內存中存儲這兩個字節有兩種方法:一種是將底序字節存儲在起始地址,稱爲小端模式;另一種方法是將高序字節存儲在起始位置,稱爲大端模式。(一般先讀取高地址存儲的數據)
大端模式(Big-endian):字數據的高字節存儲在低地址中,而字數據的低字節存儲在高地址中。
小端模式(Little-endian) : 字數據的高字節存儲在高地址中,而字數據的低字節存儲在低地址中。

22、判斷當前系統的大小端:

intCheckSystem { unioncheck{inti;charch; }c; c.i =1;returnc.ch ==1; }intmain {intret = CheckSystem;return0; }23、位域:
1》位域是指信息在存儲時,並不需要佔用一個完整的字節,而只需佔用幾個或者一個二進制位。
2》所謂“位域”是把一個字節中的二進位劃分爲幾個不同區域,並說明每個區域的位數。每個區域有一個位名,允許在程序中按照域名進行操作。這樣就可以把幾個不同的對象用一個字節的二進制與來表示。

24、枚舉與#define宏的區別:

1》#define宏是在預編譯階段進行簡單題替換的;枚舉常量編譯的時候確定其值的。
2》一般在調試器裏,可以調試枚舉常量,但是不能調試宏常量。
3》,枚舉可以一次性定義大量相關的常量,而#define宏一次只能定義一個。

25、typedef:是給一個已經存在的類型取一個別名。

——再把typedef和const放在一起看看:

typedefstructstrudent {inta; }Stu_st,* Stu_pst;這裏1》const Stu_pst stu3; 2》Stu_pst const stu3; 以前說過const int i 和 int const i 完全一樣。const 放在類型前類型後都可以 但是const int* p 和 int* const p則完全不一樣(一個修飾指針指向的內容;一個修飾指針)26、

27、單引號、雙引號

1》雙引號引起來的都是字符串常量;單引號引起來的都是字符常量
2》’a’ 和 “a”則完全不一樣,在內存裏前者佔1個字節,後者佔2個字節(還有一個\0)。
3》再看看這個例子:1,‘1’,“1”
1》第一個1是個整形,32位系統下佔4個字節。
2》第二個1是字符常量,佔1個字節。
3》第三個1是字符串常量,佔2個字節。

28、邏輯運算符:“||” 和 “&&”

1》“||”只要有一個條件爲真,結果就爲真
2》“&&”只要有一個條件爲假,結果就爲假。
3》例如:

inti=0; intj=0;if(++i0|| ++j0){ //打印出i和j的值(i=1;j=0),,因爲先計算++i>0,發現結果爲真,後面的就不再計算。 }

29、位運算符:“|”,“&”,“^”,”~”,”>>”,”<<”

1》位操作需要用宏定義好後再使用。
2》如果位操作符“~”和“<<”應用於基本類型無符號字符型或無符號短整形的操作數,結果會立即轉換成操作數的基本類型。
3》位運算符不能用於基本類型是有符號的操作數上。
4》一元減運算符不能用在計本類型無符號的表達式上,除非在使用之前對兩個操作數進行大小判斷,且被減數必須大於減數。

30、左移、右移

1》0x01<<2+3;//+優先級高於<<,所以結果是32
2》0x01<<2+30 或者 0x01<<2-3
3》上述兩個操作均會報錯,因爲一個整數長度爲32位,左移32位發生溢出,右移-1位也會溢出。
4》所以左移和右移的位數,不能大於和等於數據的長度,不能小於0.

31、++、–

1》++、–作爲前綴,先自加或者自減,再做其他運算。
2》逗號表達式,i在遇到每一個分號後,認爲本計算單元已經結束,i這個時候自加。

intmain {inti =2;intj =0; j = (i++, i++, i++);return0; }

最後:i=2+1+1+1=5;j=2+1+1=4.

3》也就是說後綴運算是在本單元計算結束後再自加自減的。

intmain {inti =2;intj =0; j = (i++)+(i++)+(i++);return0; }最後:j=2+2+2=6;i=2+1+1+1=5;32、

33、貪心算法

C語言有這樣一個規則,每個符號應該包含儘可能多的字符。也就是說,編譯器將程序分解成符號的方法是:從左到右一個一個讀入,如果該字符可能組成一個符號,那麼再讀入下一個時,判斷已經讀入的兩個字符組成的字符串是否可能是一個符號的組成部分;如果可能,繼續讀入下一個字符,重複上述判斷,知道讀入的字符組成的字符串已經不再可能組成一個有意義的符號。
需要注意的是:除了字符串和字符常量,符號的中間不能嵌有空白(空格、製表符、換行符等)。

34、

35、預處理指令

36、用define宏定義註釋符號

看下這個例子:

#define BSC //

#define BMC /*
#define BMC */

1>BSC my single-line comment 2>BMC my multi-line comment EMC

1和2都錯了,爲什麼呢?因爲註釋先於預處理指令被處理,當這兩行被展開成“//…” 或者 “/* ……/”時,註釋已經處理完畢,此時再出現“//…” 或者 “/……*/”時自然錯誤。因此,試圖用宏開始或者結束一段註釋是不行的。

37、注意:函數宏被調用時是以實參代換形參,而不是“值傳送”。

38、#undef

#undef是用來撤銷宏定義的,也就是說宏的生命週期從#define 到 #undef結束。

39、文件包含:

1》它實際上是宏替換的延伸。有兩種格式:#include 和 #include ” filename”
2》第一種,表示預處理到系統規定的路徑中去獲得這個文件 ,找到文件後,用文件內容替換該語句。
3》第二種,雙引號表示預處理應該在當前目錄中查找文件名爲filename的文件;如沒有找到,則按系統指定的路徑信息搜索其他目錄。找到文件後,用文件的內容替換該語句。
4》需要注意:#include是將已存在文件的內容嵌入到當前文件中,
5》另外,include支持相對路徑

40、#error預處理

1》#error預處理指令的作用是:編譯程序時,只要遇到#error就會生成一個編譯錯誤提示信息,並停止編譯
2》其語法格式: #error error-message 注意 error-message 不用雙引號包圍

41、#line預處理

1》#line的作用是改變當前行數和文件名稱,他們是在編譯程序中預先定義的標識符
2》命令的基本形式:#line number[“fliename”] ,例如 #line 30 a.h,其中文件名 a.h可以省略不寫。

42、#pragma預處理:設定編譯器的狀態,或者指定編譯器完成一些特定的動作

1》#pragma message :能夠在編譯信息輸出窗口中輸出相應的信息,他的用法:#pragma message(“消息文本”)
例如:
#ifdef _X86
#pragma message(“_X86 macro activated”)
#endif
當我們定義了這個宏後,編譯器就會在窗口提示這個信息,就不用再但心是否定義過這個宏了沒
2》#pragma code_seg:能夠設置程序中函數代碼存放的代碼段,開發驅動程序的時候會使用到
例如:
#pragma code_seg( [“section-name”[,”section-class”] ] )
3》#pragma once:在頭文件的最開始加入,可以保證頭文件只被編譯一次
4》#pragma hdrstop:表示預編譯頭文件到此爲止,後面的頭文件不進行預編譯。
有時單元之間有依賴關係,比如單元A依賴單元B,所以單元B要先於單元A進行編譯,你可以用#pragma startup指定編譯優先級,
5》#pragma resource:#pragma resource “. dfm”表示把.dfm文件中的資源加入工程。.dfm包括窗體外觀的定義。
6》#pragma warning
例如:#pragma warning (disable:4507 34; once: 4385;error:164)
等價於:#pragma warning(disable; 4507 34) //不顯示4507 和34號警告信息
#pragma warning(once : 4385) //4385號警告信息只顯示一次
#pragma warning(errror:164) //吧164號警告信息作爲一個錯誤
7》#pragma comment:該指令將一個註釋記錄放入一個對象文件或可執行文件中。
例如:#pragma comment( lib . “user32.lib”),該指令用來將user32.lib庫文件加入到本工程中。
8》#pragma pack(n):設置內存對齊數

43、“#”運算符:

例如: #defien SQR(x) printf(“The square of x is %d\n”, ((x)*(x)) )
輸入:SQR(8)
輸出: The square of x is 64
這裏的x並沒有被替換,要修改這種情況,可以加上#
例如: #defien SQR(x) printf(“The square of “#x” is %d\n”, ((x)*(x)) )
輸入:SQR(8)
輸出: The square of 8 is 64

44、“##”運算符:可以用於函數宏的替換部分,可以把兩個語言符號組合成單個語言符號。

例如;#define XNAME(n) x##n
如果這樣使用宏:XNAME(8)
則展開就會是: x8

45、注意:在單一的宏定義中,最多可以出現一次“#”或者“##”,但“##”不能隨意粘合任意字符,必須是合法的C語言標識符

46、指針和數組的定義雨聲明:
注意:定義和聲明的區別:定義分配內存、而聲明沒有;定義只能定義一次,可以聲明多次
1》定義爲數組,聲明爲指針:
extern char* arr;——編譯器認爲a是一個指針變量,佔4個字節。
1》0x00D28000這個地址並沒有用到,而是編譯器按照int類型的取值方法一次性取出前4個字節的值,得到0x64636261
2》地址0x64636261上的內容,按照char類型讀/寫。但是這個地址並非是一個有效地址。退一步即使是一個有效地址那也不是我們想要的。
3》應該用 (char*)&arr 取出“abcdef\0”的值
2》定義爲數組,聲明爲指針
在文件1中,編譯器分配4字節空間,並命名爲arr;同時arr裏保存了字符串常量“abcdef”的首字符的首地址,這個字符創常量本身保存在內存的靜態區,其內容不可以更改。
在文件2中,編譯器認爲arr是一個數組,其大小爲4個字節,數組裏保存的是char類型的數據。
1》指針arr裏保存的是字符串常量的地址,0xac588100,而arr本身的地址(0x00818000)並沒有用到。
2》編譯器把指針便令arr當做一個包含4個char類型數據的數組來使用,按char類型取出arr[0],arr[1],arr[2],arr[3]的值爲0xac,0x58,0x81,0x00
但這個並非我們所要的某塊內存的地址。如果給arr[i]賦值會把原來arr中保持的真正地址覆蓋,導致再也無法找出其原來指向的內存。
3》應該用 (char*)(int)arr 取出“abcdef\0”的值。
注意:以後一定要確認你的代碼在一個地方定義爲指針,在另一個地方只能聲明爲指針;在一個地方定義爲數組,在另一個地方只能聲明爲數組。47、指針和數組的特性

48、指針數組數組指針

1》指針數組:( int *p1[10]; )首先它是一個數組,數組的元素都是指針,數組佔多少字節由數組本身決定。它是“存儲指針的數組”的簡稱
2》數組指針:( int (*p2)[10]; )首先它是一個指針,它指向一個數組。在32位系統下永遠佔4字節,至於它指向的數組佔多少字節並不知道。它是
“指向指針的數組”的簡稱。
注意:這裏需要明白一個符號之間的優先級問題,
“”的優先級比“”的優先級高,p1先於結合,構成數組的定義,數組名爲p1,int修飾的是數組的內容,即數組的每一個元素。即p1是一個數組其包含10個指向int類型數據的指針,即指針數組。
至於p2 *和p2構成一個指針的定義,指針變量名爲p2,int修飾的是數組的內容,即數組的每一個元素。即p2是一個指針,它指向一個包含10個int類型數據的數組,即數組指針。

49、

structTest {intNum;char*pcName;shortsDate;charcha[2];shortsba[4]; }*p;intmain {printf("%x\n", p +0x1);printf("%x\n", (unsignedlong)p +0x1);printf("%x\n", (unsignedint*)p +0x1);return0; }

假設p的值爲0x100000,sizeof(Test)=20

1》p+0x1:即爲0x100000+0x1*sizeof(Test)=0x100014
2》(unsigned long)p+0x1:這裏涉及強制類型轉換,將指針變量p保存的值強制轉換成無符號的長整型數。所以這個表達式就是一個無符號的長整形數加上另一個整數,其值爲0x100001
3》(unsigned int)p+0x1:這裏p被強制轉換爲指向無符號整型的指針,所以其值爲0x10000+sizeof(unsigned int)*0x1
等於0x100004;

50、
51、二維數組intmain {inta[3][2] = { (01), (23), (45) };int*p= NULL ; p = a[0];printf("%d", p[0]);return0; }

注意:這裏花括號裏嵌套的是小括號,而不是花括號。這裏是花括號裏嵌套了逗號表達式,其實就相當於 int a[3][2]={1,3,5};

52、
53、二級指針
一級指針保存的是數據的地址;二級指針保存的是一級指針的地址。

54、數組參數和指針參數

——在C語言中,當一維數組作爲函數參數的時候,編譯器總是把它解析成一個指向其首元素地址的指針。

55、一級指針參數

1》能否把指針變量本身傳遞給一個函數

voidfun(char*p) {charc = p [3]; }intmain {char*p2 ="abcdefg"; fun(p2);return0; }
1>在這裏p2只是main函數裏的局部變量,它只在main函數裏面有效。
2>注意:main函數裏面的變量不是全局變量,而是局部變量,只不過他的生命週期和全局變量一樣長。全局變量一定是定義在函數外部的。
3>既然是局部變量,fun函數肯定無法使用p2的真身,函數調用的時候,對實參做一份拷貝並傳遞給被調用的函數。
2》無法把指針變量本身傳遞給一個函數voidGetMemory(char*p,intnum) { p = (char*)malloc(num*sizeof(char)); }intmain {char*str= NULL ; GetMemory(str10); strcpy(str"hello"); free(str);return0; }

在運行strcpy(str,”hello”)語句的時候發生錯誤。這時候str的值任然爲NULL。也就是說str本身並沒有改變,函數malloc的內存地址並沒有賦給str,而是賦給了_str.而這個_str是系統自動分配收回的,我們根本無法使用。所以free(str)並沒有起作用,發生了內存泄漏。

這裏有兩個方法可以獲取函數中分配的內存:
1>用return

#include <string.h>char* GetMemory(char*p,intnum) { p = (char*)malloc(num*sizeof(char));returnp ; }intmain {char*str= NULL ;str=GetMemory(str10); strcpy(str"hello"); free(str);return0; }注意:1>函數返回值類型爲char* 2>主函數裏需要接受函數的返回值

2>用二級指針

#include <string.h>voidGetMemory(char* * p,intnum) { * p = (char*)malloc(num*sizeof(char)); }intmain {char* str = NULL ; GetMemory(&str,10);strcpy(str,"hello");free(str);return0; }

注意:這裏的參數是&str而不是str。這樣的話傳遞過去的是str的地址,是一個值。在函數內部用 “”來開鎖:(&str),其值就是str。所以malloc分配的內存地址是真正賦給了str本身。

56、函數指針

1》定義一個函數指針:

char* (*fun)(char* p1,char* p2);

2》使用一個函數指針

char* fun(char* p1,char* p2) {inti =0; i = _strcmp(p1, p2);if(0== i) {returnp1; }else{returnp2; } }intmain {char* (*pf)(char* p1,char* p2);//定義一個函數指針char*ret =NULL; pf = &fun; ret=(*pf)("aa""bb");return0; }

注意:使用指針時,需要通過鑰匙“*”來取其指向的內存裏面的值,函數指針也是如此。通過用(*pf)取出存在這個地址上的函數,然後調用它。

57、(int)&p——這是什麼?

void FunTest {printf("Call FunTest"); }intmain { void (*p);*(int*)&p = (int)FunTest; (*p);return0; }1> void (*p); ——這行代碼定義了一個指針變量p,p指向一個函數,這個函數的參數和返回值都是void。 2>&p ——&p是求指針變量p本身的地址,這是一個32位的二進制常數。 3>(int*)&p ——表示將地址強制轉換成指向int類型數據的指針。 4>(int)FunTest ——表示將函數的入口地址強制轉換成int類型的數據。 5>*(int*)&p=(int)FunTest; ——表示將函數的入口地址賦值給變量p。 6>(*p) ——表示對函數的調用。58、(*(void(*))0)——這是什麼?
1>
——這是一個函數指針類型,這個函數沒有參數,沒有返回值
2>(void(*))0;
——這是將0強制轉換爲函數指針類型,0是一個地址,也就是說這個函數保存在首地址爲0的一段區域內的函數。
3>(void())0)
——這是取0地址開始的一段內存裏面的內容,其實就是保存在首地址爲0的一段區域內的函數
4>(void())0)`
——這是函數的調用

59、函數指針數組

int*p1[10];//指針數組char* (*pf[3])(char*p);

這是定義一個函數指針數組,它是一個數組,數組名爲pf,數組內存儲了3個指向函數的指針。這些指針指向一些返回值爲指向字符的指針,參數爲一個指向字符的指針,的函數。

60、函數指針數組指針

int(*p1)[10];//數組指針char* (*(*pf)[3])(char*p);

這裏的pf是一個指針。這個指針指向一個包含了3三個元素的數組,這個數組裏面存的是指向函數的指針;這些指針指向一些返回值類型爲(char*)指向字符的指針,參數爲(char*)一個指向字符的指針,的函數。

61、堆、棧和靜態區

1. 堆:由maollc系列函數或new操作符分配的內存。其生命週期由free或者delete決定。在沒有釋放之前一直存在,直到程序結束。其特點是使用靈活,空間比較大,但是容易出錯。
2. 棧:保存局部變量。棧上的內容只在函數的範圍內存在當函數運行結束時,這些內容也會自動銷燬。其特點是效率高,但空間大小有限。
3. 靜態區:保存自動全局變量和static變量(包括static全局和局部變量)。靜態區的內容在整個程序的生命週期都存在,由編譯器在編譯的時候分配。

62、函數的入口校驗

1. 一般在函數入口處使用assert(NULL!=p)對參數進行校驗,在非參數的地方使用if(NULL!=p)來校驗。但是這都有要求,即p在定義的時候被初始化爲NULL。
2. assert是一個宏,而不是函數。包含在assert.h的頭文件中。如果其後括號裏的值爲假,則程序終止運行,並提示錯誤;如果爲真,則繼續運行後續代碼。這個宏只是在Debug版本里起作用,而在Relese版本里被編譯器完全優化掉,這樣就不會影響代碼的性能

63、memset(a,0,sizeof(a))

memset有3個參數:第一個參數是要被設置的內存起始地址;第二個參數要被設置的值;第三個參數是要被設置的內存大小,單位爲字節。

64、malloc函數

先看一下malloc函數的原型:
(void*)malloc(int size);
malloc函數的返回值是一個void類型的指針,參數爲int類型的數據,即申請分配的內存大小,單位是字節。內存分配成功之後,malloc函數返回這塊內存的首地址,你需要一個指針來接收這個地址。但由於函數的返回值是void*類型的,所以必須要強制轉換成你所要接收的類型。
比如:

char* p=(char*)malloc(100);

在堆上分配了100字節的內存,返回這苦啊內存的首地址,把地址強制轉換爲(char*)類型後賦給(char*)類型的指針變量p;同時告訴我們這塊內存將用來存儲char類型的數據。也就是說只能通過指針變量p來操作這塊內存。這塊內存本身並沒有名字,對它的訪問是匿名的。

但是,並不是每一次內存分配都是成功的,所以在我們使用只想這塊內存的指針時,必須用if(NULL != p)語句來驗證內存確實分配成功了。

65、用malloc函數申請0字節內存

申請0字節內存並,函數不會返回NULL,而是返回一個正常地址,但是你卻無法使用這塊大小爲0的內存。
對於這一點要小心,因爲這個時候if(NULL != p)語句檢驗將不起作用。
發佈了27 篇原創文章 · 獲贊 19 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章