C語(yǔ)言學(xué)習(xí)1-數(shù)組、字符串
C語(yǔ)言學(xué)習(xí)2-指針的使用、const 指針
C語(yǔ)言學(xué)習(xí)3-指針和數(shù)組
C語(yǔ)言學(xué)習(xí)4-結(jié)構(gòu)體
C語(yǔ)言學(xué)習(xí)5-函數(shù)
一、指針基礎(chǔ):理解地址與數(shù)據(jù)
1.1 什么是指針?
指針 = 內(nèi)存地址,指針變量是存儲(chǔ)內(nèi)存地址的變量。
#include <stdio.h>
int main() {
int x = 5; // 在內(nèi)存中分配一個(gè)整數(shù)空間
int *px = &x; // px存儲(chǔ)x的內(nèi)存地址
printf("=== 基礎(chǔ)指針演示 ===\n");
printf("x的值: %d\n", x); // 5
printf("x的地址: %p\n", &x); // 0x7ff7bfeff374
printf("px的值: %p\n", px); // 0x7ff7bfeff374
printf("px的地址: %p\n", &px); // 0x7ff7bfeff368
printf("*px的值: %d\n", *px); // 5
return 0;
}
1.2 內(nèi)存布局示意圖
內(nèi)存地址布局圖:
+-------------------+ 指向 +-------------------+
| 指針變量 px | -----------> | 整型變量 x |
| 地址: 0x368 | | 地址: 0x374 |
| 值: 0x374 | | 值: 5 |
+-------------------+ +-------------------+
訪問(wèn)路徑說(shuō)明:
- &x → 獲取x的地址:0x374
- px → px存儲(chǔ)的值:0x374
- *px → 訪問(wèn)地址0x374的內(nèi)容:5
圖解說(shuō)明:
- 每個(gè)變量都在內(nèi)存中有自己的地址
- 指針變量存儲(chǔ)的是其他變量的地址
- 通過(guò)
*指針可以訪問(wèn)指針指向的數(shù)據(jù)
二、通過(guò)指針修改內(nèi)存數(shù)據(jù)
2.1 修改指針指向的值
#include <stdio.h>
int main() {
int x = 5;
int *px = &x;
printf("修改前: x = %d, *px = %d\n", x, *px); // 都是5
*px = 10; // 通過(guò)指針修改x的值
printf("修改后: x = %d, *px = %d\n", x, *px); // 都是10
return 0;
}
2.2 修改過(guò)程的內(nèi)存變化圖
修改前內(nèi)存狀態(tài):
+-------------------+ 指向 +-------------------+
| 指針 px | -----------> | 變量 x |
| 地址: 0x368 | | 地址: 0x374 |
| 值: 0x374 | | 值: 5 |
+-------------------+ +-------------------+
執(zhí)行 *px = 10 后:
+-------------------+ 指向 +-------------------+
| 指針 px | -----------> | 變量 x |
| 地址: 0x368 | | 地址: 0x374 |
| 值: 0x374 | | 值: 10 ← 被修改! |
+-------------------+ +-------------------+
關(guān)鍵理解:
-
px存儲(chǔ)的是地址,這個(gè)地址不變 -
*px操作的是該地址指向的內(nèi)存內(nèi)容 - 修改
*px就是修改原始變量x的值
三、多級(jí)指針(指針的指針)
3.1 二級(jí)指針詳解
#include <stdio.h>
int main() {
int x = 5;
int *px = &x; // 一級(jí)指針
int **pxx = &px; // 二級(jí)指針
printf("x = %d\n", x); // 5
printf("px = %p\n", px); // x的地址
printf("*px = %d\n", *px); // 5
printf("pxx = %p\n", pxx); // px的地址
printf("*pxx = %p\n", *pxx); // x的地址
printf("**pxx = %d\n", **pxx); // 5
return 0;
}
3.2 多級(jí)指針內(nèi)存關(guān)系圖
三級(jí)指針關(guān)系圖:
+----------------+ 指向 +----------------+ 指向 +----------------+
| 三級(jí)指針 pxx | --------> | 二級(jí)指針 px | --------> | 一級(jí)變量 x |
| 地址: 0x360 | | 地址: 0x368 | | 地址: 0x374 |
| 值: 0x368 | | 值: 0x374 | | 值: 5 |
+----------------+ +----------------+ +----------------+
訪問(wèn)路徑分解:
pxx = 0x368 (px的地址)
*pxx = 0x374 (px的值,即x的地址)
**pxx = 5 (x的值)
&pxx = 0x360 (pxx自己的地址)
&px = 0x368 (px自己的地址)
&x = 0x374 (x自己的地址)
3.3 通過(guò)多級(jí)指針修改變量
#include <stdio.h>
int main() {
int x = 5;
int *px = &x;
int **pxx = &px;
printf("初始: x = %d\n", x); // 5
// 三種方式修改同一個(gè)變量
x = 10;
printf("直接修改: x = %d\n", x); // 10
*px = 20;
printf("通過(guò)px: x = %d\n", x); // 20
**pxx = 30;
printf("通過(guò)pxx: x = %d\n", x); // 30
return 0;
}
修改過(guò)程圖解:
所有修改都作用于同一內(nèi)存位置:
+----------------+ 指向 +----------------+ 指向 +----------------+
| pxx | --------> | px | --------> | x |
| 值: 0x368 | | 值: 0x374 | | 值: 30 ← 最終值 |
+----------------+ +----------------+ +----------------+
↑ ↑ ↑
| | |
**pxx=30 *px=20 x=10
(第三層修改) (第二層修改) (第一層修改)
四、指針的大小與系統(tǒng)架構(gòu)
4.1 指針大小測(cè)試
#include <stdio.h>
int main() {
printf("=== 指針大小測(cè)試 ===\n");
printf("char* 大小: %zu 字節(jié)\n", sizeof(char*));
printf("int* 大小: %zu 字節(jié)\n", sizeof(int*));
printf("double* 大小: %zu 字節(jié)\n", sizeof(double*));
printf("void* 大小: %zu 字節(jié)\n", sizeof(void*));
printf("int** 大小: %zu 字節(jié)\n", sizeof(int**));
return 0;
}
4.2 指針大小示意圖
32位系統(tǒng)內(nèi)存布局:
+------------------------+
| 指針變量 (4字節(jié)) | → 所有指針都是4字節(jié)
| 存儲(chǔ)32位地址 (0-4GB) |
+------------------------+
64位系統(tǒng)內(nèi)存布局:
+------------------------+
| 指針變量 (8字節(jié)) | → 所有指針都是8字節(jié)
| 存儲(chǔ)64位地址 |
+------------------------+
關(guān)鍵結(jié)論:
- 指針大小只取決于系統(tǒng)架構(gòu)
- 與指向的數(shù)據(jù)類型無(wú)關(guān)
- char*、int*、double* 大小都相同
五、函數(shù)參數(shù)中的指針
5.1 指針作為函數(shù)參數(shù)
#include <stdio.h>
// 參數(shù)中的 * 表示需要傳遞地址
void modifyThroughPointer(int *location, int newValue) {
*location = newValue; // 修改指針指向的內(nèi)存
}
void demonstrateFunctionPointers() {
int value = 100;
printf("調(diào)用前: value = %d\n", value); // 100
modifyThroughPointer(&value, 200); // 傳遞地址
printf("調(diào)用后: value = %d\n", value); // 200
}
int main() {
demonstrateFunctionPointers();
return 0;
}
5.2 函數(shù)調(diào)用過(guò)程圖解
函數(shù)調(diào)用前:
main函數(shù)棧幀:
+-------------------+
| 變量 value = 100 |
| 地址: 0x1234 |
+-------------------+
調(diào)用 modifyThroughPointer(&value, 200):
參數(shù)傳遞:
+-------------------+ +------------------------+
| &value = 0x1234 | ---> | location = 0x1234 |
| newValue = 200 | | newValue = 200 |
+-------------------+ +------------------------+
| *location = 200 | ← 修改0x1234處的值
+------------------------+
函數(shù)返回后:
main函數(shù)棧幀:
+-------------------+
| 變量 value = 200 | ← 值被修改!
| 地址: 0x1234 |
+-------------------+
5.3 指針參數(shù)的常見(jiàn)使用場(chǎng)景
#include <stdio.h>
// 場(chǎng)景1:返回多個(gè)值
void getMinMax(int a, int b, int *min, int *max) {
*min = (a < b) ? a : b;
*max = (a > b) ? a : b;
}
// 場(chǎng)景2:交換兩個(gè)變量
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void demonstratePracticalUsage() {
// 返回多個(gè)值
int x = 25, y = 40;
int min, max;
getMinMax(x, y, &min, &max);
printf("min=%d, max=%d\n", min, max);
// 交換變量
printf("交換前: x=%d, y=%d\n", x, y);
swap(&x, &y);
printf("交換后: x=%d, y=%d\n", x, y);
}
交換過(guò)程圖解:
交換前:
+-----------+ +-----------+
| x = 25 | | y = 40 |
| 地址:0x100| | 地址:0x104|
+-----------+ +-----------+
調(diào)用 swap(&x, &y):
+-----------+ +-----------+
| a → 0x100 | | b → 0x104 |
| temp = 25 | | |
+-----------+ +-----------+
執(zhí)行交換:
*a = *b → 0x100處寫(xiě)入40
*b = temp → 0x104處寫(xiě)入25
交換后:
+-----------+ +-----------+
| x = 40 | | y = 25 |
| 地址:0x100| | 地址:0x104|
+-----------+ +-----------+
六、綜合調(diào)試示例
6.1 完整的指針鏈分析
#include <stdio.h>
void debugPointerChain() {
int data = 42;
int *ptr1 = &data;
int **ptr2 = &ptr1;
int ***ptr3 = &ptr2;
printf("=== 完整指針鏈分析 ===\n");
printf("data: %d (地址:%p)\n", data, &data);
printf("ptr1: %p (地址:%p) → *ptr1=%d\n", ptr1, &ptr1, *ptr1);
printf("ptr2: %p (地址:%p) → **ptr2=%d\n", ptr2, &ptr2, **ptr2);
printf("ptr3: %p (地址:%p) → ***ptr3=%d\n", ptr3, &ptr3, ***ptr3);
}
int main() {
debugPointerChain();
return 0;
}
6.2 指針鏈內(nèi)存布局總圖
完整指針鏈內(nèi)存布局:
+-------------+ 指向 +-------------+ 指向 +-------------+ 指向 +-------------+
| ptr3 | --------> | ptr2 | --------> | ptr1 | --------> | data |
| 地址:0x360 | | 地址:0x368 | | 地址:0x370 | | 地址:0x378 |
| 值:0x368 | | 值:0x370 | | 值:0x378 | | 值:42 |
+-------------+ +-------------+ +-------------+ +-------------+
訪問(wèn)路徑總結(jié):
ptr3 = 0x368 (ptr2的地址)
*ptr3 = 0x370 (ptr2的值 = ptr1的地址)
**ptr3 = 0x378 (ptr1的值 = data的地址)
***ptr3 = 42 (data的值)
&ptr3 = 0x360
&ptr2 = 0x368
&ptr1 = 0x370
&data = 0x378
七、核心概念總結(jié)
7.1 指針操作符圖解
操作符作用示意圖:
+-------------+ &操作符 +-------------+
| 變量 x | <----------- | &x |
| 值: 5 | 獲取地址 | 值: 0x374 |
| 地址: 0x374 | +-------------+
+-------------+
|
| *操作符
| 解引用
v
+-------------+
| *px |
| 值: 5 |
+-------------+
7.2 指針使用黃金法則
-
聲明語(yǔ)法:
數(shù)據(jù)類型 *指針名 - 賦值規(guī)則:必須賦值為有效地址
-
訪問(wèn)數(shù)據(jù):通過(guò)
*指針訪問(wèn) -
修改數(shù)據(jù):通過(guò)
*指針 = 值修改 - 安全使用:始終初始化,釋放后置NULL
7.3 最佳實(shí)踐代碼模板
// ? 安全指針使用模板
void safePointerUsage() {
// 1. 聲明并初始化為NULL
int *ptr = NULL;
// 2. 分配內(nèi)存并檢查
ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
printf("內(nèi)存分配失敗\n");
return;
}
// 3. 使用指針
*ptr = 100;
printf("值: %d\n", *ptr);
// 4. 釋放并置空
free(ptr);
ptr = NULL; // 防止懸空指針
}
// ? 函數(shù)參數(shù)明確意圖
void goodFunction(const int *input, // 輸入?yún)?shù)(只讀)
int *output) { // 輸出參數(shù)(可寫(xiě))
// input指向的數(shù)據(jù)不會(huì)被修改
*output = *input * 2; // 修改output指向的數(shù)據(jù)
}