malloc
和free
維護一個內存池數組
malloc
老是分配一整塊內存。根據編譯器的實現,實際分配的內存也有可能比請求的稍大一些malloc
先向OS申請新的內存,仍是不夠時返回NULL
。所以malloc
返回的指針必須先檢查free
的參數必須爲經過malloc
等函數申請到的內存指針或NULL
,它也老是釋放整個分配到的內存calloc
:返回指針前將整塊內存都初始化爲0realloc
:bash
realloc
可能返回一塊新內存的指針(原先的內容會被複制過去),所以realloc
後必須更新指針NULL
時,與malloc
行爲相同⚠️函數指針(這裏過重要了吧!!)函數
此處重點在於函數調用符(即函數名後的括號)的優先級比間接訪問要高,所以工具
int *foo(); // foo是一個返回int*指針的函數
foo
優先與函數調用符結合,成爲一個函數,而後對該函數的值(即返回值)進行間接訪問獲得int
,可據此推論函數返回值爲int*
測試
int (*foo)(); // foo是一個返回int的函數的指針
foo
被迫先與間接訪問符結合,也就是說*foo
做爲總體與函數調用符結合,即*foo
爲函數,則foo
爲函數指針命令行
int (*foo[])(); // foo是一個函數指針數組
foo
先與[]
結合,說明foo
是一個數組名,接着foo[]
與*
結合,說明foo
中的元素爲某種指針,最後*foo[]
與函數調用符()
結合,並參照類型聲明爲int
,說明元素爲返回int
值的函數指針(暈了。。)debug
函數名在表達式中的角色與數組有些相似,也是以相似指針常量的形式出現的,所以指針
void f(int arg); // f爲接收1個int參數,無返回值的函數 void (*fPtr)(int arg) = f; // 函數名能夠做爲指針被直接複製給函數指針 void (*fPtr)(int arg) = &f; // 也能夠先取址再賦值給函數指針
事實上,&
只是顯式地執行了編譯器將隱式執行的工做code
命令行參數遞歸
argc
:參數個數(即argv
數組的長度)argv
:字符串指針數組(命令行中空格分隔的每一截都爲一個參數),一般經過檢查字符串開頭是否爲-
來識別選項參數字符串常量自己其實就是一個常量指針
char *strPtr = "abcdefg"; "abcdefg"[2] == 'c'; // true "abcdefg" + 2 == strPtr + 2; // true,所以它也能夠做爲一個字符串指針在printf裏直接用%s輸出
預約義符號(不是預處理指令!)
__FILE__
:%d
源代碼名(test.c
)__LINE__
:%d
文件當前行號(25
)__DATE__
:%s
文件被編譯的日期(Jan 31 1997
)__TIME__
:%s
文件被編譯的時間(13:07:02
)__STDC__
:%d
編譯器是否遵循ANSI C標準(1
/ 0
)⚠️帶參數宏
#define macro(arg1, arg2) stuff // 參數列表的左括號必須緊鄰宏名,不然就會被認爲是stuff的一部分
(之前對宏有誤解,覺得是單純的把一段文本替換成另外一段文本,而實際上宏是先被按照符號解析再轉換爲文本進行替換的)
#define
定義能夠包含其餘#define
定義的符號,但宏不能夠遞歸(大概是由於沒有調用棧這種東西)利用printf
會將直接相鄰的字符串鏈接起來的特性,能夠實現簡單的輸出包裝:
#define PRINT(FORMAT, VALUE) printf("This value is "FORMAT".\n", VALUE) // 注意沒有分號! PRINT("%d", x + 4); // 則FORMAT爲"%d",VALUE爲x + 4 // 實際被轉換爲: printf("This value is ""%d"".\n", x + 4); // 三個字符串被自動鏈接
⚠️符號鏈接符##
:產生的新符號必須合法,不然就是undefined行爲
#define ADD_TO_SUM(sum_number, value) sum##sum_number += value // 注意沒有分號! ADD_TO_SUM(5, 23); // 5將做爲sum_number的值被鏈接到sum,產生sum5這個符號,最後整個宏被替換爲sum5 += 23; // 宏被引用時上下文中必須存在名爲sum5的變量
(這就是爲何sizeof不是一個函數吧,由於類型自己不能做爲參數傳遞,以及可變參數列表也是用宏來實現)
帶反作用的宏參數可能致使嚴重錯誤,好比:
++
/ --
:改變參數自己getchar()
:消耗I/O buffer命令行定義:
-D{name}
/ -D{name}=stuff
=== #define name stuff
(未指定時stuff默認爲1)-U{name}
=== #undef name
條件編譯:#if
/ #endif
/ #elif
/ #else
測試符號是否已被定義:
#ifdef symbol
=== #if defined(symbol)
#ifndef symbol
=== #if !defined(symbol)
⚠️能夠結合命令行定義來完成條件編譯
源代碼test.c
:
#ifndef CHOICE // 以避免與命令行定義衝突 #define CHOICE 5 #endif int main() { printf("Choice: %d\n", CHOICE); return 0; }
直接編譯執行:
> gcc test.c -o test > ./test > Choice: 5
編譯時使用命令行定義:
> gcc -DCHOICE=2 test.c -o test # 提早定義CHOICE爲5 > ./test > Choice: 2
#endif
後用註釋標註它結束的是哪個條件以提升程序可讀性⚠️使用條件編譯來避免因爲頭文件被重複包含致使的符號衝突
#ifndef _HEADERNAME_H #define _HEADERNAME_H 1 // 若是沒有1則_HEADERNAME_H被定義爲空字符串,但仍然被定義,不會影響判斷結果 #endif
#progma
指令是不可移植的,不一樣平臺的預處理器可能會徹底忽略它,或以不一樣的方式執行它