C指針基礎知識


指針的聲明

C語言聲明格式:"類型 變量名;"

  • 基本類型:int hoge;
  • 指針類型:int *pointer;

區別在於:

聲明 含義
int hoge; 聲明整數類型的變量 hoge
int *pointer; 聲明 "指向int的指針"類型的變量 pointer

關於int *pointer; 其:

指針類型 指向int的指針
指針類型的變量 pointer
指針類型的值 內存的地址

C語言的數據類型

C語言的數據類型參考:C 數據類型-菜鳥教程

指針運算

對指針進行加 N 運算, 地址的值會增加當前指針所指向數據類型的長度 ✖ N

空指針

確保沒有指向任何一個實際的對象或者函數的指針.通常使用宏定義NULL來表示空指針常量值.

指針與數組

表達式中,數組可以解讀成 "指向它的初始元素的指針".(有例外)
p[i] 是 *(p+i) 的簡便寫法.

C的數組從0開始原因:

  • 解決生活中"差1錯誤"問題
  • 在支持指針的語言中,標號被視作是偏移量,因此從0開始更符合邏輯

當今的操作系統都會給應用程序的每一個進程分配獨立的 "虛擬地址空間".
操作系統負責將物理內存分配給虛擬地址空間,同時還會對每一個內存區域設定"只讀"或者"可讀"屬性.

變量儲存地址

儲存期 含義 壽命
靜態變量 全局變量,文件內的static變量,指定static的局部變量都持有靜態儲存周期 從程序運行時開始,到程序關閉時結束
自動變量 沒有指定static的局部變量持有自動儲存期 到聲明該變量的語句塊執行結束為止
malloc() 變量通過malloc()分配的領域 到free()釋放為止

自動變量(棧)

自動變量重復使用內存區域,因此自動變量的地址不是一定的.

大部分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() ,隨着當前內存區域被重新分配,內容才開始被破壞.

碎片化 : 內存被零零散散分割,出現較多無法利用的細碎空塊.
使用 malloc() 的內存管理過程,可能引發碎片化

realloc()

  • 聲明: void *realloc(void *ptr, size_t size)
  • 功能: 嘗試重新調整之前調用 malloccalloc 所分配的 ptr 所指向的內存塊的大小,即擴展內存區域. 如果通過ptr傳遞的區域后面有足夠大小的空閑空間,則直接實施內存區域擴展. 若沒有足夠多的區域,則分配其他新空間,然后將內容復制過去.
  • realloc同樣可能引發碎片化,但相比malloc較為緩和,但要是用在擴展較大的內存區域,除了復制耗時,也會造成堆中大量的空間過分活躍,此時應該積極使用鏈表.
  • 代碼示例 realloc.c

布局內存對齊

根據硬件(CPU)的特征,對於不同數據類型的可配置地址受到一定的限制.

為了提高CPU的效率,編譯器會適當地進行 邊界調整(布局對齊) ,在結構體中插入合適的填充物(也就是再添加適當大小的儲存空間). 但即便使用手工進行布局對其,也不能提高可移植性.

字節序

字節序,即字節在電腦中存放時的序列與輸入(輸出)時的序列是先到的在前還是后到的在前.

字節排序分為大端字節序小端字節序. 說明見代碼

  • 代碼示例 byteorder.c
  • 無論是整數還是浮點數,內存上的表現形式都隨環境(CPU)的不同而不同.

字節排序

C語言的聲明

要用英語來解讀 C 的聲明.

  1. 首先着眼於標識符(變量名或者函數名).
  2. 從距離標識符最近的地方開始,依照優先順序解釋派生類型(指針.數組和函數).優先順序說明:
    1. 用於整理聲明內容的括弧
    2. 用於表示數組的 [],用於表示函數的 ()
    3. 用於表示指針的 *
  3. 解釋完派生類型,使用"of","to","returning" 等將他們連接起來.
  4. 最后,追加數據類型修飾符(在最左邊,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

指針常量與指針變量

數組指針和指針數組

詳見:數組指針與指針數組


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM