一、函数的概念
函数就是一段封装好的,可以重复使用的代码,它使得我们的程序更加模块化,不需要编写
大量重复的代码。
二、函数的分类
库函数和自定义函数
(1)库函数
常用的库函数有:
- IO函数
- 字符串操作函数
- 字符操作函数
- 时间/日期函数
- 数学函数
- 内存操作函数
strcpy函数的作用时拷贝字符串.
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "Hello";
char str2[20] = "##############";
strcpy(str2,str1);
printf("%s\n",str2);
}
(2)自定义函数
由于库函数不能干所有的事情,所以更加重要的是自定义函数 。
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
函数的组成:
***自定义函数***
ret_type fun_name(para1,*)
{
statement; // 函数体
}
ret_type 返回类型
fun_name 函数名
para1 函数参数
示例:
// 写一个函数可以找出两个整数中的最大值
// 定义函数
int getMax(int x, int y) {
int max = x;
运行结果:
if (x < y) {
max = y;
}
return max;
}
/*
返回值类型: int
函数名: getMax
参数(2个): int x,int y
*/
#include <stdio.h>
int main() {
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int m = getMax(a, b);
printf("%d\n", m);
return 0;
}
示例2:写一个函数可以交换两个整型变量的内容
#define _CRT_SECURE_NO_WARNINGS 1
// 使用指针变量,*p_x 解引用操作
void swap(int *p_x, int *p_y) {
int temp = 0;
temp = *p_x;
*p_x = *p_y;
*p_y = temp;
}
#include <stdio.h>
int main() {
int a = 0, b = 0;
scanf("%d %d", &a, &b);
// 交换两个变量
printf("交换前a = %d, b = %d\n", a, b);
swap(&a, &b); //传址调用
printf("交换后a = %d, b = %d\n", a, b);
return 0;
}
三、函数的参数
实际参数(实参):
真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,都必须有确定的值,以便把这些值传送给形参。
形式参数(形参):
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
我们可以简单的认为: 形参实例化之后其实相当于实参的一份临时拷贝 。
四、函数的调用
1.传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
2.传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
3.练习
(1)判断一个数是否是素数
int isPrime(int n) {
// 即只能被1或者自身整除的自然数(不包括1),称为素数/质数。
for (int i = 2; i < n; i++) {
if (n % i == 0) {
return 0;
}
}
return n == 1 ? 0 : 1;
}
(2)判断一个年份是否是闰年
闰年的判断条件:1、年份能被4整除,不能被100整除;2、年份能被400整除
int isLeapYear(int year) {
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
return 1;
}
return 0;
}
(3)打印从1~100之间所有的素数
int isPrime(int n) {
// 即只能被1或者自身整除的自然数(不包括1),称为素数/质数。
//for (int i = 2; i < n; i++) {
for (int i = 2; i <= n/2; i++) {
if (n % i == 0) {
return 0;
}
}
return n == 1 ? 0 : 1;
}
int main() {
for (int i = 1; i <= 100; i++) {
if (isPrime(i)) {
printf("%d\n", i);
}
}
}
(4)实现一个整型有序数组的二分法查找
int arr[9] = { 3,6,8,11,13,17,28,39,40 };
// *****此处传递的是首元素的地址
int index = binarySearch(arr, 13);
// 二分查找法
//int binarySearch(int p_arr[], int num, int length) {
int binarySearch(int* p_arr, int num, int length) {
int left = 0;
int right = length - 1;
while (left <= right) {
int middle = left + (right - left) / 2;
if (p_arr[middle] == num) {
return middle;
}
else if (p_arr[middle] > num) {
right = middle - 1;
}
else {
left = middle + 1;
}
}
if (left > right) {
printf("在数组中不存在数字%d\n", num);
}
return -1;
}
#include <stdio.h>
int main() {
int arr[9] = { 3,6,8,11,13,17,28,39,40 };
int length = sizeof(arr) / sizeof(arr[0]);
// *****此处传递的是首元素的地址
int index = binarySearch(arr, 13, length);
printf("%d", index);
}
五、函数的嵌套调用和链式访问
嵌套调用: 函数中可以调用其它函数
void print() {
printf("打印函数\n");
}
void show() {
for (int i = 0; i < 3;i++) {
print();
}
}
#include <stdio.h>
int main() {
链式访问:把一个函数的返回值作为另外一个函数的参数
show();
}
链式访问:把一个函数的返回值作为另外一个函数的参数
#include <string.h>
#include <stdio.h>
int main() {
int len = strlen("Hello");
printf("%d",len);
// 链式访问
printf("%d",strlen("Hello"));
}
六、函数的声明与定义
函数的声明
1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3. 函数的声明一般要放在头文件中的。
函数的定义
函数的定义是指函数的具体实现,交待函数的功能实现。
若函数的定义放在主函数前面,可以不用进行函数的声明,例如:
// 函数的定义
int add(int a,int b) {
return a + b;
}
#include <stdio.h>
int main() {
int a = 0;
int b = 0;
scanf("%d %d",&a, &b);
// 加法
int sum = add(a,b);
printf("%d",sum);
return 0;
}
但如果函数的定义放在主函数之后,一定要在函数前进行函数声明,否则编译器会发出警告。
#include <stdio.h>
// 函数声明
int add(int,int);
int main() {
int a = 0;
int b = 0;
scanf("%d %d",&a, &b);
// 加法,函数调用
int sum = add(a,b);
printf("%d",sum);
return 0;
}
函数的声明一定要放在使用之前。可以在文件最开始之处声明,也可以在函数中进行声明,在函数中声明的其它函数,只能该函数中使用。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void fun();// 文件开始之处声明
int main() {
void fun(); // 函数中声明函数
fun();
return 0;
}
void fun() {
printf("定义了一个函数");
}