C学习笔记(十二)函数详解
来源:互联网 发布:知乎 女神 豆瓣 编辑:程序博客网 时间:2024/05/17 02:51
函数
函数,迭代开发,封装成函数,有利于代码的复用性和维护性
要求:内部要尽量少的调用,函数要有独立性
函数调用进行的操作
①通过函数名找到函数入口地址
②给形参分配内存空间
③传值:把实参变量对应空间的值传给形参
④执行函数体里的语句
一.函数三要素:
1.函数名:动词 + 名词 下滑线连接的字符不要超过5个
自注释性(提高代码的可读性)、
2.形参
函数参数的传递
如何确定传值还是传地址?
读(不修改)实参变量对应内存空间的值时,传实参变量名
写(修改)实参变量对应内存空间的值时,传实参变量的地址
3.返回值
3.1函数返回值类型的确定
传整型变量 用整型变量来接
传字符变量 用字符变量来接
传一维数组的地址 用数组元素指针来接
传二维数组的地址用一维数组指针来接
传一维数组的地址用二维数组指针来接
2.不能返回局部变量的地址或指针
3.如何返回多个结果?
利用传出参数
利用全局变量 :因为全局变量的作用域为定义变量开始到程序结束,对于编写多个返回值的C语言函数,我们可以把返回的多个值定义成全局变量,当调用函数时,全局变量被修改,再把修改后的全局变量的值应用于主调函数中,即多个返回值。但使用全局变量时应注意,全局变量的作用域为全局,所以程序范围内都可以修改它的值,如果出现错误,难以发现,而且全局变量增加程序间模块的耦合,所以要慎用
传递数组指针:把多个返回值作为数组元素定义成一个数组的形式,并使该数组的地址作为函数形参,以传址的方式传递数组参数。函数被调用后,形参数组元素改变倒置实参改变,从改变的实参数组元素取得多个返回值。
传递结构体指针:如果返回多个值得数据类型不一样,除使用全局变量以外,也可以是使用结构体。使用方法类似数组指针。
例,编写函数求一维整型数组的最大值与最小值,并把最大值和最小值返回给主调函数(传递数组指针)
#include<stdio.h>void max_min(int *ret);int main(){ int i; int ret[8]; printf("Please input the param:\n"); for(i = 0;i < 8 ;i++) { scanf("%d",&ret[i]); } printf("befor int array = "); for(i = 0;i < 8 ;i++) { printf("%d ",ret[i]); } putchar('\n'); max_min(ret); printf("after int array = "); for(i = 0;i < 8 ;i++) { printf("%d ",ret[i]); } putchar('\n'); printf("the max param = %d\n",ret[0]); printf("the min param = %d\n",ret[7]); return 0;}void max_min(int *ret){ int i; for(i = 0; i < 8; i++) { ret[0] = (ret[0] > ret[i + 1]) ? ret[0] : ret[i + 1];ret[7] = (ret[8] < ret[i + 1]) ? ret[8] : ret[i + 1]; }}
4.return VS exit
return
char *func(){ char *src = "hello worid"; return src;}int main(){ char *ptr =func(); printf("ptr = %s\n",ptr); return 0;}PS:此程序的src 保存在全局数据区的rodata段,生命周期为程序结束后,不会乱码
若char src[100] = "hello world"; 数组保存在栈空间,生命周期为函数结束后,会有乱码。
主函数的返回值return 0;0 是反馈给系统 主函数结束前返回0,这是一个规范要求 ,可以提高运行效率
exit 用来异常处理时结束整个程序 1 是反馈给用户 命令行敲 echo ¥? 可以打印当前程序结束前的返回值
4.企业级函数的编写
函数的编码规范
十分重要的几点:
对所调用函数的错误返回码要仔细全面的处理
防止将函数的参数作为工作变量
为简单功能编写函数
不要编写依赖于其他函数内部实现的函数
检查所有参数的有效性
函数的入口参数检查
if(src == NULL || dst == NULL){ printf("the param is error!\n"); exit(1);}
函数的返回值异常处理
exit
5.函数指针 函数指针数组
回调函数:函数指针做形参,函数指针指向的函数
引申:用C语言如何实现面向对象:封装 结构体
继承 结构体的包含关系
多态:函数指针
6.函数的可变参数
C语言编程中有时会遇到一些参数个数可变的函数,例如,printf( )函数的函数原型为
int printf( const char* format, ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点“…”做参数占位符),实际调用时可以有以下的形式。
printf("%d",i); printf("%s",s); printf("the number is %d ,string is:%s", i, s);
一个简单的可变参数的C函数。先看例子程序,该函数至少有一个整数参数,其后是占位符“…”,表示后面参数的个数不定。在这个例子里,所有的输入参数必须都是整数,函数的功能只是打印所有参数的值,函数代码如下。
示例代码:可变参数函数的使用。
#include <stdio.h>#include <stdarg.h>void simple_va_fun(int start, ...) { va_list arg_ptr; int nArgValue =start; int nArgCout="0"; //可变参数的数目 va_start(arg_ptr,start); //以固定参数的地址为起点确定变参的内存起始地址 do { ++nArgCout; printf("the %d th arg: %d",nArgCout,nArgValue); //输出各参数的值 nArgValue = va_arg(arg_ptr,int); //得到下一个可变参数的值 } while(nArgValue != -1); return; }int main(int argc, char* argv[]){ simple_va_fun(100,-1); simple_va_fun(100,200,-1); return 0;}
下面解释一下这些代码。从这个函数的实现可以看到,我们使用可变参数应该有以下步骤。
(1)在程序中将用到以下这些宏。
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
va在这里是variable-argument(可变参数)的意思,这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件。
(2)函数里首先定义一个va_list型的变量,这里是arg_ptr,这个变量是存储参数地址的指针,因为得到参数的地址之后,再结合参数的类型,才能得到参数的值。
(3)然后用va_start宏初始化(2)中定义的变量arg_ptr,这个宏的第二个参数是可变参数列表的前一个参数,即最后一个固定参数。
(4)然后依次用va_arg宏使arg_ptr返回可变参数的地址,得到这个地址之后,结合参数的类型,就可以得到参数的值。
(5)设定结束条件,这里的条件就是判断参数值是否为-1。注意被调的函数在调用时是不知道可变参数的正确数目的,程序员必须自己在代码中指明结束条件。至于为什么它不会知道参数的数目,在看完这几个宏的内部实现机制后,自然就会明白。
- C学习笔记(十二)函数详解
- C/C++学习笔记(十二)extern的详解
- (c/c++学习笔记十二)const详解
- C++学习笔记(十二):重载函数
- C语言程序学习(十二)笔记
- c 语言学习笔记十二
- 学习笔记(十二)
- Halcon学习(二十二)摄像机标定(函数详解)
- linux学习笔记(十二) mount命令详解
- Cocos2d-x学习笔记(十二)CCMenuItem详解
- Hadoop学习笔记(十二)---hadoop配置文件详解
- mybatis学习笔记(十二) 映射配置文件详解
- Redis 学习笔记(十二)Redis 复制功能详解
- pytorch学习笔记(十二):详解 Module 类
- Redis 学习笔记(十二)Redis 复制功能详解
- MySQL学习笔记(十二)运算符和函数一
- mysql学习笔记之十二(存储过程和函数)
- C++学习笔记十二之函数重载
- Scalaz(49)- scalaz-stream: 深入了解-Sink/Channel
- android沉浸式
- jQuery Mobile 工具栏
- poj Aggressive cows
- HDOJ 4548 美素数
- C学习笔记(十二)函数详解
- 旧识——快速傅里叶变换
- 【Codeforces666B】【World Tour】【最短路】
- 从零开始搭建web项目并部署到本地服务器
- 成为一个开发者
- [ARM]ldr和 adr/adrl 伪指令的区别
- HDOJ 1084 What Is Your Grade?
- PostgreSQL 物流调度算法探索 - 基于PostGIS/pgrouting/机器学习
- Nginx负载均衡配置实例详解