C 语言编程 — 函数
目录
文章目录
前文列表
《程序编译流程与 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;
else
result = 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;
else
result = 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 语言编程 — 函数的更多相关文章
- Linux下C语言编程实现spwd函数
Linux下C语言编程实现spwd函数 介绍 spwd函数 功能:显示当前目录路径 实现:通过编译执行该代码,可在终端中输出当前路径 代码实现 代码链接 代码托管链接:spwd.c 所需结构体.函数. ...
- LINUX下C语言编程调用函数、链接头文件以及库文件
LINUX下C语言编程经常需要链接其他函数,而其他函数一般都放在另外.c文件中,或者打包放在一个库文件里面,我需要在main函数中调用这些函数,主要有如下几种方法: 1.当需要调用函数的个数比较少时, ...
- C语言编程学习:使用函数必须知道的3点注意事项
C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...
- C语言编程中函数指针的定义及使用
C语言中函数指针的定义: typedef int (*funcPtr)(int, int)表示定义了一个函数指针funcPtr,这个函数指针只能指向如下: int add(int, int).int ...
- C语言编程实现Linux命令——who
C语言编程实现Linux命令--who 实践分析过程 who命令是查询当前登录的每个用户,它的输出包括用户名.终端类型.登录日期及远程主机,在Linux系统中输入who命令输出如下: 我们先man一下 ...
- 个人c语言编程风格总结
总结一下我个人的编程风格及这样做的原因吧,其实是为了给实验室写一个统一的C语言编程规范才写的.首先声明,我下面提到的编程规范,是自己给自己定的,不是c语言里面规定的. 一件事情,做成和做好中间可能隔了 ...
- C语言的函数
"函数"在英文的翻译是"function",无论在自然科学还是计算机科学都是这个词,而"function"的本意是"功能" ...
- 混合语言编程:启用CLR(公共语言运行时编译)让C#调用C++
前言 关于混合C#和C++的编程方式,本人之前写过一篇博客(参见混合语言编程:C#使用原生的Directx和OpenGL),在之前的博客中,介绍了在C#的Winform和WPF下使用原生的Direct ...
- Linux基础与Linux下C语言编程基础
Linux基础 1 Linux命令 如果使用GUI,Linux和Windows没有什么区别.Linux学习应用的一个特点是通过命令行进行使用. 登录Linux后,我们就可以在#或$符后面去输入命令,有 ...
- LINUX下C语言编程基础
实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用 ...
随机推荐
- Lustre架构介绍的阅读笔记-HSM
本文是在阅读Introduction to Lustre* Architecture的Lustre HSM System Architecture时的笔记. Hierarchical Storage ...
- 如何获取华为运动健康服务授权码并调用Rest API访问数据?
华为运动健康服务(HUAWEI Health Kit)允许三方生态应用在获取用户授权后,通过REST API接口访问数据库,读取华为和生态伙伴开放的运动健康数据或写入数据到华为运动健康服务,为用户提供 ...
- openGauss Cluster Manager RTO Test
一.环境介绍 软件环境 类别 版本 下载链接 备注 OS openEuler 20.03 (LTS) https://repo.openeuler.org/openEuler-20.03-LTS/IS ...
- Excel 特殊分组汇总示例
期望结果: 按照成绩划分区间段来分组时,定义好值的划分区间后,只需用 pseg 函数便可以将分数分好段,然后统计出各段人次即可. SPL 代码如下: A B 1 =clipboard().impo ...
- 填报表中也可以添加 html 事件
在实际的项目开发中,填报表的应用十分广泛. 多数情况下,填报表会作为整个项目的一部分配合需求灵活使用,但有时也会受大项目环境的影响,产生一些特别的要求.比如,通常报表单元格的数据类型大多是文本,有时却 ...
- MVC 测试action的运行速度
前言 网络很多文章有关于action的测试机制,本文主要是整理一下思路. 正文 假如有一个acion: public ActionResult Index() { return View(); } 当 ...
- C/C++常考习题
1.什么是虚函数?什么是纯虚函数? 虚函数:允许被其子类重新定义的成员函数. 虚函数的声明:virtual returntype func(parameter);引入虚函数的目的是为了动态绑定: 纯虚 ...
- Node. js 有哪些全局对象?
一.是什么 在浏览器 JavaScript 中,通常window 是全局对象, 而 Nodejs中的全局对象是 global 在NodeJS里,是不可能在最外层定义一个变量,因为所有的用户代码都是当前 ...
- 力扣175(MySQL)-组合两个表(简单)
题目: 表: Person 表: Address 编写一个SQL查询来报告 Person 表中每个人的姓.名.城市和州.如果 personId 的地址不在 Address 表中,则报告为空 null ...
- 力扣24(java&python)-两两交换链表中的节点(中等)
题目: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点.你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换) 示例 1: 输入:head = [1,2,3,4] 输出:[ ...