目录
- 即子程序,是大型程序中某部分代码,由一个或多个语句块组成,通常被集成为软件库;
- 负责完成某项特定任务,相较于其他代码具有独立性;
- 一般会输入参数并返回值,提供对过程的封装和细节的隐藏;
一,函数声明与定义
函数的组成:包含函数名,返回值类型,函数参数及函数体;
//int 返回值
//Add 函数名
// int x/int y 参数
int Add(int x, int y)
{
//函数体
return x + y;
}
函数声明
- 告诉编译器,此函数的返回类型、函数名、参数类型,不关心是否存在;
- 函数要满足先声明后使用;
- 一般放在头文件内;
//头文件Add.h
//函数声明
int Add(int x, int y);
函数定义
- 指函数具体实现的内容;
- 定义时不开辟空间,只有调用时才开辟空间;
- 注:不写函数返回类型,默认是返回int类型;
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int Add(int, int); //如未声明,可能会报Add未定义错误;
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
int Add(int x, int y)
{
return x + y;
}
二,函数的分类
- 库函数
- 自定义函数
库函数
在开发的过程中每个程序员都可能用的到, 为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员 进行软件开发;
常用库函数
- io函数,输入输出函数printf/scanf/getchar/putchar等;
- 字符串操作函数,strcmp/strlen等;
- 字符操作函数,toupper等;
- 内存操作函数,memcpy/memset/memcmp等;
- 时间/日期函数,time等;
- 数学函数等,sqrt/pow等;
注:使用库函数时,需引用对应的头文件,以#include开头;
#include <stdio.h> //printf库函数引用
#include <string.h> //strcpy库函数引用
int main()
{
char str1[10] = { 0 };
char str2[] = "abcde";
printf("%s", strcpy(str1, str2));
}
//结果:abcde
库函数网站
自定义函数
与库函数一样,但是有程序员自己设计的;
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
三,函数参数
- 实际参数,简称实参;
- 形式参数,简称形参;
实际参数
- 直接传递给函数的参数,实参;
- 实参可以是:常量、变量、表达式、函数等。
- 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参;
形式参数
- 是指函数名后括号中的变量;
- 形式参数只有在函数被调用的过程中才实例化(分配 内存单元),所以叫形式参数。
- 当函数调用完成之后,形式参数就自动销毁了;
int Add(int x, int y) //x/y为形参
{
return x + y;
}
int main()
{
int a = 1;
int b = 2;
int c = Add(a, b); //a/b为实参
printf("%d", c);
return 0;
}
注:形参实例化之后其实相当于实参的一份临时拷贝;
四,函数的调用
- 传值调用
- 传址调用
传值调用
- 形参实例化时,将实参的值,赋予给函数的参数;
- 因为函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参;
int Add(int x, int y) //x/y为形参
{
return x + y;
}
int main()
{
int a = 1;
int b = 2;
int c = Add(a, b); //a/b为实参,调用时直接将1赋予x,2赋予给y,即传值调用
printf("%d", c);
return 0;
}
传址调用
- 形参实例化时,将实参的内存地址,赋予给函数的参数;
- 因为函数的形参和实参是同一块内存,对形参的修改会影响实参;
- 即函数内部可直接修改函数外部的变量;
int Add(int* x, int* y) //x/y为形参
{
return *x + *y;
}
int main()
{
int a = 1;
int b = 2;
int c = Add(&a, &b); //a/b为实参,调用时直接将a的地址赋予x,b的地址赋予给y,即传址调用
printf("%d", c);
return 0;
}
void Swap(int* x, int* y) //x/y为形参
{
int tmp = *x;
*x = *y;
*y = tmp;
}
int main()
{
int a = 1;
int b = 2;
Swap(&a, &b); //a/b为实参,调用时直接将a的地址赋予x,b的地址赋予给y,即传址调用
printf("%d %d", a, b); //通过地址可修改函数外变量
return 0;
}
五,函数的嵌套调用和链式访问
函数的嵌套调用
- 函数定义时,可以包含其他已定义好的函数调用,形成嵌套;
- 函数定义时,可以包含自身函数的调用,形成的嵌套即为递归;
//嵌套调用
void Swap(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
int DividSwap(int x, int y)
{
Swap(&x, &y);
return x / y;
}
int main()
{
int a = 1;
int b = 2;
printf("%d", DividSwap(a, b));
return 0;
}
//结果:2
链式访问
- 把一个函数的返回值作为另外一个函数的参数;
int Add(int x, int y)
{
return x + y;
}
void print(int x)
{
printf("%d\n", x);
}
int main()
{
int a = 1;
int b = 2;
print(Add(a, b)); //链式访问
return 0;
}
int main()
{
printf("%d", printf("%d", printf("%d", 43))); //4321
return 0;
}
六,案例
- 判断素数
#include<stdio.h>
#include<math.h>
int is_prime(int num)
{
int i = 0;
for (i = 2; i <= sqrt(num); i++)
{
if (num % i == 0)
return 0;
}
return 1;
}
int main()
{
for (int i = 100; i <= 200; i++)
{
if (is_prime(i))
printf("%d ", i);
}
return 0;
}
- 判断闰年
#include<stdio.h>
int is_leap_year(int n)
{
return n % 4 == 0 && n % 100 != 0 || n % 400 == 0;
}
int main()
{
int year = 0;
for (year = 1000; year < 2000; year++)
{
if (is_leap_year(year))
printf("%d ", year);
}
return 0;
}
- 查找有序数组中的某个数
#include<stdio.h>
int binary_search(int* arr, int key, int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] < key)
left = mid + 1;
else if (arr[mid] > key)
right = mid - 1;
else
return mid;
}
return -1;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int key = 7;
int sz = sizeof arr / sizeof arr[0];
//找到返回下班,找不到返回-1;
printf("%d\n", binary_search(arr, key, sz));
return 0;
}
- 每调用一次函数num加1
void add(int* num)
{
(*num)++;
}
int main()
{
int num = 0;
add(&num);
printf("%d", num);
return 0;
}