C語言的一些零碎知識

2021-09-12 16:03:52 字數 4049 閱讀 4259

寫這篇部落格的目的是加深記憶c語言的一些零碎知識,應付馬上到來的實習生面試。

為了提高 cpu 的儲存速度,編譯器會對 struct 和 union的儲存進行優化,即進行位元組對齊。對於 struct 或 union 中的 struct 或者 union 來說,它們的位元組對齊標準就是它的所有成員中位元組數最大的資料的位元組數。

下表是各個變數占用的位元組數:

char1位元組

short2位元組

int4位元組

long4位元組

long long8位元組

float4位元組

double8位元組

bool1位元組

位元組對齊有三個標準:

1.結構體的首位址是結構體內占用位元組最大的成員型別的整數倍;

2.結構體每個成員相對於結構體首位址的偏移量都是成員大小的整數倍;

3.結構體的總大小為結構體最大基本型別成員大小的整數倍;

舉個例子:

struct a;

struct b;

按照我們一般的思路,應該是sizeof(struct a)==sizeof(struct b)==7,但是實際上不是這樣,根據上面三條規則很容易計算出sizeof(strcut a)值為8;sizeof(struct b)的值是12。

各個硬體平台對儲存空間的處理上有很大的不同。一些平台對某些特定型別的資料只能從某些特定位址開始訪問。比如有些架構的cpu在訪問 乙個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下程式設計必須保證位元組對齊.其他平台可能沒有這種情況,但是最常見的是如果不按照適合其平台要求對 資料存放進行對齊,會在訪問效率上帶來損失。比如有些平台每次讀都是從偶位址開始,如果乙個int型(假設為32位系統)如果存放在偶位址開始的地方,那麼乙個讀週期就可以讀出這32bit,而如果存放在奇位址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit資料。

其實也很好理解,假設struct整型變數的位址不是自然對齊,比如為0x00000002,則cpu如果取它的值的話需要訪問兩次記憶體,第一次取從0x00000002-0x00000003的乙個short,第二次取從0x00000004-0x00000005的乙個short然後組合得到所要的資料,如果變數在0x00000003位址上的話則要訪問三次記憶體,第一次為char,第二次為short,第三次為char,然後組合得到整型資料。

1. static

這個關鍵字很常見,也很重要,其實一直對這個關鍵字不是完全了解,現在做個總結。

顧名思義,static是靜態的意思,其實也

第乙個作用:修飾變數。變數又分為區域性和全域性變數,但它們都存在記憶體的靜態區。

靜態全域性變數,作用域僅限於變數被定義的檔案中,其他檔案即使用extern宣告也沒法使用他。準確地說作用域是從定義之處開始,到檔案結尾處結束,在定義之處前面的那些**行也不能使用它。想要使用就得在前面再加 extern ***。

靜態區域性變數,在函式體裡面定義的,就只能在這個函式裡用了,同乙個文件中的其他函式也用不了。由於被 static 修飾的變數總是存在記憶體的靜態區,所以即使這個函式執行結束,這個靜態變數的值還是不會被銷毀,函式下次使用時仍然能用到這個值。

static int j;

void fun1(void)

void fun2(void)

int main()

return 0;

在這裡j作靜態全域性變數,在程式的整個執行過程中每次執行fun1()時,j都會被初始化,所以在main函式中,最後j還是1;

而i是靜態區域性變數,在main函式中呼叫fun2()時,i的值並不會被銷毀,所以i在執行十次後變為11.

第二個作用:修飾函式。函式前加 static 使得函式成為靜態函式。但此處「static」的含義不是指儲存方式,而是指對函式的作用域僅侷限於本檔案(所以又稱內部函式)。使用內部函式的好處是:不同的人編寫不同的函式時,不用擔心自己定義的函式,是否會與其它檔案中的函式同名。

2. const

const本來是恆定不變的意思,const 推出的初始目的,正是為了取代預編譯指令,消除它的缺點,同時繼承它的優點。

在程式設計中要盡可能多的使用const,這樣可以獲得編譯器的幫助,以便寫出健壯性的**。

1.const 修飾的唯讀變數

定義 const 唯讀變數,具有不可變性。

2.修飾指標

1.const int *p = &a;

這裡const修飾*p,表示*p不能變,也就是p指向的物件不變

2.int* const p = &a;

const修飾p,指標p是不可變的,也就是p指向的記憶體單元不可變。即p的指向不可變,p指向的記憶體單元的內容可以變。

3.const int* const p = &a;

*p和p都被const修飾了,所以p指向的記憶體單元,和p指向記憶體單元中存放的內容都是不可變的。

3.修飾函式的引數

const 修飾符也可以修飾函式的引數,當不希望這個引數值被函式體內意外改變時使用。

4.修飾函式的返回值

3. register

這個關鍵字請求編譯器盡可能的將變數存在cpu內部暫存器中而不是通過記憶體定址訪問以提高效率。注意是盡可能,不是絕對。你想想,乙個cpu的暫存器也就那麼幾個或幾十個,你要是定義了很多很多register變數,它累死也可能不能全部把這些變數放入暫存器吧,輪也可能輪不到你。雖然暫存器的速度非常快,但是使用 register 修飾符也有些限制的:register 變數必須是能被 cpu 暫存器所接受的型別。意味著 register 變數必須是乙個單個的值,並且其長度應小於或等於整型的長度。 而且 register 變數可能不存放在記憶體中,所以不能用取址運算子「&」來獲取 register 變數的位址。

4. volatile

這個關鍵字其實目前不少不常見,但是非常重要。這個關鍵字字面意思是易變的,其實他就是告訴編譯器自己是易變的。

先看看下面的例子:

int i=10;

int j = i;//(1)語句

int k = i;//(2)語句

這時候編譯器對**進行優化,因為在(1)、(2)兩條語句中,i 沒有被用作左值。這時候編譯器認為 i 的值沒有發生改變,所以在(1)語句時從記憶體中取出 i 的值賦給 j 之後,這個值並沒有被丟掉,而是在(2)語句時繼續用這個值給 k 賦值。編譯器不會生成出彙編**重新從記憶體裡取 i 的值,這樣提高了效率。但要注意:(1)、(2)語句之間 i 沒有被用作左值才行。

再看另乙個例子:

volatile inti=10;

int j = i;//(3)語句

int k = i;//(4)語句

volatile 關鍵字告訴編譯器 i 是隨時可能發生變化的,每次使用它的時候必須從記憶體中取出 i的值,因而編譯器生成的彙編**會重新從 i 的位址處讀取資料放在 k 中。這樣看來,如果 i 是乙個暫存器變數或者表示乙個埠資料或者是多個執行緒的共享資料,就容易出錯,所以說 volatile 可以保證對特殊位址的穩定訪問。

為實現時定義的命令,它允許向編譯程式傳送各種指令例如,編譯程式可能有一種選擇,它支援對程式執行的跟蹤。可用#pragma 語句指定乙個跟蹤選擇。

關於C語言的一些零碎知識

一 資料型別 k r c 長整型至少應該和整型一樣長,而整型至少應該和短整型一樣長 這說明 長整型不一定比短整型大 即 長整型 整型 短整型 預設情況下只有char型別是無符號的,其他基本型別都是有符號的 二 列舉型別 enum 預設是從零開始,後面的一次加一,如果有自定義值,那麼該值前面的依然是從...

C language 一些零碎知識的解釋

1 為什麼需要邊界對齊 特別是在結構體中 成員對齊有乙個重要的條件,即每個成員按自己的方式對齊.其對齊的規則是,每個成員按其型別的對齊引數 通常是這個型別的大小 和指定對齊引數 一般編譯器預設是8位元組 中較小的乙個對齊.並且結構的長度必須為所用過的所有對齊引數的整數倍,不夠就補空位元組。對齊的作用...

關於git的一些零碎知識

git檔案的三個狀態 已修改,已暫存,已提交 git的三個區域 工作區,暫存區,物件庫 git的幾個指標 以master為例 遠端有個master,本地有個master,本地有個指標是指向遠端的master的叫origin master 唯讀分支 git add 與git add 的區別 都是提交所...