指針
一、指針基礎(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)鍵理解:
*p和i訪問的是同一塊內(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 安全指針使用檢查清單
- ? 指針是否已初始化?
- ? 指針是否指向有效內(nèi)存?
- ? 是否檢查了空指針?
- ? 數(shù)組訪問是否越界?
- ? 指向的內(nèi)存生命周期是否有效?
- ? 是否需要使用 const 保護(hù)數(shù)據(jù)?
掌握這些原則,你就能安全有效地使用C語言指針這個(gè)強(qiáng)大工具!