C 语言编程 — 函数
目录
文章目录
- 目录
- 前文列表
- 函数
- 函数的声明
- 函数的定义
- 函数的形参与实参
- 值传递
- 引用传递
- 可变长形参列表
- 函数的调用
- 函数的指针
- 回调函数
- 递归函数
- 数的阶乘
- 斐波那契数列
- 构造函数(Constructor)
- 析构函数(Destructor)
前文列表
《程序编译流程与 GCC 编译器》
《C 语言编程 — 基本语法》
《C 语言编程 — 基本数据类型》
《C 语言编程 — 变量与常量》
《C 语言编程 — 运算符》
《C 语言编程 — 逻辑控制语句》
函数
函数的本质就是针对变量的操作过程,同时可能也会改变当前程序的状态。它接受多个输入值,计算并返回一个输出值。
函数大体上分为 3 类:
- 主函数:每个 C 程序都至少有一个
main()
。 - 内置函数:由 C 标准库提供。例如,
strcat()
用来连接两个字符串,memcpy()
用来复制内存到另一个位置。 - 自定义函数
函数的声明
函数声明就是告诉 C 编译器函数的名称、返回类型和参数以及如何调用函数。函数的实际主体可以单独定义,但当你在一个源文件中定义函数且在另一个源文件中调用函数时,函数声明是必需的。
声明函数时,首先将返回值的类型写在前面,后面紧跟函数的名字,而后的一对圆括号里面包裹函数的输入参数,参数之间用 ,
进行分割。
函数声明包括以下几个部分:
return_type function_name(parameter list);
在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面两种都是有效的声明:
int max(int num1, int num2);
int max(int, int);
函数的定义
函数体部分紧跟其后,
函数定义除了定义函数的名称、返回类型、形参列表之外,最重要的是编写函数的主题,即函数体。包裹在 {}
里,里面包含了函数执行的所有语句,语句之间使用 ;
分隔。return
语句用来结束函数的执行,并返回一个值。
C 程序中函数的结构如下:
return_type function_name(parameter list){body of the function
}
- 返回类型:一个函数可以返回一个值,返回类型定义了函数返回的值的数据类型。有些函数不需要返回值,在这种情况下,return_type 是关键字
void
。 - 函数名(函数标识符):函数名和参数列表一起构成了函数签名。
- 形式参数:形参就像是占位符。当函数被调用时,向形参传递一个值,而这个值被称为实际参数。形参列表包括函数参数的类型、顺序、数量。形参是可选的,即形参列表可以为。
- 函数主体:包含了一组定义函数执行任务的语句。
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2)
{/* 局部变量声明 */int result;if (num1 > num2)result = num1;elseresult = num2;return result;
}
函数的形参与实参
如果函数要使用参数,则必须声明接受参数值的变量。这些变量称为函数的形式参数。形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。当调用函数时,有两种向函数传递参数的方式:
- 值传递:该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
- 引用传递:通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。
值传递
默认情况下,C 语言使用传值调用方法来传递参数。一般来说,这意味着函数内的代码不会改变用于调用函数的实际参数。
- swap.c
/* 函数定义 */
void swap(int x, int y)
{int temp;temp = x; /* 保存 x 的值 */x = y; /* 把 y 赋值给 x */y = temp; /* 把 temp 赋值给 y */return;
}
- main.c
#include <stdio.h>/* 函数声明 */
void swap(int x, int y);int main ()
{/* 局部变量定义 */int a = 100;int b = 200;printf("交换前,a 的值: %d\n", a );printf("交换前,b 的值: %d\n", b );/* 调用函数来交换值 */swap(a, b);printf("交换后,a 的值: %d\n", a );printf("交换后,b 的值: %d\n", b );return 0;
}
引用传递
通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。
- swap.c
/* 函数定义 */
void swap(int *x, int *y)
{int temp;temp = *x; /* 保存地址 x 的值 */*x = *y; /* 把 y 赋值给 x */*y = temp; /* 把 temp 赋值给 y */return;
}
- main.c
#include <stdio.h>/* 函数声明 */
void swap(int *x, int *y);int main ()
{/* 局部变量定义 */int a = 100;int b = 200;printf("交换前,a 的值: %d\n", a );printf("交换前,b 的值: %d\n", b );/* 调用函数来交换值* &a 表示指向 a 的指针,即变量 a 的地址* &b 表示指向 b 的指针,即变量 b 的地址*/swap(&a, &b);printf("交换后,a 的值: %d\n", a );printf("交换后,b 的值: %d\n", b );return 0;
}
可变长形参列表
有时候我们需要函数带有可变数量的参数,而不是预定义数量的参数。C 语言提供了 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。
int func(int, ... ) {}int main() {func(1);func(1, 2);func(1, 2, 3);return 0;
}
- 定义一个函数,
int
形参代表了要传递的可变参数的总数,...
可变长形参运算符标识函数的形参数量是可变的。 - 在函数定义中创建一个
va_list
类型变量,该类型是在 stdarg.h 头文件中定义的。 - 使用 int 参数和
va_start
宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。 - 使用
va_arg
宏和 va_list 变量来访问参数列表中的每个项。 - 使用宏
va_end
来清理赋予 va_list 变量的内存。
#include <stdio.h>
#include <stdarg.h>double Avg(int num, ...) {va_list valist;double sum = 0.0;int i;/* 初始化 valist,数量为 num 个 */va_start(valist, num);for (i = 0; i < num; i++) {/* 访问所有赋给 valist 的参数 */sum += va_arg(valist, int);}/* 清理为 valist 保留的内存 */va_end(valist);return sum / num;}int main() {printf("Average of 2, 3, 4, 5 = %f\n", Avg(4, 2, 3, 4, 5));printf("Average of 5, 10, 15 = %f\n", Avg(3, 5, 10, 15));return 0;
}
运行:
$ ./main
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000
函数的调用
定义 C 函数时,会定义函数做什么,然后通过调用函数来完成已定义的任务。当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。
调用函数时,传递所需参数,如果函数返回一个值,则可以存储返回值。
#include <stdio.h>/* 函数声明 */
int max(int num1, int num2);int main ()
{/* 局部变量定义 */int a = 100;int b = 200;int ret;/* 调用函数来获取最大值 */ret = max(a, b);printf( "Max value is : %d\n", ret );return 0;
}/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2)
{/* 局部变量声明 */int result;if (num1 > num2)result = num1;elseresult = num2;return result;
}
函数的指针
函数名本质就是一个地址,也就是一个指针。函数指针是指向函数的指针类型变量,函数指针可以像一般函数一样,用于调用函数、传递参数。也就是说,函数指针其实可以看做就是一个函数的 “别名”。
函数指针很方便,可以作为实参传入函数,在函数体中,仅仅通过指针就可以调用函数,这也是为了提高代码的效率。函数指针和普通指针差不多,主要的区别是函数指针指向了特定的函数。
声明一个函数指针:
typedef int (*fun_ptr)(int, int);
e.g.
#include <stdio.h>int max(int x, int y){return x > y ? x : y;
}int main(void){/* 定义并初始化一个函数指针 */int (*p)(int, int) = &max; // 可以省略地址运算符 &int a, b, c, d;printf("Input three numbers:");scanf("%d %d %d", &a, &b, &c);d = p(p(a, b), c); printf("MAX: %d\n", d);return 0;
}
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。即:回调函数就是一个通过函数指针调用的函数。
通常的,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
#include <stdlib.h>
#include <stdio.h>void populate_array(int *array, size_t arraySize, int (*getNextValue)(void)) {size_t i;for(i = 0; i < arraySize; i++) {array[i] = getNextValue();}
}int getNextRandomValue(void) {return rand();
}int main() {int i;int myarray[10];populate_array(myarray, 10, &getNextRandomValue); // & 可以省略,因为函数名的本质就是一个指针for(i = 0; i < 10; i++) {printf("%d ", myarray[i]);}printf("\n");return 0;
}
递归函数
递归,指的是在函数的定义中调用函数自身。
void recursion()
{statements;... ... ...recursion(); /* 函数调用自身 */... ... ...
}int main()
{recursion();
}
但在使用递归时,一定要注意编写退出条件,否则会进入死循环。递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。
数的阶乘
#include <stdio.h>double factorial(unsigned int i) {if(i <= 1){return 1;}return i * factorial(i - 1);
}int main() {int i = 15;printf("%d 的阶乘为 %f\n", i, factorial(i));return 0;
}
斐波那契数列
#include <stdio.h>int fibonaci(int i) {if (i == 0) {return 0;}if (i == 1) {return 1;}return fibonaci(i-1) + fibonaci(i-2);
}int main() {int i;for (i = 0; i < 10; i++) {printf("%d\t\n", fibonaci(i));}return 0;
}
构造函数(Constructor)
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值。例如:
- Java 中的 new 运算符
- Python 中的
__init__
函数
通常的,一个类中只有一个构造函数;特别的,一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们即构造函数的重载。
析构函数(Destructor)
析构函数与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做 “清理善后” 的工作。
以 C++ 为例:析构函数名也应与类名相同,只是在函数名前面加一个位取反符~,例如:~stud( )
,以区别于构造函数。它不能带任何参数,也没有返回值(包括 void 类型)。只能有一个析构函数,不能重载。如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有用显式的析构函数。
C 语言编程 — 函数相关推荐
- arg是什么函数_C 语言编程 — 函数
函数 函数的本质就是针对变量的操作过程,同时可能也会改变当前程序的状态.它接受多个输入值,计算并返回一个输出值. 函数大体上分为 3 类: 主函数:每个 C 程序都至少有一个 main(). 内置函数 ...
- 51单片机c语言编程函数,单片机C语言教程:C51函数
其实一直出现在例子中的 main()也算是一个函数,只不过它比较特殊,编译时以它做为程序的开始段.有了函数C 语言就有了模块化的优点,一般功能较多的程序,会在编写程序时把每项单独的功能分成数个子程序模 ...
- 的c语言编程函数题,C语言编程题及答案Word版
<C语言编程题及答案Word版>由会员分享,可在线阅读,更多相关<C语言编程题及答案Word版(122页珍藏版)>请在人人文库网上搜索. 1.传播优秀Word版文档 ,希望对您 ...
- c语言 编程 函数声明,C语言编程开发中的函数声明与定义
函数功能的使用在许多编程开发语言中都是有不同的使用方法的,而今天我们就一起来了解一下,在C语言编程开发中的函数功能使用与定义. 对函数的"定义"和"声明"不是一 ...
- Go 语言编程 — 函数
目录 文章目录 目录 定义一个函数 形参列表 值传递 引用传递 返回值 初始化函数 构造函数 析构函数 回调函数 闭包(Closure)函数 方法函数 递归函数 定义一个函数 函数声明需要指定: 函数 ...
- c语言编程函数补充上机题,2011年计算机二级C语言上机操作题及答案(10)
/********found********/ void fun(char *s, *t1, *t2, *w) { int i; char *p, *r, *a; strcpy(w, s); w ...
- python语言编程函数_Python编程语言的函数功能
编程语言导游财报抄写员. 工具/原料 电脑 Python 工作原理 1 如果你有一定的Python基础,想必现在已经熟悉print(),input(),len()函数.Python提供这样一些内建函数 ...
- 用C语言编程函数求圆台体积,第六篇 AutoCAD二次开发技术.pdf
第六章 ObjectARX 开发技术 1. AutoCAD二次开发概述 7. MFC对话框 2. ObjectARX简介 8. 搜索图形数据库及获取实体 3. 常用实体类和几何类 数据 4. 创建实体 ...
- c语言编程函数名:b开头
函数名:bar 功 能:画一个二维条形图 用 法:void far bar(int left,int top,int right,int bottom); 程序例: #include <grap ...
最新文章
- 031_mysql事务的安全隐患
- 【python图像处理】图像的增强(ImageEnhance类详解)
- 机器人布里茨说什么_LOL蒸汽机器人布里茨英雄介绍、技能加点、技巧攻略 机器人出什么装备...
- ibatis.net:第五天,QueryForObject
- [2014NoDEA]An Osgood type regularity criterion for the liquid crystal flows
- JavaScript(十三)面向对象
- 给ADSL用户的忠告
- Apache Prefork、Worker和Event三种MPM简单分析
- Go-项目结构和代码组织
- android view rotate,Android使用RotateImageView 旋转ImageView
- [转]关于内存泄露测试工具
- shell脚本统计httpd进程和子进程占用的CPU、内存大小,要求每隔5s统计一次
- android 教程实例系列
- linux下用C计算某一个目录总大小
- 此版本专旧版本为android,关于Android9.0 此应用专为旧版Android打造,因此可能无法正常运行。请尝试检查更新或与开发者联系...
- L298N电机驱动原理图+PCB制版文件(和你买的模块一模一样的!)
- 购物直播APP系统+短视频系统开发方案
- HTML5前端基础知识
- vue使用高德地图web端JSAPI 路线规划、搜索提示教程
- 装系统需不需要格式化系统盘?别再被这些说法骗了!
热门文章
- mysql innodbuffer修改_mysql参数之innodb_buffer_pool_size大小设置
- hive值乘以0.01保留一位小数_Hive窗口函数01-SUM、MIN、MAX、AVG
- Unigui 服务器解析php,如何正确使用uniGUI的HyperServer(负载均衡)功能
- php表格 单元格,如何更改PHPWord表格单元格高度?
- python深拷贝和浅拷贝的使用场景_深拷贝、浅拷贝的理解与使用场景
- python gui label_更新tkinter Label以在pythongui上显示一个文本文件,每次一行
- iOS应用开发入门(2)——添加一个按钮
- 睡前刷8分钟手机,身体兴奋1小时
- MAXIEYE创始人周圣砚:以规模化迎接智能驾驶科技平权时代 | MEET2022
- 一口气,米哈游连告B站7次