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

善于运用指针--函数与指针

 

文章目录

  • 前言
  • 一、函数的指针
  • 二、函数指针运用
    • 1函数名地址
    • 2指针变量调用函数
    • 3指向函数的指针变量做函数参数
  • 二、返回指针值的函数
  • 总结

 


 

前言

如果在程序中定义了一个函数,在编译时会把函数的源代码转换为可执行代码并分配一段空间。这段空间有一个起始地址,也称为函数的入口地址。每次调用函数时都从该地址入口开始执行此函数代码。 


一、函数的指针

函数名就是函数的起始地址,就是函数的指针。

01f28c84c06e499b94c052cfbb34d8ac.png

二、函数指针运用

1.函数名地址

代码如下(示例):

int add(int x, int y)
{return x + y;
}
int main()
{printf("%p", add);//打印函数地址return 0;
}

打印结果为00007FF6BC9013D9 ,是函数地址,由此可见函数名是一个地址,定义指针变量可以将函数名直接赋给指针变量。


2.指针变量调用函数

代码如下(示例):

void swap(int*p1, int*p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int main()
{int arr[] = { 1,2,3,4,5 };//swap(&arr[1], &arr[4]);  void (*pc)(int*, int*) = swap;//定义指向函数的指针变量(*pc)(&arr[1], &arr[4]);//用指针变量调用swap函数int i = 0;for (i = 0; i < 5; i++){printf("%d ", arr[i]);}return 0;
}

该处可以看出,swap函数的调用,指针*pc对应函数名,括号后面调用一样。

但是得注意,定义指针变量指向函数名(也就是函数地址)时,定义的类型应与函数返回值类型一致,后面括号里的量也应与被调函数的定义保持一致。

在给指针变量赋值时,只需给出函数名,无需给出函数参数。void (*pc)(int*, int*) = swap;

用指针变量调用函数时,只需用*pc代替函数名,在(*p)的括号根据需要写上实参

6fdfcd6ce1e04e12a360f424374a1710.png

函数指针变量不能进行算数运算

用函数名调用函数,只能调用所指定的一个函数,但是用指针变量调用函数,可以根据不同情况先后调用不同函数。

注意:定义的pc指针,只能指向参数类型为int、int,返回类型为void的函数。


3、用指向函数的指针做函数参数

指向函数的指针,把函数地址作为参数传递给其它函数 

用指向函数的指针做函数参数,这个函数的返回值类型是一个指针,参数是函数指针的参数


设计一个计算器,实现加减乘除:

代码如下:

void print()
{printf("********************************\n");printf("***** 1.add     2.sub **********\n");printf("***** 3.mul     4.div **********\n");printf("*****    0.esc   ***************\n");
}
int add(int x, int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
int mul(int x, int y)
{return x * y;
}
int div(int x, int y)
{return x / y;
}
int main()
{int a = 0;int b = 0;int ret = 0;int input = 0;print();scanf("%d", &input);switch (input){case 1:printf("请输入两个数\n");scanf("%d%d", &a, &b);ret=add(a, b);printf("%d", ret);break;case 2:printf("请输入两个数\n");scanf("%d%d", &a, &b);ret = sub(a, b);printf("%d", ret);break;case 3:printf("请输入两个数\n");scanf("%d%d", &a, &b);ret = mul(a, b);printf("%d", ret);break;case 4:printf("请输入两个数\n");scanf("%d%d", &a, &b);ret = div(a, b);printf("%d", ret);break;default:printf("输入错误");break;}return 0;
}

这里在case里反复重复的内容太多,代码不够简洁,这里add、sub、mul、div的函数参数和返回值都是同样类型的,因此可定义一个指针(即*p)用来指向这几个函数,再定义一个指针函数,参数为指向这几个函数的指针(即*p)


代码如下: 

void col(int(*p)(int,int))
{int a = 0;int b = 0;int ret = 0;printf("请输入两个数\n");scanf("%d%d", &a, &b);ret =(*p)(a, b);printf("%d", ret);
}void print()
{printf("********************************\n");printf("***** 1.add     2.sub **********\n");printf("***** 3.mul     4.div **********\n");printf("*****    0.esc   ***************\n");
}
int add(int x, int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
int mul(int x, int y)
{return x * y;
}
int div(int x, int y)
{return x / y;
}
int main()
{int input = 0;print();scanf("%d", &input);switch (input){case 1:col(add);break;case 2:col(sub);break;case 3:col(mul);break;case 4:col(div);break;default:printf("输入错误");break;}return 0;
}

这样的程序更简洁,更具可读性。


三、返回指针值的函数

函数返回值是一个地址或者指针。

字符串拷贝函数 

char* string_copy(char* str1, char* str2)
{char* start = str2;while (*str2++ = *str1++);return start;
}
int main()
{char arr1[] = "abcdef";char arr2[20] = { 0 };char* ret = string_copy(arr1, arr2);printf("% s\n", ret);return 0;
}

如上,函数的返回值是一个指针,这里将函数返回值赋给ret这个指针,通过指针指向的地址找到并打印字符,如果解引用,找到的会是字符数组的首地址的内容,只能打印“a”

在这里,%s, 传参ret是一个地址,打印该地址对应的字符串,遇到\0停止。

在C语言中,找到地址就相当于找到了值。


找学生序号对应的分数

int *score(int(*p)[3], int n)
{return *(p + n);
}
int main()
{int n = 0;int arr[][3] = { {1,2,3},{4,5,6},{7,8,9} };int i = 0;scanf("%d", &n);int* p = score(arr, n);for (i = 0; i < 3; i++){printf("%d", *(p + i));}return 0;
}

 这个地方的解引用是二维数组某行的首地址,也就是找到地址对应的行,依次打印数字

总结

(未完待续...)

 

 


http://www.mrgr.cn/news/79839.html

相关文章:

  • CSS的2D和3D动画效果
  • LEED绿色建筑认证最新消息
  • C语言——上下弹跳的小球
  • Node.js(v16.13.2版本)安装及环境配置教程
  • 时间敏感网络与工业通信的融合:光路科技电力专用交换机和TSN工业交换机亮相EP电力展
  • 2024年中国生成式AI大会上海站盛大启幕,首日峰会聚焦大模型技术前沿
  • 《机器学习》2.4假设检验 t分布 F分布
  • 光伏电站建设成本利润估算
  • Playwright中Page类的方法
  • Android 第三方框架:RxJava:源码分析:责任链模式
  • 【学习笔记】目前市面中手持激光雷达设备及参数汇总
  • 信奥题解:Recamán 序列
  • 100 模数与数模转换器
  • 51c深度学习~合集9
  • Visual Studio的解决方案管理器怎么从文件夹视图切换回原视图——最后一根稻草
  • 从小学题到技术选型哲学:以智能客服系统为例,解读相关AI技术栈20241211
  • Python基础笔记17--面向对象(其他)
  • C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
  • Scala的隐式对象
  • prometheus
  • LLMs之APE:基于Claude的Prompt Improver的简介、使用方法、案例应用之详细攻略
  • 系统思考—结构影响行为
  • D95【python 接口自动化学习】- pytest进阶之fixture用法
  • 【开源】基于SpringBoot框架美容院管理系统(计算机毕业设计)+万字说明文档 T012
  • C# 在dataview可以直接增删改查mysql数据库
  • C++day4