指針的聲明
C語言聲明格式:"類型 變量名;"
- 基本類型:
int hoge;
- 指針類型:
int *pointer;
區別在於:
聲明 | 含義 |
---|---|
int hoge; | 聲明整數類型的變量 hoge |
int *pointer; | 聲明 "指向int的指針"類型的變量 pointer |
關於int *pointer;
其:
指針類型 | 指向int的指針 |
指針類型的變量 | pointer |
指針類型的值 | 內存的地址 |
C語言的數據類型參考:C 數據類型-菜鳥教程
- 代碼示例 pointer.c
指針運算
對指針進行加 N 運算, 地址的值會增加當前指針所指向數據類型的長度 ✖ N
- 代碼示例 pointer-calc.c
空指針
確保沒有指向任何一個實際的對象或者函數的指針.通常使用宏定義NULL來表示空指針常量值.
- 代碼示例 null_pointer.c
指針與數組
表達式中,數組可以解讀成 "指向它的初始元素的指針".(有例外)
p[i] 是 *(p+i) 的簡便寫法.
C的數組從0開始原因:
- 解決生活中"差1錯誤"問題
- 在支持指針的語言中,標號被視作是偏移量,因此從0開始更符合邏輯
當今的操作系統都會給應用程序的每一個進程分配獨立的 "虛擬地址空間".
操作系統負責將物理內存分配給虛擬地址空間,同時還會對每一個內存區域設定"只讀"或者"可讀"屬性.
- 代碼示例 vmtest.c
變量儲存地址
儲存期 | 含義 | 壽命 |
---|---|---|
靜態變量 | 全局變量,文件內的static變量,指定static的局部變量都持有靜態儲存周期 | 從程序運行時開始,到程序關閉時結束 |
自動變量 | 沒有指定static的局部變量持有自動儲存期 | 到聲明該變量的語句塊執行結束為止 |
malloc() | 變量通過malloc()分配的領域 | 到free()釋放為止 |
- 代碼示例 print_address.c
自動變量(棧)
自動變量重復使用內存區域,因此自動變量的地址不是一定的.
- 代碼示例 auto.c
大部分CPU中已經嵌入棧的功能,C語言通常直接使用.
C語言中,通常將自動變量1保存在棧中.
assert(條件表達式)
:若表達式為真,assert()不執行任何動作,否則在標准錯誤 stderr 上顯示錯誤消息,並中止程序執行.
動態內存分配
malloc():
- 聲明:
void *malloc(size_t size)
- 功能:分配所需的內存空間,並返回一個指向它的指針.
- 在標准C庫中,提供了
malloc/free
函數分配釋放內存,這兩個函數底層是由brk,mmap,munmap這些系統調用實現的,但他們不是系統調用,是標准庫函數.
可能調用 free()
后,對應的內存區域不會立即返還給操作系統,因為可能有2個指針(A與B)在引用當前區域的情況出現,使用指針A引用的區域后倉促調用 free()
,指針B引用的內存區域也不會立即破壞,暫時保留以前的值,直到某個地方執行 malloc()
,隨着當前內存區域被重新分配,內容才開始被破壞.
- 代碼示例 free.c
碎片化 : 內存被零零散散分割,出現較多無法利用的細碎空塊.
使用malloc()
的內存管理過程,可能引發碎片化
realloc()
- 聲明:
void *realloc(void *ptr, size_t size)
- 功能: 嘗試重新調整之前調用
malloc
或calloc
所分配的ptr
所指向的內存塊的大小,即擴展內存區域. 如果通過ptr
傳遞的區域后面有足夠大小的空閑空間,則直接實施內存區域擴展. 若沒有足夠多的區域,則分配其他新空間,然后將內容復制過去. realloc
同樣可能引發碎片化,但相比malloc
較為緩和,但要是用在擴展較大的內存區域,除了復制耗時,也會造成堆中大量的空間過分活躍,此時應該積極使用鏈表.- 代碼示例 realloc.c
布局內存對齊
根據硬件(CPU)的特征,對於不同數據類型的可配置地址受到一定的限制.
為了提高CPU的效率,編譯器會適當地進行 邊界調整(布局對齊) ,在結構體中插入合適的填充物(也就是再添加適當大小的儲存空間). 但即便使用手工進行布局對其,也不能提高可移植性.
- 代碼示例 alignment.c
字節序
字節序,即字節在電腦中存放時的序列與輸入(輸出)時的序列是先到的在前還是后到的在前.
字節排序分為大端字節序和小端字節序. 說明見代碼
- 代碼示例 byteorder.c
- 無論是整數還是浮點數,內存上的表現形式都隨環境(CPU)的不同而不同.
C語言的聲明
要用英語來解讀 C 的聲明.
- 首先着眼於標識符(變量名或者函數名).
- 從距離標識符最近的地方開始,依照優先順序解釋派生類型(指針.數組和函數).優先順序說明:
- 用於整理聲明內容的括弧
- 用於表示數組的 [],用於表示函數的 ()
- 用於表示指針的 *
- 解釋完派生類型,使用"of","to","returning" 等將他們連接起來.
- 最后,追加數據類型修飾符(在最左邊,int double等).
C語言 | 英語表達 | 中文表達 |
---|---|---|
int hoge; | hoge is int | hoge 是 int |
int hoge[10]; | hoge is array(元素個數10)of int | hoge 是 int 的數組(元素個數10) |
int hoge[10][3]; | hoge is array(元素個數10) of array(元素個數3)of int | hoge 是 int 數組(元素個數3)的數組(元素個數10) |
int *hoge[10]; | hoge is array(元素個數10) of pointer to int | hoge 是指向 int 的指針的數組(元素個數10) |
int (*hoge)[3]; | hoge is pointer to array(元素個數3) of int | hoge 是指向 int 的數組(元素個數3)的指針 |
int func(int a); | func is function(參數為int a) returning int | func 是返回 int 的函數(參數為int a) |
int (*func_p)(int a); | func_p is pointer to function(參數為int a) returning int | func_p是指向返回 int 的函數(參數為int a)的指針 |
C語言的表達式
基本表達式:
- 標識符(變量名,函數名)
- 常量(包括整數常量和浮點數常量)
- 字符串常量(使用 " " 括起來的字符串)
- 使用 () 括起來的表達式
左值
作為變量,它有作為 "自身的值" 和作為 "自身內存區域" 兩種情況, 例如 hoge = 10;
和 *hoge_p = &hoge; *hoge_p = 10;
.像這樣表達式代表某處的內存區域的時候,稱當前的表達式為左值(lvalue); 相對的,當表達式代表值的時候,稱當前表達式為右值 .
數組和指針相關的運算符
運算符 | 作用 |
---|---|
* |
單目運算符,稱為解引用.將指針作為操作數,返回指針所指向的對象或者函數. 只要不是返回函數,運算符* 的結果都是左值 |
& |
單目運算符,稱為地址運算符.將一個左值作為操作數,返回指向該左值的指針.對左值的類型加上一個指針,就是& 運算符的返回類型 |
-> |
箭頭運算符,通過指針訪問結構體的成員的時候,用它引用某成員 |
const
const
是在ANSI C中追加的修飾符,它將類型修飾為"只讀"
可以定義const
常量,但const
不一定代表常量
假如有一個結構體:
struct Books{
char *title;
int price;
char isbn[32];
};
void regist_book(struct Books const *book_data)
因為使用了const
所以book_data
所指向的對象是禁止改寫的,但book_data->title
所指向的內容是可以被改寫的.
這種情況是因為由於const
而成"只讀"對象是 "book_data
所指向的對象自身" ,而不包括 "book_data
所指向的對象再向前追溯到的對象" 即結構體內的title
所指向的對象.解決辦法為char const *title
指針常量與指針變量
數組指針和指針數組
詳見:數組指針與指針數組