在閱讀陳正衝寫的《C語言深度剖析》的 過程中,發現自己掌握的C語言還不是很深入,很多細節之前學習的時候都沒有注意到,現將其中的知識點進行總結:
目錄
4、字符串以'\0'結尾,那麼'\0'對應的ASCII是什麼?如何通過編程得到'\0'?
附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));
}
}