c基礎知識點記錄(一)

uname -a :查看linux的版本號
cat /proc/version :正在運行的內核版本
cat /etc/issue:     發行版本信息
gcc -v :gcc版本

int 4 字節   char 1字節   float 4字節 指針4字節

字符串代表指向這個數組起始字符的指針


1、sizeof是算符,strlen是函數。
sizeof是運算符,在頭文件中typedef爲unsigned int,其值在編譯時即計算好了,參數可以是數組、指針、類型、對象、函數等。
sizeof操作符的結果類型是size_t,它在頭文件中typedef爲unsigned int類型。該類型保證能容納實現所建立的最大對象的字節大小。數組——編譯時分配的數組空間大小;指針——存儲該指針所用的空間大小(存儲該指針的地址的長度,是長整型,應該爲4)。

strlen是函數,要在運行時才能計算。參數必須是字符型指針。當數組名作爲參數傳入時,實際上數組就退化成指針了。
返回字符串的長度。該字符串可能是自己定義的,也可能是內存中隨機的,該函數實際完成的功能是從代表該字符串的第一個地址開始遍歷,直到遇到結束符NULL \0 。返回的長度大小不包括NULL, \0。

char* ss = "0123456789";
sizeof(ss) 結果 4 :ss是指向字符串常量的字符指針,sizeof 獲得的是一個指針所佔的空間,應該是長整型的,所以是4
sizeof(*ss) 結果 1 :*ss是第一個字符 其實就是獲得了字符串的第一位'0' 所佔的內存空間,是char類型

變量定義就是告訴編譯器在何處創建變量的存儲,以及如何創建變量的存儲。
變量聲明向編譯器保證變量以指定的類型和名稱存在,這樣編譯器在不需要知道變量完整細節的情況下也能繼續進一步的編譯。變量聲明只在編譯時有它的意義,在程序連接時編譯器需要實際的變量聲明。

左值(lvalue):指向內存位置的表達式被稱爲左值(lvalue)表達式。左值可以出現在賦值號的左邊或右邊。
右值(rvalue):術語右值(rvalue)指的是存儲在內存中某些地址的數值。右值是不能對其進行賦值的表達式,也就是說,右值可以出現在賦值號的右邊,但不能出現在賦值號的左邊。

全局變量保存在內存的全局存儲區中,佔用靜態的存儲單元;局部變量保存在棧中,只有在所在函數被調用時才動態地爲變量分配存儲單元。
C語言經過編譯之後將內存分爲以下幾個區域:
 (1)棧(stack):由編譯器進行管理,自動分配和釋放,存放函數調用過程中的各種參數、局部變量、返回值以及函數返回地址。操作方式類似數據結構中的棧。
 (2)堆(heap):用於程序動態申請分配和釋放空間。C語言中的malloc和free,C++中的new和delete均是在堆中進行的。正常情況下,程序員申請的空間在使用結束後應該釋放,若程序員沒有釋放空間,則程序結束時系統自動回收。注意:這裏的“堆”並不是數據結構中的“堆”。
(3)全局(靜態)存儲區:分爲DATA段和BSS段。DATA段(全局初始化區)存放初始化的全局變量和靜態變量;BSS段(全局未初始化區)存放未初始化的全局變量和靜態變量。程序運行結束時自動釋放。其中BBS段在程序執行之前會被系統自動清0,所以未初始化的全局變量和靜態變量在程序執行之前已經爲0。
 (4)文字常量區:存放常量字符串。程序結束後由系統釋放。
 (5)程序代碼區:存放程序的二進制代碼

 

\ooo 是對用三位八進制數轉義表示任意字符的形象化描述。

比如 char ch = '\101'; 等價於 char ch = 0101; (以0開頭的表示八進制)。

\xhh 裏面是 x 是固定的,表示十六進制(hexadecimal),h 也表示十六進制。

舉例,char ch = '\x41'; 就是用十六進制來表示,它與前面的 \101 是等價的。

 

const 定義的是變量不是常量,只是這個變量的值不允許改變是常變量!帶有類型。編譯運行的時候起作用存在類型檢查。

define 定義的是不帶類型的常數,只進行簡單的字符替換。在預編譯的時候起作用,不存在類型檢查。

1、兩者的區別

(1) 編譯器處理方式不同

  • #define 宏是在預處理階段展開。

  •  const 常量是編譯運行階段使用。

(2) 類型和安全檢查不同

  •  #define 宏沒有類型,不做任何類型檢查,僅僅是展開。
  •  const 常量有具體的類型,在編譯階段會執行類型檢查。

(3) 存儲方式不同

  • #define宏僅僅是展開,有多少地方使用,就展開多少次,不會分配內存。(宏定義不分配內存,變量定義分配內存。)
  • const常量會在內存中分配(可以是堆中也可以是棧中)。

(4) const 可以節省空間,避免不必要的內存分配。 例如:

#define NUM 3.14159 //常量宏
const doulbe Num = 3.14159; //此時並未將Pi放入ROM中 ......
double i = Num; //此時爲Pi分配內存,以後不再分配!
double I= NUM; //編譯期間進行宏替換,分配內存
double j = Num; //沒有內存分配
double J = NUM; //再進行宏替換,又一次分配內存!

const 定義常量從彙編的角度來看,只是給出了對應的內存地址,而不是象 #define 一樣給出的是立即數,所以,const 定義的常量在程序運行過程中只有一份拷貝(因爲是全局的只讀變量,存在靜態區),而 #define 定義的常量在內存中有若干個拷貝。

(5) 提高了效率。 編譯器通常不爲普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成爲一個編譯期間的常量,沒有了存儲與讀內存的操作,使得它的效率也很高。

(6) 宏替換隻作替換,不做計算,不做表達式求解;

宏預編譯時就替換了,程序運行時,並不分配內存。

static 存儲類指示編譯器在程序的生命週期內保持局部變量的存在,而不需要在每次它進入和離開作用域時進行創建和銷燬。因此,使用 static 修飾局部變量可以在函數調用之間保持局部變量的值。

static 修飾符也可以應用於全局變量。當 static 修飾全局變量時,會使變量的作用域限制在聲明它的文件內。

全局聲明的一個 static 變量或方法可以被任何函數或方法調用,只要這些方法出現在跟 static 變量或方法同一個文件中。

 static對局部變量進行修飾過後,其生命週期以及存儲空間發生了變化,但是其作用域並沒有改變,其仍然是一個局部變量,作用域僅限於該語句塊

靜態局部變量如果沒有進行初始化的話,對於整型變量系統會自動對其賦值爲0,對於字符數組,會自動賦值爲'\0'.

static定義的變量只能在當前 c 程序文件中使用,在另一個 c 代碼裏面 , 即使使用 extern 關鍵詞也不能訪問這個static變量。如果實在函數內部定義的,那麼這個變量只初始化一次,即使再次調用這個函數,這個static變量也不會再次初始化 , 這個變量的取值就會一直保存着,也就是說,當你再次調用這個函數的時候,裏面用到這個static變量時,就會發現,它還是上一次函數調用時的結果。

 

extern 修飾符通常用於當有兩個或多個文件共享相同的全局變量或函數的時候 

判斷一個整數是否是2的整數次冪

二進制數的位權是以2爲底的冪,如果一個整數 m 是 2 的 n 次冪,那麼轉換爲二進制之後只有最高位爲 1,其餘位置爲 0,m&(m-1) 的結果爲 0 

如果兩個不同長度的數據進行位運算時,系統會將二者按右端對齊,然後進行位運算。

以“與”運算爲例說明如下:我們知道在 C 語言中 long 型佔 4 個字節,int 型佔 2 個字節,如果一個 long 型數據與一個 int 型數據進行“與”運算,右端對齊後,左邊不足的位依下面三種情況補足:

  •  (1)如果整型數據爲正數,左邊補 16 個 0。
  •  (2)如果整型數據爲負數,左邊補 16 個 1。
  •  (3)如果整形數據爲無符號數,左邊也補 16 個 0。

 在函數聲明中,參數的名稱並不重要,只有參數的類型是必需的,函數聲明會告訴編譯器函數名稱及如何調用函數

 

argc 和 argv 是 main 函數的形式參數。

這兩個形式參數的類型是系統規定的。如果 main 函數要帶參數,就是這兩個類型的參數;否則main函數就沒有參數。

 [0] 、a[1]...a[i] 代表的都是值,a、(a+0)、(a+1)、(a+i) 代表的是地址;另外這裏的 a 代表整個數組的首地址,相當於 a[0] 的地址,而這裏 (a+1) 就代表的是 a[0+1] 的地址

只能給元素逐個賦值,不能給數組整體賦值。

例如給 10 個元素全部賦值爲 1,只能寫作:int a[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

指針:也是一個變量,存儲的數據是地址,可以進行數值運算

數組名:不是變量,不可以進行數值運算。代表的是該數組最開始的一個元素的地址

int a[10];
int *p;
p = &a[0] // 可以寫成 p = a; 
  • 對數組元素 a[i]的引用也可以寫成*(a+i)這種形式。
  • 賦值語句  p=&a[0] 也可以寫成下列形式: p=a。
  • p 是個指針,p[i]與*(p+i)是等價的。
enum DAY day;
定義枚舉類型的同時定義枚舉變量enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;

枚舉類型不連續,這種枚舉無法遍歷。

空指針:在大多數的操作系統上,程序不允許訪問地址爲 0 的內存,因爲該內存是操作系統保留的。然而,內存地址 0 有特別重要的意義,它表明該指針不指向一個可訪問的內存位置。但按照慣例,如果指針包含空值(零值),則假定它不指向任何東西。 

 

  •  兩個指針不賦 NULL,是壞習慣
  •  初始化指針不賦 NULL,因爲這樣的指針會指向一片未知的區域,這樣的指針不是空指針,但指向一片訪問受限制的內存區域,你無法使用它,這樣的情況下的指針,業界給了它一個形象的名字:“野指針”,而且難以調試,在許多編譯器單步 debug 會出現奇怪的錯誤,但經常看見的 "Segmentation Fault" 這樣的錯誤,實測當代碼多的時候,這是一個非常蛋疼的錯誤,野指針就是成因之一,所以看到這樣的錯誤,首先是想想,是否有某些指針沒有初始化引起的
  •  free() 後指針不賦 NULL,爲指針分配內存後,指針便可以指向一片合法可使用的內存,但使用 free() 釋放那片內存時,指針依舊存放着那片內存的地址,也就是依舊指向那片內存,但這片內存已經釋放,不可訪問,這時若不小心使用了這個指針,便會內存錯誤,又是會有奇怪的 bug ,代碼幾百行多點就會難以調試,業界給這樣的指針也有個統稱:“懸空指針”,爲了避免這種蛋疼的情況出現,一定要釋放內存後,給指向這片內存的指針,都賦值爲 NULL,從中也可以看出,free() 這個函數釋放內存時跟指向這片內存的指針並沒有什麼卵關係,不會連着把指針一起搞定掉的! 珍愛生命,遠離 "野指針" 與 "懸空指針" !

 

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