C語言深度剖析學習筆記

在閱讀陳正衝寫的《C語言深度剖析》的 過程中,發現自己掌握的C語言還不是很深入,很多細節之前學習的時候都沒有注意到,現將其中的知識點進行總結:


目錄

1、sizeof怎麼使用?是關鍵字還是函數?   

2、C語言中有多少個關鍵字?你能想出多少個?

3、C語言的基本數據類型是哪幾個?

4、字符串以'\0'結尾,那麼'\0'對應的ASCII是什麼?如何通過編程得到'\0'?

5、case關鍵字後面的值有什麼要求?

6、const修飾指針時不同 位置的不同效果?

7、extern關鍵詞什麼作用?

8、struct和class的區別是什麼?

9、union關鍵字的特點是什麼?

10、大小端的特點是什麼?

11、enum關鍵字有什麼用?

12、typedef關鍵詞的功能是什麼?

13、C語言中 反斜槓(\)有哪些作用?

14、‘a’和“a”一樣嗎?

15、老生常談的問題:指針和數組有什麼關係?

16、數組指針和指針數組的區分

17、逗號表達式瞭解一下?

18、二維數組作爲參數和二維指針作爲參數的寫法

19、堆、棧和靜態區各存放什麼數據?

20、指針沒有指向合法內存什麼意思?

21、內存泄漏了解一下?

附1:有趣的編程問題一則

附2:另一則有意思的編程題(不使用任何變量編寫 strlen 函數)



1、sizeof怎麼使用?是關鍵字還是函數?   

我們一般以sizeof(int)這樣的方式來獲取某種類型在內存中所佔的空間大小,其形式很像函數吧?但是實際上sizeof是C語言的關鍵詞。正因爲sizeof是關鍵字,它還可以這樣使用:

int i = 0;
sizeof i            //4
sizeof(int)         //4
sizeof(i)           //4
sizeof int          //Error,不能這樣使用

即:sizeof在計算變量所佔空間大小時括號可以省略。計算類型所佔空間大小時括號不能省略。

2、C語言中有多少個關鍵字?你能想出多少個?

C語言中共有32個關鍵詞。 

3、C語言的基本數據類型是哪幾個?

C語言的基本數據類型可以分爲:整型(短整型、整型、長整型)、浮點型(單精度型、雙精度型)和字符類型。分別對應short, int, long, float, double, char。

在32位機器上所佔的內存大小依次爲:short(2 byte), int(4 byte), long(4 byte), float(4 byte), double(8 byte), char (1 byte)。

4、字符串以'\0'結尾,那麼'\0'對應的ASCII是什麼?如何通過編程得到'\0'?

'\0'對應的ASCII碼爲0(NULL)。

整理了幾種方法得到'\0',方法一:

int i = 0;
char chr = (char)i;

方法二:

char chr = NULL;

理解了這個問題,來看一道題目:

int main() 
{ 
    char a[1000]; 
    int i; 
    for(i=0; i<1000; i++) 
    { 
        a[i] = -1-i; 
    } 
    printf("%d",strlen(a)); 
    return 0;
}

答案應該是多少呢?你能解決 這個問題嗎?(255)

5、case關鍵字後面的值有什麼要求?

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

6、const修飾指針時不同 位置的不同效果?

const int *p; // p 可變,p 指向的對象不可變 
int const *p; // p 可變,p 指向的對象不可變 
int *const p; // p不可變,p 指向的對象可變 
const int *const p; //指針 p 和 p 指向的對象都不可變

區分方法:看 const 離哪個近,離誰近就修飾誰。

7、extern關鍵詞什麼作用?

extern 可以置於變量或者函數前,以標示變量或者函數的定義在別的文件中,下面的代碼用到的這些變量或函數是外來的,不是本文件定義的,提示編譯器遇到此變量和函數時在其他模塊中尋找其定義。

8、struct和class的區別是什麼?

struct的 成員默認是public的,而class的成員默認是private的。

9、union關鍵字的特點是什麼?

1.所有的數據成員共用一個空間,同一時間只能存儲一個數據成員的值。所有數據成員有相同的起始地址。

2.union的成員是public的,只配置一個足夠大的空間來容納最大長度的成員。

3.可以利用union的特性來確定系統的大小端。

10、大小端的特點是什麼?

大端模式表示字數據的高字節存儲在低地址,而字數據的低字節存儲在高地址;小短模式表示字數據的高字節存儲在高地址,而字數據的低字節存儲在低地址。可以這樣記憶:大端模式高對應低,小端模式高對應高。一個存在地址0x100處的int型的變量x的16進製表示爲0x01234567,則其大小端的表示如下圖所示:

11、enum關鍵字有什麼用?

一般地,枚舉類型定義 方式如下:

enum enum_type_name
{
    ENUM_CONST_1,
    ENUM_CONST_2,
    ...
} enum_variable_name;

實際上enum_type_name 類型是對一個變量取值範圍的限定,而花括號內是它的取值範圍,即 enum_type_name 類型 的變量 enum_variable_name 只能取值爲花括號內的任何一個值,如果賦給該類型變量的值 不在列表中,則會報錯或者警告。ENUM_CONST_1、ENUM_CONST_2、...、 ENUM_CONST_n,這些成員都是常量,也就是我們平時所說的枚舉常量(常量一般用大寫)。 

enum 變量類型還可以給其中的常量符號賦值,如果不賦值則會從被賦初值的那個常量開始 依次加 1,如果都沒有賦值,它們的值從 0 開始依次遞增 1。

如分別用一個常數表示不同顏 色: 

enum Color
{
    GREEN = 1, 
    RED, 
    BLUE, 
    GREEN_RED = 10, 
    GREEN_BLUE
}ColorVal;

其中各常量名代表的數值分別爲:GREEN = 1、RED = 2 、BLUE = 3、 GREEN_RED = 10、 GREEN_BLUE = 11

12、typedef關鍵詞的功能是什麼?

typedef 的真正意思是給一個已經存在的數據類型(注意:是類型不是變量)取一個別 名,而非定義一個新的數據類型。

13、C語言中 反斜槓(\)有哪些作用?

1.C 語言裏以反斜槓(\)表示斷行。編譯器會將反斜槓剔除掉,跟在反斜槓後面的字符 自動接續到前一行。代碼也可以利用 反斜槓換行哦~

2.表示轉義字符。

14、‘a’和“a”一樣嗎?

不一樣,‘a’是字符,在內存中佔一個字節;“a”是字符串, 在內存中佔兩個 字節,因爲後面還跟一個‘\0’。

15、老生常談的問題:指針和數組有什麼關係?

作者的 回答是:沒有屁關係!很解氣吧?

作者說:指針就是指針,指針變量在 32 位系統下,永遠佔 4 個 byte,其值爲某一個內存的地址。 指針可以指向任何地方,但是不是任何地方你都能通過這個指針變量訪問到。數組就是數組,其大小與元素的類型和個數有關。定義數組時必須指定其元素的類型和個數。數組可以存任何類型的數據,但不能存函數。

雖然的確沒有本質上的關係,但我覺得還是要正面解釋這個問題,發現有一個解釋比較詳細,從歷史的角度解釋了一下指針和數組的愛恨情仇,點這裏瞭解一下

16、數組指針和指針數組的區分

指針數組:首先它是一個數組,數組的元素都是指針,數組佔多少個字節由數組本身 決定。它是“儲存指針的數組”的簡稱。數組指針:首先它是一個指針,它指向一個數組。在 32 位系統下永遠是佔 4 個字節, 至於它指向的數組佔多少字節,不知道。它是“指向數組的指針”的簡稱。

那麼下面到底哪個是數組指針,哪個是指針數組呢:

A),int *p1[10];

B),int (*p2)[10];

需要弄清楚“[]”和“*”的優先級,“[]”的優先級要比“*”的 優先級高,。p1 先與“[]”結合,構成一個數組的定義,數組名爲 p1,int* 修飾的是數組的內容,即數組的每個元素。那現在我們清楚,這是一個數組,其包含 10 個 指向 int 類型數據的指針,即指針數組。至於 p2 就更好理解了,在這裏“()”的優先級比 “[]”高, “*”號和 p2 構成一個指針的定義,指針變量名爲 p2,int 修飾的是數組的內容, 即數組的每個元素。數組在這裏並沒有名字,是個匿名數組。那現在我們清楚 p2 是一個指 針,它指向一個包含 10 個 int 類型數據的數組,即數組指針。

17、逗號表達式瞭解一下?

逗號也是一個運算符,逗號表達式你聽過麼?

C語言提供一種特殊的運算符:逗號運算符,優先級別最低,它將兩個及其以上的式子聯接起來,從左往右逐個計算表達式,整個表達式的值爲最後一個表達式的值。如:(3+5,6+8)稱爲逗號表達式,其求解過程先表達式1,後表達式2,整個表達式值是表達式2的值,因此其值是14。

18、二維數組作爲參數和二維指針作爲參數的寫法

二維數組:int arr[3][4],二維指針int **ptr。

二維數組作爲參數的寫法有兩種:

void fun(char (*p)[4]);        // 這裏的*p兩邊的括號不能去掉!因爲[]的優先級高
void fun(chara[ ][4]);       // 一維數組括號裏的數字可以不寫

針對寫法2需要注意一點:不作爲參數時,在C語言中允許多維數組的第一個維度不聲明具體值,但必須在緊跟其後的初始化的一對{}中給明具體的元素。如:int a[][4]={{1,2,3,4},{5,6}}; 這種寫法是正確的,但int a[][4];這種寫法是錯誤的。 

19、堆、棧和靜態區各存放什麼數據?

靜態區:保存自動全局變量和靜態變量(靜態全局和局部變量)。靜態區的內容在整個 程序的聲明週期內都存在,由編譯器在編譯的時候分配。

棧:保存局部變量、函數的參數值等。棧裏的內容只在函數的範圍內存在,當函數運行結束後這些內容會被自動銷燬。特點是效率高,但空間大小有限。

堆:由malloc或new分配的內存。生命週期由free和delete決定。在沒有釋放內存之前一直存在,直到程序結束。特點是使用靈活,空間比較大,但容易出錯造成內存泄漏。

20、指針沒有指向合法內存什麼意思?

像下面這個例子,編譯沒有問題,但是在運行時會出錯。第14行代碼將字符串“Tomcat”複製到一個野指針name指向的不合法地址中去。因此在程序運行時調用strcpy函數會導致無權訪問出錯。解決方法是使用malloc爲name指針申請一塊內存空間。添加代碼:stu.name = (char *)malloc(sizeof(char));在main函數中。

#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;

struct student 
{
	char *name;
	int age;	
} stu, *pstr;
 
int main()
{
	strcpy(stu.name, "Tomcat");
	stu.age = 20;
	cout << stu.name << endl;
	return 0;
}

另外如果要使用pstu時,因爲pstu也是指針,需要分配內存, 也需要使用malloc函數給pstu指針分配內存空間。

pstu = (structstudent*)malloc(sizeof(structstudent));

21、內存泄漏了解一下?

malloc和free兩個函數配套使用, 一個申請內存,一個釋放內存。但是如果對同一個指針malloc兩次free一次會造成內存泄漏;malloc一次free兩次會出錯。使用 free 函數之後指針變量 p 本身保存的地址並沒有改變,我們需要手動重新把 p 的值變爲 NULL: p = NULL;如果忘記賦值,那麼p將會變成野指針!!!

附1:有趣的編程問題一則

下面的程序在運行的時候會出錯,解釋一下爲什麼?

void GetMemory(char* p,int num)
{
    p = (char*)malloc(num*sizeof(char));
}

int main()
{
    char *str= NULL;
    GetMemory(str, 10);
    strcpy(str, “Hello”);
    free(str);            //free 並沒有起作用,內存泄漏
    return0;
}

這裏存在一個很基本的問題:main函數中調用GetMemory函數時傳的實參str和GetMemory函數中獲得 的形參p是 同一個指針麼?當然不是!GetMemory函數得到的只是str的一個副本的地址,因此在GetMemory函數中分配的空間在這個函數執行完畢之後就被釋放了,在main函數中的free只能導致內存泄漏。那麼如何解決這個問題呢?

方法一:用return

char* GetMemory(char* p,int num)
{
    p = (char*)malloc(num*sizeof(char));
    return p;
}

int main()
{
    char *str= NULL;
    str = GetMemory(str, 10);
    strcpy(str, “Hello”);
    free(str);
    return0;
}

 方法 二:用二級指針!(這個方法我是沒想到過的!)

void GetMemory(char **p,int num)
{
    *p = (char*)malloc(num*sizeof(char));
}

int main()
{
    char *str= NULL;
    str = GetMemory(&str, 10);
    strcpy(str, “Hello”);
    free(str);
    return0;
}

 這裏的實參是&str即str的地址,是一個值,因此傳到函數中的就是其本身而不是副本。而str本身就是一個指向char類型的指針,因此GetMemory函數中的第一個參數爲二級指針,在GetMemory函數內部,p即爲str的地址&str,*p即爲*(&str)也即str,因此malloc所分配的地址就是給str分配的。

附2:另一則有意思的編程題(不使用任何變量編寫 strlen 函數)

這個題目的解法肯定是多種多樣的,我提供一種容易想到的也容易理解的答案。

intmy_strlen( const char* strDest) 
{ 
    assert(NULL!= strDest); 
    if ('\0' == *strDest) 
    { 
        return 0; 
    } 
    else 
    { 
        return(1 +my_strlen(strDest + 1)); 
    } 
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章