函數前使用extern沒有意義;extern用在變量前表示變量是一個外部鏈接符號。(函數天然是一個外部鏈接符號)
——與此相關的,指定函數的調用風格(__cdecl, __stdcall, __fastcall)等,或者生成動態連接庫時(__declspec(dllimport)、__declspec(dllexport)),或者指定編譯語言類型時(C, C++)時,都只需要在 聲明體(.h文件) 中說明,不需要在實現體(.c文件)中說明。
同時,static對於函數和變量的意義也不同。
非可移植的內存分配技巧:(要求平台的malloc庫實現時采用連續分配,用途:概要訪問,隱式存儲)
struct name {
int namelen;
char namestr[1];
};
#include <stdlib.h>
#include <string.h>
struct name *makename( char *newname )
{
struct name *ret = malloc(sizeof(struct name)-1 + strlen(newname) + 1);
if (ret != NULL) {
ret->namelen = strlen(newname);
strcpy(ret->namestr, newname);
}
return ret;
}
C99引入了許多C++中已有的特性:結構體越來越像 類 了——可以創建無名的臨時 結構體變量
結構體的sizeof值是內存占用范圍,“空洞”也計算在內
如何計算field在struct中的字節偏移量?
非可移植方案:#define offsetof(struct, f) (size_t) ( (char*) (struct*)0->f - (char*) (struct*)0 )
不要使用內建的==或!= 來比較結構體變量——編譯器對齊操作可能會導致結構體的稀疏,出現空洞
在需要指針型的地方,如何傳入一個常量?
如intf(int *)函數,怎樣傳入一個常量? ——在C99中,可以使用“復合字面量”:f( (int[]){5} ); 類似Java??
函數名本質上也是一個地址,類似數組名; 函數指針本質上也是一個可以參與 賦值、算術 的變量。 () 是唯一可以用於函數指針的後綴運算符。
不同的是,函數名、函數指針的地址實際上是代碼區或者說ROM的地址,而數據變量名、數據指針的地址實際上是數據區RAM的地址,二者不能通用。例如,空的函數指針為void (*)(),空的數據變量指針為void *;
NULL沒有想象中的省力——在函數參數傳入時,仍然需要使用類型轉換。
一般來說,NULL僅僅是一個提示:這裡是一個指針0,沒有其他意義。所以,在不需要指針的地方(如ASCII空字符:)不要用它。
使用規則:
1、在需要空指針常量時,用0或NULL都是等價的;
2、在函數調用傳入0時,根據原型在0或NULL前添加強制轉換;
為什麼需要NULL?因為NULL(表示空指針)在某些平台上實現為0,而在另一些平台上用特殊值來實現——為什麼C標准不把它統一定義為0?因為在某些平台上用特殊值來觸發自動的硬件陷阱,可以捕捉到空指針非法訪問的錯誤,統一用0實現空指針是一種不幸的倒退。
數組並非C語言一級元素,下標也並非一級運算符,而是定義在指針算術運算基礎上——因此,a[i] 等價於 *( (a) + (i) ),也等價於 i[a]。
通過定義不同類型(主要是維數)的指針p,關聯數組a後,可以對p實現++或--不同粒度的重載。
一本C語言的習題匯編:
C Programming FAQs, Steve Summit, 人民郵電2008