C語言學(xué)習(xí)2-指針的使用、const 指針

指針

一、指針基礎(chǔ)概念

1.1 什么是指針?

指針是一種特殊變量,用于存儲內(nèi)存地址。

1.2 為什么需要專門的指針類型?

雖然地址本質(zhì)上是整數(shù),但使用特定類型可以:

  • 明確指向的數(shù)據(jù)類型
  • 支持指針?biāo)阈g(shù)運(yùn)算
  • 幫助編譯器進(jìn)行類型檢查

1.3 基本指針類型

char*           // char 類型變量的地址
short*          // short 類型變量的地址  
int*            // int 類型變量的地址
float*          // float 類型變量的地址
double*         // double 類型變量的地址
unsigned char*  // unsigned char 類型變量的地址
XXX*            // 任意 XXX 類型變量的地址

二、指針的基本操作

2.1 指針的定義與使用

#include <stdio.h>

int main() {
    int *p;        // 定義指針變量 p
    int i = 3;     // 定義整型變量 i
    
    p = &i;        // p 保存 i 的地址(p 指向 i)
    
    int j;
    j = *p;        // 通過 *p 訪問 i 的值
    
    printf("i = %d, j = %d, *p = %d\n", i, j, *p);
    return 0;
}

2.2 理解指針語法

  • int *p; - p 是指針變量名
  • int* - 數(shù)據(jù)類型,表示 p 存放 int 變量的地址
  • &i - 取地址運(yùn)算符,獲取 i 的內(nèi)存地址
  • *p - 解引用運(yùn)算符,訪問 p 指向的內(nèi)存

關(guān)鍵理解*pi 訪問的是同一塊內(nèi)存,可以互相替換

2.3 指針聲明風(fēng)格

以下三種寫法完全等價(jià):

int* p;    // 風(fēng)格1:強(qiáng)調(diào) p 是 int* 類型
int * p;   // 風(fēng)格2:空格分隔  
int *p;    // 風(fēng)格3:強(qiáng)調(diào) *p 是 int 類型

三、指針的重要規(guī)則

3.1 類型安全規(guī)則

int a = 10;
int* pa = &a; 

// ? 錯(cuò)誤:不同類型指針不能互相賦值
double* pd = pa;     // 錯(cuò)誤!int* → double*
char c = 'A';
float* pf = &c;      // 錯(cuò)誤!char* → float*

原因:不同類型的數(shù)據(jù)在內(nèi)存中的表示方式和大小不同。

3.2 星號操作:通過地址訪問內(nèi)存

int a = 0x123;
int *p = &a;        // p 指向 a 的內(nèi)存

*p += 2;            // 修改 p 指向的內(nèi)存:a = 0x125

int b = *p;         // 讀取 p 指向的值:b = 0x125
int c = *p + 2;     // 使用指針值運(yùn)算:c = 0x127

四、指針作為函數(shù)參數(shù)

4.1 基本用法

#include <stdio.h>

void modify_value(int *p) {
    *p = 10;  // 修改指針指向的值
}

int main() {
    int a = 0;
    modify_value(&a);
    printf("a = %d\n", a);  // 輸出:a = 10
    return 0;
}

4.2 實(shí)際應(yīng)用:交換兩個(gè)變量

#include <stdio.h>

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    printf("交換前: x=%d, y=%d\n", x, y);
    
    swap(&x, &y);
    printf("交換后: x=%d, y=%d\n", x, y);
    
    return 0;
}

4.3 指針參數(shù)的優(yōu)點(diǎn)

  • ? 讀取上一層函數(shù)的變量值
  • ? 修改上一層函數(shù)的變量值
  • ? 實(shí)現(xiàn)函數(shù)返回多個(gè)值
  • ? 高效傳遞大型數(shù)據(jù)結(jié)構(gòu)

五、指針與數(shù)組

5.1 數(shù)組名即指針

#include <stdio.h>

// 兩種完全等價(jià)的函數(shù)聲明
int sum_array(int *arr, int length) {
// int sum_array(int arr[], int length) {  // 等價(jià)寫法
    int sum = 0;
    for(int i = 0; i < length; i++) {
        sum += arr[i];  // arr[i] 等價(jià)于 *(arr + i)
    }
    return sum;
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    
    // 不同的調(diào)用方式
    int total1 = sum_array(numbers, 5);       // 整個(gè)數(shù)組
    int total2 = sum_array(numbers, 3);       // 前3個(gè)元素
    int total3 = sum_array(numbers + 1, 3);   // 從第2個(gè)元素開始
    
    printf("結(jié)果: %d, %d, %d\n", total1, total2, total3);
    return 0;
}

重要原則:傳遞數(shù)組時(shí),必須同時(shí)傳遞長度信息!

六、const 指針

6.1 const 指針的基本概念

// 1. 普通指針:可讀可寫
int a = 10;
int* p = &a;
*p = 20;        // ? 可以修改
int b = *p;     // ? 可以讀取

// 2. const 指針:只讀
int a = 10;
const int* p = &a;
// *p = 20;     // ? 錯(cuò)誤:不能修改
int b = *p;     // ? 可以讀取

6.2 const 指針的實(shí)際應(yīng)用

#include <stdio.h>

// 使用 const 保護(hù)輸入數(shù)據(jù)不被修改
int calculate_sum(const int* arr, int length) {
    int sum = 0;
    for(int i = 0; i < length; i++) {
        sum += arr[i];     // ? 可以讀取
        // arr[i] = 0;    // ? 錯(cuò)誤:不能修改
    }
    return sum;
}

void process_data(const int* input, int* output, int length) {
    for(int i = 0; i < length; i++) {
        output[i] = input[i] * 2;  // ? 讀取input,寫入output
    }
}

int main() {
    int data[] = {1, 2, 3, 4, 5};
    int result[5];
    
    int sum = calculate_sum(data, 5);
    process_data(data, result, 5);
    
    printf("總和: %d\n", sum);
    return 0;
}

6.3 const 使用場景總結(jié)

  • 函數(shù)參數(shù):表明該參數(shù)是輸入?yún)?shù),函數(shù)內(nèi)不會修改
  • 保護(hù)數(shù)據(jù):防止意外修改重要數(shù)據(jù)
  • 提高代碼可讀性:明確標(biāo)識數(shù)據(jù)的只讀屬性

注意:const 只限制通過指針修改內(nèi)存,不影響指針本身的算術(shù)運(yùn)算。

七、指針的安全使用

7.1 空指針(Null Pointer)

int *p = NULL;        // 或 int *p = 0;

// ? 危險(xiǎn):解引用空指針
// printf("%d\n", *p); 

// ? 安全做法:使用前檢查
if (p != NULL) {
    printf("%d\n", *p);
}

7.2 空指針的實(shí)際應(yīng)用

#include <stdio.h>

void find_values(const int* arr, int length, 
                 int* pmin, int* pmax) {
    if (length == 0) return;
    
    int min = arr[0], max = arr[0];
    for (int i = 1; i < length; i++) {
        if (arr[i] < min) min = arr[i];
        if (arr[i] > max) max = arr[i];
    }
    
    // 安全賦值:只在指針非空時(shí)寫入
    if (pmin) *pmin = min;
    if (pmax) *pmax = max;
}

int main() {
    int data[] = {5, 2, 8, 1, 9};
    int min_val, max_val;
    
    // 獲取最小值和最大值
    find_values(data, 5, &min_val, &max_val);
    printf("最小值: %d, 最大值: %d\n", min_val, max_val);
    
    // 只獲取最小值
    find_values(data, 5, &min_val, NULL);
    printf("最小值: %d\n", min_val);
    
    return 0;
}

7.3 野指針(Wild Pointer)

int *p;            // ? 危險(xiǎn):未初始化的指針(野指針)
// printf("%d", *p); // 指向隨機(jī)內(nèi)存,可能導(dǎo)致崩潰

// ? 好習(xí)慣:始終初始化指針
int *safe_ptr = NULL;

7.4 數(shù)組越界

int arr[4] = {1, 2, 3, 4};
int *p = arr;

// p += 4;        // ? 越界:指向數(shù)組后的位置
// *p = 12;       // 修改未知內(nèi)存,極其危險(xiǎn)!

// ? 安全做法:檢查邊界
if (p >= arr && p < arr + 4) {
    *p = 12;
}

7.5 懸掛指針(Dangling Pointer)

#include <stdio.h>

int main() {
    int *p = NULL;
    
    {   // 代碼塊開始
        int a = 10;    // a 的生命期開始
        p = &a;        // p 指向 a
    }   // 代碼塊結(jié)束,a 的生命期結(jié)束
    
    // *p = 11;       // ? 危險(xiǎn):p 指向已失效的內(nèi)存
    return 0;
}

八、安全使用指針的黃金法則

8.1 初始化規(guī)則

int *p = NULL;           // ? 初始化為空
int *q = &valid_var;     // ? 初始化為有效地址

8.2 使用前檢查

void safe_function(int *ptr) {
    if (ptr == NULL) {
        printf("錯(cuò)誤:空指針\n");
        return;
    }
    // 安全使用指針
    *ptr = 100;
}

8.3 生命周期管理

// ? 安全:指向全局變量
int global_var;
int *safe_ptr = &global_var;

// ? 安全:指向動(dòng)態(tài)分配內(nèi)存
int *dynamic_ptr = malloc(sizeof(int));

// ? 危險(xiǎn):指向即將失效的局部變量
int* dangerous_function() {
    int local = 10;
    return &local;  // 錯(cuò)誤!
}

九、總結(jié)

9.1 指針的核心價(jià)值

  • 多返回值:通過指針參數(shù)返回多個(gè)值
  • 高效傳參:傳遞地址比傳遞大對象更高效
  • 動(dòng)態(tài)內(nèi)存:實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu)和內(nèi)存管理
  • 硬件訪問:直接操作內(nèi)存映射的硬件寄存器

9.2 安全指針使用檢查清單

  1. ? 指針是否已初始化?
  2. ? 指針是否指向有效內(nèi)存?
  3. ? 是否檢查了空指針?
  4. ? 數(shù)組訪問是否越界?
  5. ? 指向的內(nèi)存生命周期是否有效?
  6. ? 是否需要使用 const 保護(hù)數(shù)據(jù)?

掌握這些原則,你就能安全有效地使用C語言指針這個(gè)強(qiáng)大工具!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容