一、函数分类
函数定义:一般有输入参数和返回值,对过程进行封装和隐藏。
(一)库函数
- 库函数由编译厂商提供,函数名、输入参数和返回值形式一般相同,内部细节不同。
- 一般使用库函数,必须包含#inclued的头文件,例如#include <stdio.h>。
- 库函数查询:库函数
(二)自定义函数
自定义函数组成:函数名,参数(定义类型),返回值。
- 交换数字
void swap(int *a, int *b)
{
int n = 0;
n = *a;
*a = *b;
*b = n;
}
int main()
{
int a,b = 0;
while (scanf("%d %d", &a, &b) != EOF)
{
swap(&a,&b);
printf("%d %d\n", a, b);
}
return 0;
}
因为形参是实参的一份临时拷贝,出函数就会销毁,如果不传地址,改变不了值。
- 判断闰年
int is_year(int n)
{
return ((n % 400 == 0) || (n % 4 == 0 && n % 100 != 0)) ? 1 : 0;
}
int main()
{
int a,b = 0;
while (scanf("%d", &a) != EOF)
{
is_year(a)? printf("是\n"): printf("否\n");
}
return 0;
}
这里用到了三目操作符,形式:(x > y) ? x : y
。
- 判断素数
void is_prime(int n)
{
int i,flag = 1;
for (i = 2; i <= sqrt(n); i++)
{
if (n % i == 0)
{
flag = 0;
break;
}
}
if (flag == 1)
{
printf("%d ", n);
}
}
int main()
{
int n = 100;
for (n = 100; n <= 200; n++)
{
is_prime(n);
}
return 0;
}
二、函数参数的调用
(一)实参
实参(真实传递给函数的参数)类型:常量、变量、表达式、函数,必须要有确定的值,必须和函数定义时的参数类型一致。
(二)形参
形参在函数调用时才会分配内存单一,出函数就会被销毁,形参和实参占有不同内存块,对形参的修改不会影响实参。
(三)函数调用
- 传值调用(对实参的值进行拷贝,函数调用时,才被分配内存单元)。
- 传址调用(内存地址传递给函数参数)
(四)嵌套调用和链式访问
- 嵌套调用:在函数内部调用另一个函数,注意不能嵌套定义,即不能在函数内部定义另一个函数。
void compare(int a, int b)
{
int n = (a > b) ? a : b;
printf("%d", n);
}
int main()
{
int a, b = 0;
scanf("%d %d", &a, &b);
compare(a, b);
return 0;
}
在compare这个函数中,调用了printf()函数,这个就是嵌套调用。
- 链式访问:把一个函数的返回值作为另一个函数的参数。
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
printf()函数先打印数字,返回值为打印数字的个数,最后结果是4321。
三、函数的声明和定义
(一)分文件书写
- 函数的声明:告诉编译器函数名称,参数类型,返回类型。
- 函数的定义:定义函数具体的使用内容。
- 函数先声明后使用。
- 一般采用分文件的书写形式,主要为了协作和代码保护,例子如下:
头文件即声明文件(.h)
#pragma once
void print_int(int n);
函数实现文件(.c)
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS 1
void print_int(int n)
{
if (n > 9)
print_int(n / 10);
printf("%d ", n % 10);
}
主函数所在文件(.c),注意要包含声明.h文件。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "print_int.h"
int main()
{
print_int(234);
return 0;
}
(二)静态库函数调用
为了实现对代码的保护,将解决方案生成改为静态库,静态库可以隐藏代码内容。
- 点击解决方案属性,将配置类型改为静态库。
- 将生成的**静态库(.lib)和声明文件(.h)**复制到新的项目中(类似出售所写的函数)。
- 在新项目中创建主函数(.c),包含静态库
#pragma comment(lib,"test_20210715.lib")
,即可调用隐藏代码的函数。
#include <stdio.h>
#pragma comment(lib,"test_20210715.lib")
int main()
{
print_int(234);
return 0;
}
三、函数递归
(一)什么是递归
函数在定义中有直接或间接调用自身,将比较复杂的问题转化为较小的问题。
由于函数调用需要创建空间,函数递归较深,容易导致溢出。
(二)递归条件
- 存在限制,当满足条件,函数不再递归。
- 每次递归越来越接近这个限制。
(三)经典代码
- 写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和。
例如,调用DigitSum(1729),则应该返回1+7+2+9,它的和是19
输入:1729,输出:19
int sum(int n)
{
if (n <= 9)
return n;
else
return n % 10 + sum(n / 10);
}
int main()
{
int num = 0;
scanf("%d", &num);
printf("%d", sum(num));
return 0;
}
- 递归和非递归分别实现strlen。
size_t strlen(char* arr)
{
if (*(arr + 1) == '\0')
return 1;
else
return 1 + strlen(arr + 1);
}
int strlen2(char* arr)
{
int i = 1;
while (*(arr + 1) != '\0')
{
i++;
arr = arr + 1;
}
return i;
}
int main()
{
char arr[] = "abcdefgh";
printf("%zd\n", strlen(arr));
printf("%d", strlen2(arr));
return 0;
}
- 递归和非递归分别实现求n的阶乘(不考虑溢出的问题)。
int fac(int n)
{
if (n == 1)
return 1;
else
return n * fac(n - 1);
}
int fac2(int n)
{
int count = 1;
for (int i = 2; i <= n; i++)
{
count = count * i;
}
return count;
}
int main()
{
printf("%d\n", fac(3));
printf("%d", fac2(3));
return 0;
}