目录

1. 函数定义

2. 函数参数

3. 函数调用

4. 嵌套调用

5. 链式访问

6. 函数声明

6.1 函数调用前声明

6.2 分离式编译在头文件声明

7. 递归调用

8. C99 内联函数


1. 函数定义

返回类型 函数名(参数列表)
{
	函数体
}

返回类型是函数返回值的类型。“返回类型 函数名(参数列表)”整个叫函数头。

例如:

int max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;
}
// 函数头      int max(int x, int y)
// 返回类型    int
// 函数名      max
// 参数列表    int x, int y
// 函数体      {}内部的为函数体

2. 函数参数

  • 实际参数(实参)

真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

  • 形式参数(形参)

形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。形参实例化之后其实相当于实参的一份临时拷贝。

形参和实参可以同名,它们之间相互独立、互不影响。因为实参在函数外部有效,而形参在函数内部有效。

#include <stdio.h>

int max(int x, int y) // x y是形参
{
	if (x > y)
		return x;
	else
		return y;
}

int main()
{
	int a = 36;
	int b = 21;
	int maxValue = max(a, b); // a b是实参
	printf("maxValue = %d\n", maxValue);
	return 0;
}

3. 函数调用

使用函数的过程,称为调用函数。

  • 传值调用是指函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
  • 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

写一个函数,每调用一次这个函数,就会将num的值增加1。

  • 使用传值调用
#include <stdio.h>
 
int Add(int n)
{
	return ++n;
}
 
int main()
{
	int num = 0;
	num = Add(num);
	printf("%d\n", num); // 1
	num = Add(num);
	printf("%d\n", num); // 2
	num = Add(num);
	printf("%d\n", num); // 3
	num = Add(num);
	printf("%d\n", num); // 4
	return 0;
}
  • 使用传址调用
#include <stdio.h>
 
void Add(int* p)
{
	(*p)++;
}
 
int main()
{
	int num = 0;
	Add(&num);
	printf("%d\n", num); // 1
	Add(&num);
	printf("%d\n", num); // 2
	Add(&num);
	printf("%d\n", num); // 3
	Add(&num);
	printf("%d\n", num); // 4
	return 0;
}

4. 嵌套调用

嵌套调用是指在一个函数调用另外一个函数。函数可以嵌套调用,但是不能嵌套定义。

#include <stdio.h>

void func1(int x, int y)
{
	printf("%d %d\n", x, y); // func1()函数中调用printf()函数
}

void func2(int x, int y)
{
	x *= 2;
	func1(x, y); // func2()函数中调用func1()函数
}

int main()
{
	int a = 2;
	int b = 1;
	func2(a, b); // main()函数中调用func2函数
	return 0;
}
// 4 1

5. 链式访问

链式访问是指把一个函数的返回值作为另外一个函数的参数。

#include <stdio.h>

int main()
{
	printf("%d", printf("%d", printf("%d", 43)));
	// printf()函数的返回值是所有打印字符的个数
	return 0;
}
// 4321

6. 函数声明

6.1 函数调用前声明

只有后面定义过的函数才能调用前面定义过的函数,如果函数定义在后面,需要在函数调用前进行函数声明。

函数定义在调用之前不需要声明。

#include <stdio.h>

// 函数定义
int max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;
}

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("%d\n",max(a, b));
	return 0;
}

函数定义在调用之后需要在调用前声明。

#include <stdio.h>

// 函数声明
int max(int, int);
/*
int max(int x, int y);
*/

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("%d\n",max(a, b));
	return 0;
}

// 函数定义
int max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;
}

6.2 分离式编译在头文件声明

max.h:

#pragma once

// 包含源文件所需要的头文件
#include <stdio.h>

// 函数声明
int max(int x, int y);
/*
int max(int, int);
*/

max.c:

// 函数定义
int max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;
}

test.c:

// 包含max.h头文件
#include "max.h"

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("%d\n",max(a, b));
	return 0;
}

7. 递归调用

程序调用自身的编程技巧称为递归( recursion)。递归调用是一种特殊的嵌套调用。

递归的必要条件:

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续
  • 每次递归调用之后越来越接近这个限制条件

例题1:按照顺序输出无符号整数的每一位

假设输入1234,输出1 2 3 4

思路:

print(1234)

print(123) 4

print(12) 3 4

print(1) 2 3 4

1 2 3 4 

#include <stdio.h>
 
void print(unsigned int n)
{
	if (n > 9)
	{
		print(n / 10);
	}
	printf("%d ", n % 10);
}
 
int main()
{
	unsigned int num = 0;
	scanf("%u", &num);
	print(num);
	return 0;
}

例题2:下列程序执行后,输出的结果为?

#include <stdio.h>

int cnt = 0;

int fib(int n)
{
	cnt++;
	if (n == 0)
		return 1;
	else if (n == 1)
		return 2;
	else
		return fib(n - 1) + fib(n - 2);
}

int main()
{
	fib(8);
	printf("%d\n", cnt);
    return 0;
}

每进入fib函数一次,cnt++,所以cnt记录了fib函数的调用次数。

fib(0):1次

fib(1):1次

fib(2)=fib(1)+fib(0):fib(2)本身调用1次,fib(1)调用1次,fib(0)调用1次,总共调用1+1+1=3次

同理:

fib(3)=fib(2)+fib(1):1+3+1=5

fib(4)=fib(3)+fib(2):1+5+3=9

fib(5)=fib(4)+fib(3):1+9+5=15

fib(6)=fib(5)+fib(4):1+15+9=25

fib(7)=fib(6)+fib(5):1+25+15=41

fib(8)=fib(7)+fib(6):1+41+25=67

所以代码的运行结果为67。

8. C99 内联函数

C99引入了内联函数,以inline修饰的函数叫做内联函数,编译时编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

inline int max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;
}

内联函数是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。

一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。很多编译器都不支持内联递归函数,而且一个75行的函数也不大可能在调用点内联地展开。

内联函数声明和定义分离在不同文件中会报错。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐