详细解释各种C语言指针

1. 指针的基本分类体系

按指向的数据类型分类:

// 1. 基本类型指针
int *int_ptr;
char *char_ptr;
float *float_ptr;
double *double_ptr;

// 2. 结构体指针
struct student *stu_ptr;

// 3. 联合体指针
union data *union_ptr;

// 4. 枚举指针
enum color *enum_ptr;

// 5. 函数指针
int (*func_ptr)(int, int);

// 6. 空类型指针
void *void_ptr;

2. 按指针的层级分类

单级指针

int a = 10;
int *p = &a;        // p是指向int的指针

多级指针

int a = 10;
int *p = &a;        // 一级指针
int **pp = &p;      // 二级指针(指向指针的指针)
int ***ppp = &pp;   // 三级指针

3. 按指针的用途和特性分类

3.1 数组指针 vs 指针数组

数组指针 - 指向整个数组的指针:

int arr[5] = {1, 2, 3, 4, 5};
int (*ptr_to_array)[5] = &arr;  // 指向包含5个int的数组

// 访问方式
printf("%d\n", (*ptr_to_array)[2]);  // 输出: 3

指针数组 - 元素都是指针的数组:

int a = 1, b = 2, c = 3;
int *ptr_array[3] = {&a, &b, &c};  // 包含3个int指针的数组

// 访问方式
printf("%d\n", *ptr_array[1]);  // 输出: 2

3.2 函数指针

// 函数原型
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }

// 函数指针声明和使用
int (*operation)(int, int);  // 声明函数指针

operation = add;            // 指向add函数
printf("%d\n", operation(3, 4));  // 输出: 7

operation = multiply;       // 指向multiply函数  
printf("%d\n", operation(3, 4));  // 输出: 12

3.3 常量指针 vs 指针常量

常量指针 - 指向常量的指针(指针可改,值不可改):

const int *p1;           // 指向const int的指针
int const *p2;           // 同上,等价写法

int a = 10, b = 20;
const int *p = &a;
// *p = 30;              // 错误:不能通过p修改a的值
p = &b;                  // 正确:可以改变指针的指向

指针常量 - 指针本身是常量(指针不可改,值可改):

int *const p;            // 指向int的const指针

int a = 10, b = 20;
int *const p = &a;
*p = 30;                 // 正确:可以修改a的值
// p = &b;               // 错误:不能改变指针的指向

指向常量的指针常量 - 都不能改:

const int *const p;      // 指向const int的const指针

int a = 10;
const int *const p = &a;
// *p = 20;              // 错误:不能修改值
// p = &b;               // 错误:不能改变指向

4. 理解指针的语法规律

4.1 "右左法则" - 解析复杂指针声明的秘诀

规则:从变量名开始: 有括号先看括号; 没括号就 先向右看,再向左看。

示例1int *p[5]

p        → 变量名p
p[5]     → 向右:p是包含5个元素的数组  
*p[5]    → 向左:数组的元素是指针
int *p[5] → 向左:指针指向int类型

结果:p是包含5个int指针的数组(指针数组)

示例2int (*p)[5]

(*p)     → 变量名p,被括号包围,先看括号内:p是指针
(*p)[5]  → 向右:指针指向包含5个元素的数组
int (*p)[5] → 向左:数组的元素是int类型

结果:p是指向包含5个int的数组的指针(数组指针)

示例3int *(*p[5])(int)

p                 → 变量名p
p[5]              → p是包含5个元素的数组
*p[5]             → 数组的元素是指针
(*p[5])(int)      → 向右:指针指向函数,函数接受int参数
*(*p[5])(int)     → 向左:函数返回指针
int *(*p[5])(int) → 向左:指针指向int类型

结果:p是包含5个函数指针的数组,这些函数接受int参数并返回int指针

4.2 星号(*)的位置规律

星号的位置不影响含义,只是风格差异:

int* p;    // 风格1:紧贴类型(强调p是int指针类型)
int * p;   // 风格2:在中间
int *p;    // 风格3:紧贴变量(强调*p是int类型)

5. 特殊指针类型详解

5.1 void指针(通用指针)

void *generic_ptr;
int a = 10;
char c = 'A';

generic_ptr = &a;        // 可以指向任何类型
generic_ptr = &c;

// 使用时必须进行类型转换
int *int_ptr = (int*)generic_ptr;
char *char_ptr = (char*)generic_ptr;

5.2 空指针和野指针

int *null_ptr = NULL;    // 空指针,明确表示不指向任何地方
int *wild_ptr;           // 野指针,未初始化,指向随机地址(危险!)

// 正确做法:总是初始化指针
int *safe_ptr = NULL;

5.3 近指针、远指针、巨指针(历史概念)

// 在16位DOS时代的内存模型
char near *near_ptr;     // 近指针(同一段内)
char far *far_ptr;       // 远指针(不同段)
char huge *huge_ptr;     // 巨指针(可跨段)
// 现代32/64位系统中已不再需要

6. 指针运算的规律

6.1 算术运算

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;

p + 1;      // 指向arr[1],实际地址增加 sizeof(int)
p + 2;      // 指向arr[2],实际地址增加 2 * sizeof(int)
p++;        // p现在指向arr[1]
p--;        // p现在指向arr[0]

6.2 关系运算

int *p1 = &arr[0];
int *p2 = &arr[2];

if (p1 < p2)  // true,比较内存地址
if (p1 == p2) // false

7. 实际应用模式

7.1 动态内存分配

int *dynamic_array = (int*)malloc(10 * sizeof(int));
if (dynamic_array != NULL) {
    for (int i = 0; i < 10; i++) {
        dynamic_array[i] = i * 10;
    }
    free(dynamic_array);  // 必须释放
}

7.2 字符串处理

char str[] = "Hello";
char *p = str;

while (*p != '\0') {
    printf("%c", *p);
    p++;
}

7.3 结构体访问

typedef struct {
    int id;
    char name[20];
    float score;
} Student;

Student s = {1, "Alice", 95.5};
Student *ptr = &s;

// 两种访问方式
(*ptr).id = 2;    // 传统方式
ptr->id = 3;      // 箭头语法(推荐)

8. 记忆口诀和技巧

8.1 指针声明口诀

"有括号先处理括号, 先右后左"

8.2 常量指针记忆法

const int *p;    // "const在左,值不变"(指向常量的指针)
int *const p;    // "const在右,针不变"(指针常量)
const int *const p;  // "两边const,都不变"

8.3 数组vs指针记忆

int *p[5];   // "p[5]先出现,是指针数组"
int (*p)[5]; // "(*p)先出现,是数组指针"

9. 综合练习

让我们解析一个复杂声明:

char *(*(*func_array[5])(int))[10];

使用右左法则:

func_array          → 变量名
func_array[5]       → 5个元素的数组
*func_array[5]      → 数组元素是指针
(*func_array[5])(int) → 指针指向函数,函数接受int参数
*(*func_array[5])(int) → 函数返回指针
(*(*func_array[5])(int))[10] → 指针指向10个元素的数组
char *(*(*func_array[5])(int))[10] → 数组元素是char指针

结果:func_array是包含5个函数指针的数组,这些函数接受int参数, 返回指向包含10个char指针的数组的指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值