当前位置: 首页 > news >正文

指针核心知识:5篇系统梳理4

本系列将通过 5 篇文章系统梳理指针核心知识,欢迎关注专栏指针梳理,获取完整内容

1.字符指针变量

字符指针变量的类型是char*

代码举例:

//字符指针使用举例 #include <stdio.h> int main() { char a = 'w'; char* ch = &a; *ch = 'z'; printf("%c\n", a); return 0; }

易错点——>思考:以下代码是将''hello world''放到指针变量pstr里了吗?

#include <stdio.h> int main() { char* pstr = "hello world"; printf("%s\n", pstr); return 0; }

初学者容易误解这段代码,以为 pstr 存储了整个字符串内容,但实际上它只保存了字符串首字符的地址。


2.《剑指offer》的一道字符串笔试题

#include <stdio.h> int main() { char str1[] = "hello bit."; char str2[] = "hello bit."; const char* str3 = "hello bit."; const char* str4 = "hello bit."; if (str1 == str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if (str3 == str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0; }

拆解:

str1和str2

  • char str1[ ]="hello bit."会在栈内存中开辟一块独立空间,把字符串内容拷贝进去;
  • char str2[ ]="hello bit."会再开辟另一块独立空间,把字符串内容拷贝进去;
  • 即使内容完全一样,它们是两个不同的数组,所以str1和str2指向的地址不同,用==比较时,比较的是指针地址,因此结果为false

str3和str4

  • "hello bit."是字符串常量,C/C++会把它放在只读数据段(常量区);
  • 编译器会做常量折叠优化:相同内容的字符串常量,只会存一份,节省空间
  • const char* str3和const char* str4都指向这个唯一的常量字符串,所以地址相同,用==比较时,地址相等,因此结果为true

3.数组指针变量

定义

数组指针变量:存放的是数组的地址,能够指向数组的指针变量

提问:下面哪个是数组指针变量?

int* p1[10]; int(*p2)[10];

答案是第二个

[ ]的优先级要高于*号,所以必须加上()来保证p先和*结合

int(*p2)[10];

解释:p2先和*结合,说明p是一个指针变量,然后指针指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。


数组指针变量的初始化

int arr[10] = { 0 }; int(*p)[10] = &arr;

数组指针类型解析:

int (*p) [10] = &arr; | | | | | | | | p指向数组的元素个数 | p是数组指针变量名 p指向的数组的元素类型

4.二维数组传参的本质

二维数组本质上是由多个一维数组组成的数组结构。具体来说,二维数组的每个元素本身就是一个一维数组。因此,二维数组的首元素实际上就是它的第一行数据,这个首元素本身就是一个完整的一维数组

因此,依据数组名代表数组首元素地址这一规则,二维数组名实际上表示第一行的地址,即一维数组的地址。以前文示例为例,第一行的一维数组类型为int[5],因此第一行地址的类型应为数组指针类型int(*)[5]。这意味着二维数组传参本质上是地址传递,传递的是第一行这个一维数组的地址。

5.函数指针变量

函数指针变量是用来存放函数地址的,未来通过地址能够调用函数

提问:函数是否有地址呢?

#include <stdio.h> void test() { printf("hehe\n"); } int main() { printf("test: %p\n", test); printf("&test: %p\n", &test); return 0; }

函数是有地址的,函数名就是函数的地址,当然也可以通过&函数名的方式获得函数的地址。

这里两种取函数地址的方式没有区别,不像数组

函数指针类型解析

int (*pf3) (int x, int y) | | ------------ | | | | | pf3指向函数的参数类型和个数的交代 | 函数指针变量名 pf3指向函数的返回类型 int (*) (int x, int y) //pf3函数指针变量的类型

举例使用:

#include <stdio.h> int Add(int x, int y) { return x + y; } int main() { int(*pf3)(int, int) = Add; printf("%d\n", (*pf3)(2, 3)); printf("%d\n", pf3(3, 5)); return 0; }

6.typedef关键字

typedef是用来类型重命名的,可以将复杂的类型,简单化

比如,你觉得 unsigned int 写起来不方便,如果能写成 uint 就方便多了,那么我们可以使用:

typedef unsigned int uint; //将unsigned int 重命名为uint

如果是指针类型,能否重命名呢?其实也是可以的,比如,将 int* 重命名为 ptr_t ,这样写:

typedef int* ptr_t;

但是对于数组指针和函数指针稍微有点区别:

比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

typedef void(*pfun_t)(int);//新的类型名必须在*的右边

7.函数指针数组

要把函数的地址存到一个数组中,那这个数组就叫函数指针数组

int (*parr1[3])(int,int);

parr1 先和 [ ] 结合,说明parr1是数组,数组里的元素是int (*)(int,int) 类型的函数指针

8.函数指针数组的应用——转移表

简易计算器的一般实现:

#include <stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return a / b; } void menu() { printf("---------------------------------\n"); printf("------- 1.加法 2.减法 ----------\n"); printf("------- 3.乘法 4.除法 ----------\n"); printf("---------- 0.退出 --------------\n"); printf("---------------------------------\n"); } int main() { int x, y; int input = 1; int ret = 0; do { menu(); printf("请选择:"); scanf("%d", &input); switch (input) { case 1: printf("输入操作数:"); scanf("%d %d", &x, &y); ret = add(x, y); printf("ret = %d\n", ret); break; case 2: printf("输入操作数:"); scanf("%d %d", &x, &y); ret = sub(x, y); printf("ret = %d\n", ret); break; case 3: printf("输入操作数:"); scanf("%d %d", &x, &y); ret = mul(x, y); printf("ret = %d\n", ret); break; case 4: printf("输入操作数:"); scanf("%d %d", &x, &y); ret = div(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出程序\n"); break; default: printf("选择错误\n"); break; } } while (input); return 0; }

使用函数指针数组实现:

#include <stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return a / b; } void menu() { printf("---------------------------------\n"); printf("------- 1.加法 2.减法 ----------\n"); printf("------- 3.乘法 4.除法 ----------\n"); printf("---------- 0.退出 --------------\n"); printf("---------------------------------\n"); } int main() { int x, y; int input = 1; int ret = 0; int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表 do { menu(); printf("请选择:"); scanf("%d", &input); if ((input <= 4 && input >= 1)) { printf("输入操作数:"); scanf("%d %d", &x, &y); ret = (*p[input])(x, y); printf("ret = %d\n", ret); } else if (input == 0) { printf("退出计算器\n"); } else { printf("输入有误\n"); } } while (input); return 0; }

switch—case版本痛点:

  • 代码高度重复:每个case里都要写「输入操作数→调用函数→打印结果」的模板代码。
  • 扩展性差:如果要新增「取模、幂运算」,就要多写一个case,代码越来越长

而函数指针数组的版本更好

// 声明一个数组,每个元素是「指向返回 int、参数为两个 int 的函数」的指针 int (*p[5])(int x, int y) = {0, add, sub, mul, div};

用数组下标直接调用函数

// input 是用户选择的 1~4,直接作为数组下标 ret = (*p[input])(x, y); // 也可以写成 ret = p[input](x, y);

优势:

  • 代码极简:去掉了所有case分支,用一行调用替代。
  • 扩展性强:新增运算只需在数组里加一个函数指针,不用改主逻辑。

由于单篇文章无法涵盖所有知识,其他内容请移步至本专栏其他文章指针梳理

http://www.jsqmd.com/news/494881/

相关文章:

  • 2026澳洲最好的证券公司求职笔试辅导在哪里:独家面经(必看) - 品牌排行榜
  • 口碑好的装修公司
  • 拜访客户管理的软件哪个好用?免费选工具攻略 - 企业数字化观察家
  • TVC与RCD参数耦合约束解析
  • Python深度学习新闻情感分析预测系统 SnowNLP情感分析
  • 口碑见证实力!天津陪诊机构守嘉,用贴心服务赢得市民信赖 - 品牌排行榜单
  • GIMP 3.2 (Linux, macOS, Windows) - 免费开源图像编辑器
  • hot100题解 —— 23.合并K个升序链表
  • 使用Claude Code Agent 开发独立业务功能,文档代码全搞定
  • 【PyTorch】2.0 入门学习
  • Win11 连接某公司云电脑 MySQL 数据库教程,新手小白使用,超详细,草履虫也能学会
  • db-scheduler 支持的任务模式
  • 信安毕业设计2026项目选题建议
  • 别再踩坑了!CentOS 7 安装 MySQL 看这一篇就够
  • Synfig Studio 1.5.5 (Linux, macOS, Windows) - 开源 2D 动画软件
  • 【数据集】地级市城乡居民社保相关数据(2000-2024年)
  • 影刀自动化工作流RPA采集教程
  • Typora快速上手教学
  • 安全篇:为什么所有大模型都逃不过 Prompt Injection?一次完整的转义攻击技术分析
  • L2TP-域名配置步骤
  • Java 抽象类 相关知识点
  • 第57届家博会见!奇兵到家将助力广州家具商决胜“服务战”
  • 在储能系统中,储能变流器的三相并网电压矢量控制是实现双向充放电的关键技术。今天我们就来聊聊这个技术,顺便看看代码实现
  • 使用LangGraph构建AIAgent:RAG与长期记忆
  • 强强联手!移远通信与小牛电动达成深度战略合作,开启AI两轮出行新篇章
  • C++:命名空间与输入输出
  • C++编译期字符串处理
  • AudioTrack音频播放流程深度解析
  • 蓝牙相关技术
  • 【Linux】网络编程基础—套接字