详解进阶指针(下)

简介: 详解进阶指针(下)

函数指针

在C语言中,函数指针是指向函数的指针变量。它可以用来存储和访问函数的地址,从而可以通过函数指针调用相应的函数。

我们先来看一段代码:

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

两个都是test函数的地址,想要将test的地址保存到指针里,我们可以这样做:

void test()
{
  printf("hehe\n");
}
int main()
{
  printf("%p\n", test);
  printf("%p\n", &test);
  void (*pfun)() = test;
  printf("%p\n", pfun);
  return 0;
}

这样就成功的将test函数地址存到了指针中去。

利用函数指针数组实现简易计算器

相信大家都能够写出一个计算器,所以我们来用函数指针数组来实现一下。

#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;
}
int main()
{
  printf("*********************\n");
  printf("**1.add*******2.sub**\n");
  printf("**3.mul*******4.div**\n");
  printf("*******0.exit********\n");
  int x, y;
  int input = 1;
  int (*p[5])(int, int) = { 0,add,sub,mul,div };
  int ret = 0;
  do
  {
    printf("请选择操作:");
    scanf("%d", &input);
    if (0 < input && input < 5)
    {
      printf("请输入操作数:");
      scanf("%d %d", &x, &y);
      ret = (*p[input])(x, y);
      printf("%d\n", ret);
    }
    else if (input == 0)
    {
      printf("退出程序");
    }
    else
    {
      printf("输入有误,重新选择");
    }
  } while (input);
  return 0;
}

我们首先将加减乘除四个功能分别封装为四个函数,然后将四个函数的地址,存放到指针p中,为了更好的对应,我们把第一个元素设置为0,之后进入循环,将input作为操作数传参给p指针数组的下标,x和y作为操作数传参给对应的函数将返回值赋给ret,最后打印ret即可得到结果。

指针和数组题测试

我们了解了之后,来看一下我们掌握的怎么样

//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));// 16,数组名单独放到sizeof代表的是整个数组的大小,16个字节
printf("%d\n",sizeof(a+0));// 4/8 并非是单独放到sizeof中所以代表的是数组首元素的地址
printf("%d\n",sizeof(*a));//4,*a代表对首元素地址解引用,也就是1这个数字也就是4byte//*a == *(a+0) == a[0]
printf("%d\n",sizeof(a+1));// 4/8代表数组中第二个元素的地址
printf("%d\n",sizeof(a[1]));// 4代表数组中第二个元素
printf("%d\n",sizeof(&a));// 4/8 &a代表a数组的地址,地址的字节是4/8
printf("%d\n",sizeof(*&a));// 16 &a得到数组的地址,后解引用访问整个数组的大小,所以是16
printf("%d\n",sizeof(&a+1));//4/8 &a得到的是地址,+1还是地址所以是4/8
printf("%d\n",sizeof(&a[0]));//4/8 a[0]是首元素,&得到地址所以是4/8
printf("%d\n",sizeof(&a[0]+1));//4/8 得到地址后+1还是地址
//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));//6 数组名arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节
printf("%d\n", sizeof(arr+0));//4/8 arr是首元素的地址==&arr[0],是地址就是4/8个字节
printf("%d\n", sizeof(*arr));//1 arr是首元素的地址,*arr就是首元素,大小就是1Byte
printf("%d\n", sizeof(arr[1]));//1 数组中第二个元素
printf("%d\n", sizeof(&arr));//4/8 整个数组的地址
printf("%d\n", sizeof(&arr+1));//4/8 整个数组地址+1
printf("%d\n", sizeof(&arr[0]+1));//4/8 数组第一个元素的地址+1,第二个元素的地址
printf("%d\n", strlen(arr));//随机值,arr是首元素的地址
printf("%d\n", strlen(arr+0));//随机值,arr是首元素的地址, arr+0还是首元素的地址
printf("%d\n", strlen(*arr));//err,arr是首元素的地址, *arr就是首元素 - 'a' - 97
printf("%d\n", strlen(arr[1]));//err, 'b' - 98
printf("%d\n", strlen(&arr));//随机值 地址
printf("%d\n", strlen(&arr+1));//随机值 地址
printf("%d\n", strlen(&arr[0]+1));//随机值 地址
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7 整个数组的大小包括'\0'
printf("%d\n", sizeof(arr+0));//4/8 首元素地址
printf("%d\n", sizeof(*arr));//1 访问数组首元素
printf("%d\n", sizeof(arr[1]));//1 数组中第二个元素
printf("%d\n", sizeof(&arr));//4/8 地址
printf("%d\n", sizeof(&arr+1));//4/8 地址
printf("%d\n", sizeof(&arr[0]+1));//4/8 地址
printf("%d\n", strlen(arr));//6 数组的长度,不包括'\0'
printf("%d\n", strlen(arr+0));//6 arr + 0 的类型是指向字符串的指针,strlen(arr + 0) 返回指针指向的字符串的长度,不包括终止符 \0
printf("%d\n", strlen(*arr));//err 
printf("%d\n", strlen(arr[1]));//err
printf("%d\n", strlen(&arr));//随机值 地址
printf("%d\n", strlen(&arr+1));//随机值 地址
printf("%d\n", strlen(&arr[0]+1));//随机值 地址
char *p = "abcdef";
printf("%d\n", sizeof(p));//4/8 地址
printf("%d\n", sizeof(p+1));//4/8 地址
printf("%d\n", sizeof(*p));//1 代表a
printf("%d\n", sizeof(p[0]));//1 代表a
printf("%d\n", sizeof(&p));//4/8 地址
printf("%d\n", sizeof(&p+1));//4/8 地址
printf("%d\n", sizeof(&p[0]+1));//4/8 地址
printf("%d\n", strlen(p));//随机值 地址
printf("%d\n", strlen(p+1));//随机值 地址
printf("%d\n", strlen(*p));//err
printf("%d\n", strlen(p[0]));//err
printf("%d\n", strlen(&p));//随机值 地址
printf("%d\n", strlen(&p+1));//随机值 地址
printf("%d\n", strlen(&p[0]+1));//随机值 地址
//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));//48 整个数组的大小,4*12=48
printf("%d\n",sizeof(a[0][0]));//4 首行数组的首元素
printf("%d\n",sizeof(a[0]));//16 首行数组
printf("%d\n",sizeof(a[0]+1));//4/8 首行数组地址+1
printf("%d\n",sizeof(*(a[0]+1)));//4 第一行第二个元素解引用
printf("%d\n",sizeof(a+1));//4/8 未单独放代表地址,+1还是地址
printf("%d\n",sizeof(*(a+1)));//16 a+1是第二行的地址,*(a+1)就是第二行,计算的就是第二行的大小
printf("%d\n",sizeof(&a[0]+1));//4/8 地址
printf("%d\n",sizeof(*(&a[0]+1)));//16 第二行的大小
printf("%d\n",sizeof(*a));a表示数组首元素的地址,也就是第一行的地址 *a 就是第一行,也就相当于是第一行的数组名 *a--> *(a+0) -- a[0]
printf("%d\n",sizeof(a[3]));//16

总结

数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址

指针题测试

给大家留一个看起来很复杂的指针测试题,相信大家理解了指针后,一定能轻而易举的写出来

int main()
{
 char *c[] = {"ENTER","NEW","POINT","FIRST"};
 char**cp[] = {c+3,c+2,c+1,c};
 char***cpp = cp;
 printf("%s\n", **++cpp);
 printf("%s\n", *--*++cpp+3);
 printf("%s\n", *cpp[-2]+3);
 printf("%s\n", cpp[-1][-1]+1);
 return 0;
}

最后吸收一下大家的手气。

目录
相关文章
|
弹性计算 安全 Linux
阿里云服务器搭建宝塔面板(新手入门)
阿里云服务器搭建宝塔面板(新手入门)阿里云服务器网以CentOS操作系统为例,安装宝塔Linux面板,先远程连接到云服务器,然后执行宝塔面板安装命令,系统会自动安装宝塔面板,安装完成后会返回面板地址、账号和密码,然后在安全组开通宝塔面板端口号
769 0
|
网络协议 数据可视化 Java
衣带渐宽终不悔!嵌入式大牛10年调Bug经验总结
衣带渐宽终不悔!嵌入式大牛10年调Bug经验总结
131 0
|
运维 Prometheus 监控
产品动态丨阿里云计算巢月刊-2023年第04期
计算巢支持对服务实例的批量升级、全托管服务支持自定义运维项等7项功能更新;计算巢私有化部署接入Prometheus监控最佳实践;鸿翼医药智能RA平台、可道云网盘Kodbox及数据可视化开放平台datart等合作伙伴服务更新……让优秀的企业软件生于云、长于云~
产品动态丨阿里云计算巢月刊-2023年第04期
|
定位技术 iOS开发
iOS中 百度地图详解 韩俊强的博文
需要准备工作按照下图引进类库 需要添加 添加的两个字符串为:NSLocationWhenInUseUsageDescription  /  NSLocationAlwaysUsageDescription 默认定位设置: 设置工作准备完毕上代码: 指示根视图: ...
752 0
|
6天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
17天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1320 7
|
5天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
296 129
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
|
4天前
|
监控 JavaScript Java
基于大模型技术的反欺诈知识问答系统
随着互联网与金融科技发展,网络欺诈频发,构建高效反欺诈平台成为迫切需求。本文基于Java、Vue.js、Spring Boot与MySQL技术,设计实现集欺诈识别、宣传教育、用户互动于一体的反欺诈系统,提升公众防范意识,助力企业合规与用户权益保护。