Xiyoulinux group 17.18.19納新題知識點總結

三年納新題主要知識點總結


主要知識點

  1. printf的返回值
  2. sizeof操作符與字符串中的轉義字符(\)
  3. static關鍵字
  4. const關鍵字
  5. 不同類型的數據在進行運算時發生的隱式自動類型轉換
  6. 結構的字節對齊
  7. 大小端
  8. 二維數組與指針以及運算
  9. C 語言從源程序到可執行程序的四個步驟
  10. sizeof與strlen的區別
  11. 函數指針與返回函數指針的函數

1.printf的返回值

以這一段代碼爲例

int main(int argc, char *argv[])
{
int a = 10, b = 20, c = 30;
printf("%d\n", printf("%d ", a+b+c));
return 0;
}

仔細一看,這個程序特殊的地方就是在printf函數里加了一個printf函數。那麼,這個程序會輸出什麼呢?
答案就是60 3
讓我們來解讀一下
第一步:第一個printf的輸出結果是60,這很好理解。
第二布:第二個printf的輸出結果爲什麼會是3呢?這是因爲printf函數是有返回值的,它返回一個int值,表示被打印的字符數,因此返回的是3。(60有兩個字符加上一個空格)

2.sizeof操作符與字符串中的轉義字符(\)

int main(int argc, char *argv[])
{
int t = 4;
printf("%lu\n", sizeof(t--));
printf("%d\n", t);
printf("%lu\n", sizeof("ab c\nt\012\xa1*2"));
printf("%lu\n",strlen("ab c\nt\012\xa1*2"));
return 0;
}

這段代碼的輸出結果分別是4 4 11 10。
因爲sizeof操作符在編譯時就已經起作用。所以sizeof後括號內t的自減爲無效操作,因此前兩個均輸出4。
而後面字符串ab c\nt\012\xa1*2之所以長度爲10的原因是所有的ASCII碼都可以用“\”加數字(一般是8進制數字)來表示。而C中定義了一些字母前加“\”來表示常見的那些不能顯示的ASCII字符,如\0,\t,\n等,就稱爲轉義字符,因爲後面的字符,都不是它本來的ASCII字符意思了。而在\之後三個數字若均爲阿拉伯數字則用8進制數字表示,之後爲x與兩個字符的組合則用16進制數字來表示一個字符,例如\101表示爲A,\x41也表示爲A。

3.static關鍵字

static在c語言中有三個用法
1.static 函數類型 函數名(函數參數表){……}
此用法可定義內部函數,內部函數的作用域僅侷限於本源文件。
使用內部函數的好處是:不同的人編寫不同的函數時,不用擔心自己定義的函數,是否會與其它文件中的函數同名,因爲同名也沒有關係。

2.static 全局變量
此用法可定義靜態全局變量,與第一種用法的作用大同小異,作用域僅侷限於本源文件。
好處也是不用擔心同名。

3.static 局部變量
此用法可定義靜態局部變量,靜態變量只初始化一次,作用域與局部變量作用域相同,但是生存週期同全局變量相同。

最後對static的三條作用做一句話總結。首先static的最主要功能是隱藏,其次因爲static變量存放在靜態存儲區,所以它具備持久性和默認值0。

4.const關鍵字

const char *p;
char const *p;
char *const p;

以上三個表達式1與2的意思是相同的,表示指針p所指變量的值不能被改變。
式子3的意思是指針p所值的變量不能被改變,即指針變量p的值爲一個const值。

我們可以這樣理解*p可以表示爲p所值的變量,前面加上const,則 *p的值是一個const值,即p所指變量的值爲const。const加在指針p之前,則表示p的值爲一個const的值,即p只能指向當前所指向的變量。

5.不同類型的數據在進行運算時發生的隱式自動類型轉換

unsigned int a = 10;
int b = -20;
if (a + b > 0)
{
		
printf("a+b = %d\n", a+b);
}
else
{
		
printf("a = %d b = %d\n", a, b);
}

以上代碼輸出結果是a+b=-10,之所以會有這樣的輸出結果是因爲不同類型的數據在進行運算時發生了隱式自動類型轉換。
隱式自動轉換規則
1、執行算術運算時,低類型(短字節)可以轉換爲高類型(長字節);例如: int型轉換成double型,int轉爲unsigned int型,char型轉換成int型等等;
2、賦值表達式中,等號右邊表達式的值的類型自動隱式地轉換爲左邊變量的類型,並賦值給它;
3、函數調用時,將實參的值傳遞給形參,系統首先會自動隱式地把實參的值的類型轉換爲形參的類型,然後再賦值給形參;
4、函數有返回值時,系統首先會自動隱式地將返回表達式的值的類型轉換爲函數的返回類型,然後再賦值給調用函數返回;

6.結構的字節對齊

struct icd
{	
	int x;
	char y;
	double z;
}struct idc
{	
	int x; 
	double y; 
	char z;
};

以上代碼中兩個結構的所佔字節大小分別是16和24。
這裏牽扯到結構在內存中的字節對齊,字節對齊的細節和具體編譯器實現相關,但一般而言,滿足三個準則:
1.結構體變量的首地址能夠被其最寬基本類型成員的大小所整除;
2.結構體每個成員相對於結構體首地址的偏移量都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充字節。
3.結構體的總大小爲結構體最寬基本類型成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充字節。

7.大小端

大端模式,是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中,這樣的存儲模式有點兒類似於把數據當作字符串順序處理:地址由小向大增加,而數據從高位往低位放;這和我們的閱讀習慣一致。
小端模式,是指數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中,這種存儲模式將地址的高低和數據位權有效地結合起來,高地址部分權值高,低地址部分權值低。

對於0x11223344 儲存如下
在這裏插入圖片描述

8.二維數組與指針以及運算

在這裏插入圖片描述
從圖中可以看到,雖然這三個表達式的值在數值上是一樣的,但是在它們分別作+1運算後,所得到的值是完全不同的。
我們知道指針是一個值爲地址的變量,所以我們可以把這些值認爲是指針的值,而&a+1,&a[0]+1,&a[0][0]+1則可以認爲是指針作加一的運算。指針加一的運算規則就是在指針的值上加一個指針所指變量的單位,比如有一個int數組,指針p指向這個數組的首元素,指針p的值是數組的第一個int變量的地址,則p+1的結果就是數組的第二個int變量的地址。
我們再說明一個知識,一維數組可以malloc一段內存然後賦值給一個指針實現,而二維數組可以以二級指針的方式實現,就是一個二級指針指向一級指針,多個一級指針分別指向一個元素。 因此我們可以以指針的方式來理解數組。

&a

a是一個二維數組,可以理解爲a是一個二級指針,而&a是這個二級指針的地址,則可以理解爲是一個指向它的一個三級指針的值。因此再上述例子中&a值的類型爲int***,它所指變量的類型int**,因此&a+1就是下一個int**的地址,也就是整個二維數組之後的那個地址。

&a[0]

同樣的,a[0]是二維數組的首行,可以理解爲一個一級指針,因此&a[0]的值是一個指向它的二級指針的值,則&a[0]值的類型爲int**,所指變量類型爲int*,所以&a[0]+1就是下一個int*的地址,也就是二維數組的第二行的地址。

&a[0][0]

a[0][0]是一個int類型的元素,&a[0][0]就是一個指向它的一級指針的值,則&a[0][0]值的類型爲int*,所指變量類型是int,&a[0][0]+1就是下一個int的地址,也就是二維數組首行第二個變量的地址。

9.C 語言從源程序到可執行程序的四個步驟

1.預處理
在這一階段,源碼中的所有預處理語句得到處理,例如#include語句所包含的文件內容替換掉語句本身,所有已定義的宏被展開,根據#ifdef,#if等語句的條件是否成立取捨相應的部分,預處理之後源碼中不再包含任何預處理語句,生成.i文件
2.編譯
這一階段,編譯器對源碼進行詞法分析、語法分析、優化等操作,最後生成彙編代碼.s文件。這是整個過程中最重要的一步,因此也常把整個過程稱爲編譯。
3.彙編
這一階段使用彙編器對彙編代碼進行處理,生成機器語言代碼,保存在後綴爲.o的目標文件中
4.鏈接
鏈接的主要內容是把各個模塊之間相互引用的部分處理好,將各個目標文件鏈接生成.exe可執行文件

10.sizeof與strlen的區別

int main(int argc, char *argv[])
{
printf("%lu\n", sizeof("abc"));
printf("%lu\n",strlen("abc"));
return 0;
}

以上代碼的輸出值分別爲4與3。
sizeof的返回值是字符串的字節大小,字符串均是由’\0’結尾(系統會自動在字符串末尾加上’\0’),因此字符串的字節大小應該是字符串長度加上末尾的’\0’,也就是字符串長度加一。
strlen返回值是字符串的長度,因此不會加上末尾的’\0’。

11.函數指針與返回函數指針的函數

我們先來了解一下函數指針。
函數指針是指向函數的指針,定義的一般形式如下
類型名 (*指針變量名) ();
例如:int (*p)(int a);
即指針p指向一個含有一個int的參數,且返回值爲int值的函數。

size_t q(size_t b)
{
return b;
}
size_t (*p(char *str))(size_t a)
{
printf("%s\n", str);
return q;
}
int main(int argc, char *argv[])
{
char str[] = "XiyouLinuxGroup";
printf("%lu\n", p(str)(strlen(str)));
return 0;
}

我們再來分析以下以上程序
(1)程序先初始化一個字符數組str。
(2)我們先來分析以下size_t ( *p(char *str))(size_t a)這個函數,這是一個返回函數指針的函數,這個可能比較難以理解,我們首先抓住p,因爲運算符()的優先級比 *高,所以p先與(char *str)結合。p(char *str)是含有一個char *類型參數的函數,這個函數的返回值比較特殊,它的返回值類型爲size_t ( *)(size_t a),也就是一個函數指針, 該指針指向的函數含有size_t參數
(3)函數的調用,調用函數p並以str爲實參,函數p返回指向函數q的指針,再通過指向函數q的指針調用函數q,調用完成後打印返回值。

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