详解C语言指针(下):指针解引用,二级指针(结合近些年笔/面试的实际例子)
🔥个人主页:艾莉丝努力练剑
❄专栏传送门:《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题、C/C++干货分享&学习过程记录
🍉学习方向:C/C++方向
⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平
C++的两个参考文档:
老朋友(非官方文档):cplusplus
官方文档(同步更新):cppreference
一、指针解引用的本质指针解引用(*操作)实际上是通过地址访问内存的过程。每次解引用都相当于一次"寻址-取值"操作。记住这个核心原则:
解引用前必须确保指针指向有效的内存区域
大厂常见坑点案例这里我们以腾讯2023面试题为例:
代码语言:javascript复制int *p = NULL;
*p = 10; 问:会发生什么?
答案:触发段错误(Segmentation Fault),因为试图访问0地址的内存。
实际工程项目中指针解引用的正确用法:
代码语言:javascript复制int value = 100;
int *p = &value; // 先让指针指向有效地址
printf("%d", *p); // 安全解引用二、二级指针的实战理解 二级指针就是“指向指针的指针”,在以下场景特别有用——
1、需要修改指针本身时;
2、处理指针数组时;
3、动态分配二维数组时。
这里展示一个阿里巴巴高频面试案例:
代码语言:javascript复制void allocate_memory(char **ptr, int size)
{
*ptr = (char*)malloc(size); // 通过二级指针修改外部指针
}
int main()
{
char *str = NULL;
allocate_memory(&str, 100); // 传入指针的地址
strcpy(str, "Alibaba");
free(str);
}二级指针与二维数组下面这个字节跳动2024年的一道笔试真题:
代码语言:javascript复制int arr[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int (*p)[4] = arr; // 数组指针
int **pp = (int**)arr; // 这种转换是否正确?
printf("%d", arr[1][2]); // 正确
printf("%d", *(*(arr+1)+2)); // 正确
printf("%d", pp[1][2]); // 可能崩溃! 关键点:注意二维数组不是二级指针!它们的内存布局完全不同。
三、指针解引用案例1、结构体指针解引用下面这道题是美团的一个真实工程案例:
代码语言:javascript复制typedef struct
{
int id;
char name[20];
} Employee;
Employee e = {1001, "张三"};
Employee *p = &e;
// 两种访问方式对比
printf("%s", (*p).name); // 传统方式
printf("%s", p->name); // 更推荐的箭头语法2、指针解引用与类型转换下面这道是华为安全部门的一道考题:
代码语言:javascript复制float f = 3.14;
int *p = (int*)&f;
printf("%d", *p); // 输出什么? 答案:输出浮点数的二进制表示(IEEE 754编码)的整数值。
四、二级指针经典应用1、动态字符串数组下面是腾讯云面试实战的一道题:
代码语言:javascript复制char **create_string_array(int count)
{
char **arr = (char**)malloc(count * sizeof(char*));
for(int i=0; i { arr[i] = (char*)malloc(50 * sizeof(char)); sprintf(arr[i], "String%d", i+1); } return arr; } // 释放时也要二级指针操作 void free_string_array(char **arr, int count) { for(int i=0; i { free(arr[i]); } free(arr); }2、修改外部指针下面是百度搜索部门的考题: 代码语言:javascript复制void change_pointer(int **pptr) { static int value = 100; *pptr = &value; // 修改外部指针的指向 } int main() { int *ptr = NULL; change_pointer(&ptr); printf("%d", *ptr); // 输出100 }五、指针操作技巧掌握要领1、指针偏移解引用下面是拼多多2023年的一道笔试题: 代码语言:javascript复制int arr[] = {10,20,30,40,50}; int *p = arr; printf("%d", *(p + 2)); // 30 printf("%d", p[2]); // 30 (等价写法) printf("%d", 2[p]); // 30 (这种写法也是合法的!)2、多级解引用下面是阿里“双11”的一道压测题: 代码语言:javascript复制int value = 999; int *p1 = &value; int **p2 = &p1; int ***p3 = &p2; printf("%d", ***p3); // 999 // 解析过程: // 1. *p3得到p2 // 2. *p2得到p1 // 3. *p1得到value六、常见错误及排查方法1、悬垂指针问题: 代码语言:javascript复制int *create_int() { int x = 10; return &x; // 返回局部变量地址! } // 函数结束后x的内存被回收 // 正确做法:使用static或动态分配2、指针运算错误: 代码语言:javascript复制char str[] = "hello"; int *p = (int*)str; p++; // 移动了4字节!可能破坏字符串结构3、解引用未初始化指针: 代码语言:javascript复制int *p; // 未初始化 *p = 10; // 随机写入内存,是非常危险的!七、性能优化1、减少不必要的解引用操作: 代码语言:javascript复制// 不好的写法 for(int i=0; i<100; i++) { sum += *ptr; ptr++; } 下面就是优化之后的写法—— 代码语言:javascript复制// 优化写法 int temp = *ptr; for(int i=0; i<100; i++) { sum += temp; temp = *(++ptr); }2、使用restrict关键字(C99): 代码语言:javascript复制void copy_array(int *restrict dest, int *restrict src, int n) { // 告诉编译器两个指针不会重叠 for(int i=0; i { dest[i] = src[i]; } }往期回顾: 详解C语言指针(上):(含大厂面试实战哦!)指针运算、指针和数组、多级函数 结语:本期内容到这里就结束了,希望对大家有帮助!