首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言指針進階:從基礎操作到函數與數組應用

     編輯:關於C語言

C語言指針進階:從基礎操作到函數與數組應用

聲明:原創作品,轉載時請注明文章來自SAP師太博客,並以超鏈接形式標明文章原始出處,否則將追究法律責任!

第四章 指針核心概念與操作

指針的本質是存儲內存地址的變量。它將名字與內存位置關聯起來,這種關聯由編譯器實現而非硬件。硬件始終通過地址訪問內存,指針變量名只是為程序員提供了一種記住地址的便捷方式。

指針的聲明與初始化陷阱

聲明指針並不會分配目標類型的存儲空間,只會為指針本身分配空間。未初始化的指針指向隨機位置,對其解引用會導致不可預測的行為:

int *a;
*a = 12; // 極度危險!a 未初始化,寫入隨機地址

正確做法是先初始化指針,使其指向確定的內存:

int b, *a;
a = &b;      // 初始化指針指向 b 的地址
*a = 12;     // 安全:將 12 存入 b 的內存位置

NULL 指針與安全檢查

NULL 指針表示不指向任何有效對象。將指針賦值為 0 或 NULL 宏可實現此狀態。對 NULL 指針解引用的行為因編譯器而異:可能訪問地址 0 導致錯誤數據,或觸發運行時錯誤終止程序。編譯器負責將 0 與機器內部 NULL 表示之間轉換。

多級指針與指針運算

二級指針(指向指針的指針)常用於修改一級指針的值(如函數參數傳遞指針地址)。指針運算遵循類型大小:ptr + 1 移動 sizeof(*ptr) 字節。間接訪問操作符 * 產生左值,允許修改目標內存。

指針表達式的左值/右值特性至關重要:

  • *ptr 是左值(可修改目標內存)
  • ptr + 1 是右值(臨時計算結果,不可取地址)
  • *(ptr + 1) 是左值(解引用後定位到具體內存)

指針數組副作用與修正

函數參數接收指針數組(實為二級指針)時,若在函數內修改二級指針(如 (*strings)++),會改變主調函數中數組元素的指針值,導致無法重複調用。解決方法是使用臨時變量遍歷:

int find_char(char **strings, int value) {
    char *str;
    while ((str = *strings++) != NULL) {
        while (*str != '\0') {
            if (*str++ == value) return 1;
        }
    }
    return 0;
}

常量數組與指針數組的區別

字符串常量 "abc" 代表地址,可直接初始化指針數組:char *p[] = {"a", "b"};。但整型常量數組 {1,2,3} 不代表地址,不能直接初始化指針數組,需先定義數組再取地址:int a1[] = {1,2}; int *p[] = {a1};。

二維數組與指向數組的指針

二維數組名是指向一維數組的指針(int (*p)[COLS]),而非二級指針。訪問元素有多種等價形式:

a[i][j] == *(*(a + i) + j) == (*(a + i))[j] == *(a[i] + j)

指向數組的指針移動單位由聲明決定:int (*p)[3] 以 3 個 int 為單位移動。

高效數組遍歷技巧

使用指針而非下標可消除循環中的乘法運算:

for (p = arr; p < &arr[SIZE];) *p++ = 0;
// 編譯器優化:p++ 內部乘法在編譯期完成

標準允許指針比較 p < &arr[SIZE](指向尾後位置),但禁止 p >= &arr[0](指向首前位置),為保證可移植性應避免後者。

第五章 函數與可變參數

函數原型與舊式定義

K&R 風格定義無參數類型檢查,現代 C 應始終使用原型。無參函數原型應寫作 int func(void);。調用無原型函數時編譯器假定返回 int,若實際返回其他類型將導致錯誤。

可變參數實現

使用 <stdarg.h> 宏處理可變參數:

  1. va_list ap; 定義參數指針
  2. va_start(ap, last_named); 初始化
  3. va_arg(ap, type); 依次獲取參數(注意默認類型提升:float→double, char/short→int)
  4. va_end(ap); 清理
int sum(int first, ...) {
    va_list ap; va_start(ap, first);
    int s = 0, v = first;
    while (v != -1) { s += v; v = va_arg(ap, int); }
    va_end(ap); return s;
}

數據封裝與靜態內部結構

通過 static 限制文件作用域,僅暴露接口函數(如 lookup_address),隱藏內部數組結構,實現類似面向對象的封裝。

第六章 數組與指針深度解析

數組名本質

數組名在表達式中轉換為指向首元素的常量指針(int *const)。例外:作為 sizeof 或 & 操作數時代表整個數組。sizeof(arr) 返回數組總字節數,而 sizeof(ptr) 返回指針大小。

下標與指針等價性

arr[i] 完全等價於 *(arr + i),甚至 i[arr] 也合法(因加法交換律)。指針遍歷效率高於下標:下標需每次乘法,指針增量編譯期確定。

函數參數傳遞數組

傳遞數組實為傳遞首元素地址(指針拷貝)。形參聲明 int func(int *a) 和 int func(int a[]) 等價,但本質是指針。函數內 sizeof(a) 返回指針大小,無法獲取數組長度,需額外傳遞長度參數。

多維數組參數

二維數組參數必須指定除第一維外的所有維度:void func(int a[][10]) 或 void func(int (*a)[10])。絕不可用 int **a,因二維數組在內存中連續,而二級指針指向指針數組。

第七章 字符串與內存操作

字符串表示與常見函數

C 無字符串類型,字符串以字符數組存儲並以 \0 結尾。strlen 返回 size_t(無符號),減法比較需當心下溢。strcpy/strcat 要求目標可修改且空間足夠,否則緩衝區溢出。

長度受限函數

strncpy 固定寫入 n 字符,不足補 \0,超長不加 \0(需手動添加)。strncat 最多追加 n 字符並保證以 \0 結尾。

查找與分割函數

  • strchr/strrchr:查找字符位置
  • strpbrk:查找集合中任一字符
  • strstr:查找子串(無標準 strrstr,文中提供實現)
  • strspn/strcspn:計算前綴匹配/不匹配長度
  • strtok:分割字符串(非線程安全,修改原串)

內存操作函數

memcpy/memmove/memcmp/memchr/memset 處理任意字節(含 \0),適用於結構體、整型數組等非字符串數據。memmove 處理重疊內存安全,memcpy 更快但不保證重疊安全。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved