(一)函数的定义
是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定的任务,相较于其它的代码,具备相对独立性。
一般会有输入参数并有返回值,提供对过程封装和细节的隐藏。这些代码通常被集成为软件库。
#define _CRT_SECURE_NO_WARINGS 1
#include<stdio.h>
int Add(int x, int y)
{
int z;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int sum = Add(a, b);
printf("%d\n", sum);
return 0;
}
(二)C语言中函数的分类:
1.库函数
1.1作用
一些用以描述基础功能,不是业务性的代码;开发的过程中每个程序员都能用上的;可以支持可移植性和提高程序的效率;C语言的基础库为我们提供了一系列类似的库函数。
1.2常用
IO函数(input output)
字符串操作函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其它库函数
1.3怎么使用函数
参照文档,学习使用库函数
MSDN
例如strcpy
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "ak\0";
char arr2[20] = "#########";
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
接收后为ak\0######
,打印是时\0为字符串的结束标志
例如memset
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "hello world";
memset(arr, '*', 5);
printf("%s\n", arr);
return 0;
}
2.自定义函数
2.1函数的基本组成
ret_type fun_name(para1,*)
{
statement; //语句项
}
//ret_type 返回类型
//fun_name 函数名
//para1 函数参数
2.2例子
取最大值
#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)
{
return (x > y) ? (x) : (y);
}
int main()
{
int num1 = 10;
int num2 = 20;
int max = get_max(num1, num2);
printf("max = %d\n", max);
return 0;
}
交换
我们可以先不用函数,了解交换的逻辑:
#include<stdio.h>
int main()
{
int a = 20;
int b = 10;
int tmp = 0;
printf("a=%d b=%d\n", a, b);
tmp = a;
a = b;
b = tmp;
printf("a=%d b=%d\n", a, b);
return 0;
}
那接下来我们可以引入函数解决问题:
void Swap(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
#include<stdio.h>
int main()
{
int a = 20;
int b = 10;
printf("a=%d b=%d\n", a, b);
Swap(a, b);
printf("a=%d b=%d\n", a, b);
return 0;
}
但是,我们发现这样写并没有达到我们设想的效果
这是因为x,y有自己的坐标空间,除了接收a,b的值以外,与a,b无更多联系。(详见(三)_3)
知识预备
#include<stdio.h>
int main()
{
int a = 10;
int* pa = &a;//pa指针变量
*pa = 20;//解引用操作符
printf("%d\n", a);
return 0;
}
通过将a和b的地址传输给x,y;
void Swap(int *x, int *y)
{
int tmp = 0;
tmp = *x;
*x = *y;
*y = tmp;
}
#include<stdio.h>
int main()
{
int a = 20;
int b = 10;
printf("a=%d b=%d\n", a, b);
Swap(&a, &b);
printf("a=%d b=%d\n", a, b);
return 0;
}
任务完成
(三)函数的参数
1.实际参数:
真实传给函数的参数,叫实参。
可以是:常量,变量,表达式,函数等。
//常量
#include<stdio.h>
int find_max(int x, int y)
{
if (x > y)
return x;
else
return y;
}
int main()
{
int a = 10;
int b = 20;
int max = find_max(200,100); //看这里
printf("the max is %d\n", max);
return 0;
}
//变量
#include<stdio.h>
int find_max(int x, int y)
{
if (x > y)
return x;
else
return y;
}
int main()
{
int a = 10;
int b = 20;
int max = find_max(a,b); //看这里
printf("the max is %d\n", max);
return 0;
}
\\表达式
#include<stdio.h>
int find_max(int x, int y)
{
if (x > y)
return x;
else
return y;
}
int main()
{
int a = 10;
int b = 20;
int max = find_max(a,b+1); //看这里
printf("the max is %d\n", max);
return 0;
}
//函数
int find_max(int x, int y)
{
if (x > y)
return x;
else
return y;
}
int main()
{
int a = 10;
int b = 20;
int max = find_max(a,find_max(100,7)); //看这里
printf("the max is %d\n", max);
return 0;
}
无论是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
2.形式参数:
函数名后括号中的变量,只有在函数被调用的时候才实例化(分配内存单元)
形参当函数掉用完后就自动销毁。
void Swap(int *x, int *y) //xy为形式参数
{
int tmp = 0;
tmp = *x;
*x = *y;
*y = tmp;
}
#include<stdio.h>
int main()
{
int a = 20;
int b = 10; //ab为实参
printf("a=%d b=%d\n", a, b);
Swap(&a, &b);
printf("a=%d b=%d\n", a, b);
return 0;
}
3.注意
形参是实参的一份临时拷贝,拥有自己的空间,对形参的修改不会改变实参!
(四)函数调用
1.传值调用(a,b)
函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参。
使用情况:不改变函数外变量,如比大小。
2.传址调用(&a,&b)
把函数外部创建变量的地址传递给函数参数的一种调用函数的方式。
可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
使用情况:要改变函数外变量,比如交换。
3.练习
3.1写一个函数可以判断一个数是不是素数。
#include<stdio.h>
#include<math.h>
int is_prime(int a)
{
int j = 0;
for (j = 2; j <=sqrt(a); j++)
{
if (a % j == 0)
return 0;
}
return 1; //当j超出sqrt(a)时都没找到因数,说明此时i为素数,返回1。
}
int main()
{
int i=0;
for (i = 100;i<=200;i++)
{
if (is_prime(i) == 1)
printf(" %d", i);
}
return 0;
}
3.2判断是不是闰年
#include<stdio.h>
int is_leap_year(int y)
{
if ((y % 4 == 0 && y % 100 != 0 )|| (y % 400 == 0))
return 1;
else
return 0;
}
int main()
{
int year = 0;
for (year = 1000; year <= 2000; year++)
{
if (is_leap_year(year) == 1)
printf("%d ",year);
}
return 0;
}
3.3实现一个整形有序数组中实现二分查找
#include<stdio.h>
int binary_search(int arr[], int k) //此时arr本质上是指针
{
int sz = sizeof(arr) / sizeof(arr[0]);
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int ret=binary_search(arr, k); //数组传输时只传输arr首元素的地址,节约空间
if (ret == -1)
{
printf("找不到指定的数字\n");
}
else
{
printf("找到了,下标是: %d\n", ret);
}
return 0;
}
我们发现带码运行错误,
这是因为数组传输时为了节约空间只传输arr首元素的地址,此时形参的arr相当于指针。
#include<stdio.h>
int binary_search(int arr[], int k,int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int sz = sizeof(arr) / sizeof(arr[0]);
int ret=binary_search(arr, k,sz);
if (ret == -1)
{
printf("找不到指定的数字\n");
}
else
{
printf("找到了,下标是: %d\n", ret);
}
return 0;
}
注:
11行代码 int mid = (left + right) / 2;
不能移除出循环;
循环的判断条件一定是<=;
找不到时要有对应的输出-1
3.4每调用一次函数,num+1
#include<stdio.h>
void add(int* p)
{
(* p)++;
}
int main()
{
int num = 0;
add(&num);
printf("num=%d\n", num);
add(&num);
printf("num=%d\n", num);
add(&num);
printf("num=%d\n", num);
return 0;
}
(五)函数的嵌套调用和链式访问
1.嵌套调用
函数之间可以相互调用
#include<stdio.h>
void new_line()
{
printf("dd\n");
}
void three_new_line()
{
int i = 1;
for (i = 1; i <=3; i++)
{
new_line();
}
}
int main()
{
three_new_line();
return 0;
}
2.链式访问
把一个函数的返回值作为另一个函数的参数。
#include<stdio.h>
int main()
{
int len = 0;
len = strlen("abc");
printf("%d\n", len);
printf("%d\n", strlen("abc"));
return 0;
}
思考以下函数打印什么
#include<stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
先打印43,两个字符,返回2
打印2,一个字符,返回1
打印1,
(六)函数的声明和定义
1.函数的声明
告诉编译器一个函数叫什么,参数是什么,返回类型是什么,但是具体存不存在,函数声明决定不了;
函数的声明一般放在函数前,先声明,后使用;
函数声明一般放在头文件中。
程序需要顺序阅读,当函数放在主体代码后时,需要在前面先声明函数(当然,在一些新的编译器中做出了优化,即使不声明,也可以用函数)。
#include<stdio.h>
//int add(int x, int y);
int main()
{
int a = 10;
int b = 56;
int sum = add(a, b);
printf("%d", sum);
return 0;
}
int add(int x, int y)
{
int z = x + y;
return z;
}
当然,也可以创建一个add.h用来存放声明,创建一个add.c存放函数,以后使用时,在主程序中引用(注意使用双引号)即可。
#ifndef:如果没有定义函数,就定义;如果之前定义过,为假结束,防止重复引用。
2.函数的定义
指函数的具体实现,交代函数的功能实现。
test.h放置函数的声明
test.c放置函数的实现