1. 什么是函数?
- 函数也叫做子程序,是某部分代码,负责完成某部分代码,具有相对独立性。一般会输入参数并有返回值。
- c语言函数的分类:① 库函数 ② 自定义函数
库函数:C语言本身提供给我们的函数,例如:printf( ), scanf( ) ...可以在cplusplus.com/cppreference.com中找到C语言所有的库函数相关信息。C语言常用的库函数有:
- IO函数
- 字符串操作函数 eg. strcpy( ) -- string copy -- 字符串拷贝
- 内存操作函数,eg. memset( ) -- memory set -- 内存设置
- 时间/日期函数
- 数学函数
- 其他库函数
如何利用文档学习呢?举两个例子
① strcpy():利用以上网址了解到strcpy()函数的相关信息,例如:如何使用、参数类型...在查阅文档时,我们发现参数放的是目的地与源头地,可以通俗理解为从后往前传。参数类型为char,传参内容包含\0。具体信息如下。
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "bit";
char arr2[] = "######";
strcpy(arr2,arr1);
printf("%s\n",arr2);
return 0;
}
② memset( ): 分析方法与strcpy()相同,此处不过多赘述。如下。
#include<stdio.h>
#include<string.h>
int main()
{
//memset()
char arr[] = "hello world";
memset(arr,'*',5);
printf("%s\n",arr);
return 0;
}
自定义函数:程序员自己编写的函数程序。函数命名规范如下。
#include<stdio.h>
int get_max(int x, int y)
{
if (x > y)
{
return x;
}else return y;
}
int main()
{
int a = 10;
int b = 20;
int max = get_max(a,b);
printf("%d\n",max);
return 0;
}
练习1: 写一个函数交换两个整型变量的内容。先做尝试,参考程序在文章结尾。
函数参数:实参(实际参数)和形参(形式参数)。实参是指在主函数中调用函数输入的参数。形参是在自定义函数构建中所使用的参数,形式参数只有在函数被调用的时候才实例化,调用完后就会自动销毁。
函数调用:传值调用和传址调用,具体使用可以看练习1。
练习2:
- 写一个函数,判断一个数是否为素数。
- 写一个函数,判断是否为闰年
- 写一个函数,实现在整型有序数组中的二分查找
- 写一个函数,每调用一次函数就实现num+1
练习2中主要对3进行详细解析。1.2和前面文章的练习一样,不过多赘述。4中需要注意的是函数调用方式,分清楚是传值还是传址。
函数的嵌套调用和链式访问
- 嵌套调用:函数与函数可以有机组合
#include<stdio.h> void new_line() { printf("hehe\n"); } void three_line() { for (int i = 0; i < 3; i++) { new_line(); } } int main() { three_line(); return 0; }
- 链式访问:把一个函数的返回值作为另外一个函数的参数
int len = 0; len = strlen("abc"); printf("%d\n",len); //链式访问 printf("%d\n",strlen("abc"));
练习3:以下代码的打印结果是什么?
printf("%d",printf("%d",printf("%d",43)));
分析:该语句使用了嵌套调用,打印的是printf()的返回值。那我们就需要知道printf()的返回值是什么?通过上述网址,我们可以轻易的查到printf()返回值为所打印字符的个数。
2. 函数的声明和定义
函数声明:
- 如果函数放在主函数后面,需要函数声明。不要忘记后面的分号。
- 函数源文件(.c)+头文件(.h)构成一个函数模块,要在头文件中声明函数,在源文件中做函数定义。在主程序源文件中引入#include“。。。.h”包含一下即可,(此处自己定义的头文件用双引号,库函数用<>)
- 函数声明一般要在函数使用之前,先声明再使用。
函数定义:函数功能的具体实现。
在头文件中要声明。#ifndef——if no define + 文件名;以下语句就是表示如果没有定义文件名,就定义。结尾加#endif
练习1参考程序:
分析:看到交换变量内容,首先想到的就是前面练习中出现的排序算法。根据这个思路就可以写出函数。如下例所示。实际操作后可以发现,其实是有问题的。原因就是自定义函数中参数与主函数中参数不共用一个地址,这就导致,在自定义函数中开辟一处地址并完成了内容交换,但实际主函数中的调用参数却没有改变。
#include<stdio.h>
void swap(int x, int y)
{
int temp = 0;
temp = x;
x = y;
y = temp;
}
int main()
{
//写一个函数可以交换两个整型变量的内容
int a = 10;
int b = 20;
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的值没有关系,那改正就需要把两者建立联系。
#include<stdio.h>
void swap2(int* pa, int* pb)
{
int temp = 0;
temp = *pa;
*pa = *pb;
*pb = temp;
}
int main()
{
//把x,y和a,b是相同地址
int a = 10;
int b = 20;
//函数调用--传址调用
swap2(&a,&b);
printf("\na = %d,b = %d",a,b);
return 0;
}
练习2:写一个函数,实现在整型有序数组中的二分查找
分析:按照之前练习中的程序,可以很容易的写出代码,但此处需要注意的是程序报错的原因,以及此处的arr是个指针,所以不能再函数中用常规的方式计算数组的长度。而且在主函数中调用的arr传递的是首元素地址。进行如下改进。
#include<stdio.h>
int Binary_search(int arr[], int k, int sz)
{
int left = 0;
//int sz = sizeof(arr)/sizeof(arr[0]);
int right = sz - 1;
while(left <= right)
{
int mid = (left + right)/2;
if (k > arr[mid])
{
left = mid + 1;
}
else if (k < arr[mid])
{
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]);
int ret = Binary_search(arr,key,sz);
if(ret == -1)
{
printf("找不到\n");
}else
{
printf("找到了,下标是%d\n",ret);
}
return 0;
}