一.函数是什么
维基百科中对函数的定义:子程序
1.在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,
subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组
成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
2.一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软
件库。
二.C语言中函数的分类
1.库函数
2.自定义函数
1.库函数
1.库函数是c语言库内已经写好的函数,只要我们引入相应的头文件就可以直接使用,比如在编写完一个代码后想打印在屏幕上看看,这个时候我们会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上(printf)。
2.我们会频繁的做一些字符串的拷贝工作(strcpy).
3.计算某个数的n次方(pow)。
像上面非业务性的代码,我们在编写代码的时候用的都比较频繁,为了支持可移植性和提高程序的效率,所以C语言的基础库(标准库)中提供了一系列类似的库函数,方便程序员进行软件开发。
C语言中常用的库函数有:
IO函数:即输入()input输出(output)函数
字符串操作函数:strlen、strcmp
字符操作函数:大小写转换、字符分类
内存操作函数:memcpy、memmove、memset…
时间/日期函数:time
数学函数:pow、sqrt
其他库函数
使用库函数必须要包含它所对应的头文件
接下来我们来学习几个常用的库函数,掌握库函数的使用方法:
(1)strcpy()
char * strcpy ( char * destination, const char * source );
以上是strcpy()函数的函数原型,具体包含了:
1.返回类型:char*
2.函数名:strcpy
3.函数参数及其类型:两个参数,类型均为char* ,const是一个修饰符,具体含义之后会有总结
strcpy()函数的功能:将source内的字符串拷贝到destination字符数组中,包含‘\0’.
用以下代码来演示:
#include <stdio.h> #include <string.h> int main() { char arr1[20] = { 0 }; char arr2[] = "hello world"; printf("%s\n", arr1);//拷贝前arr1内为空,什么都没有 //将arr2中的字符串拷贝到arr1中 strcpy(arr1, arr2);//arr1就是destination,arr2就是source,都表示的是字符的首地址 printf("%s\n", arr1); return 0; }
具体的实现细节:
代码结果:
(2)memset()
void * memset ( void * ptr, int value, size_t num );
1.返回类型:void*
2.函数名:memset
3.函数参数及其类型:三个参数,类型分别为void* 、int、size_t
memset()函数的功能为:把ptr指向的这块空间的前num个字节的数据设置成value这个值。
如下参数介绍:
返回值介绍:
简单来说就是设置内存,把一个字符串原本的内容覆盖掉,设置成我们想要的字符,比如将hello的前三个字符设置成字符*,如下代码:
#include <stdio.h> #include <string.h> int main() { char arr[20] = "hello"; printf("%s\n", arr);//设置前 memset(arr, '*', 3); printf("%s\n", arr);//设置后 return 0; }
代码结果:
2.自定义函数
作为一个程序员,其实更重要的是自定义函数,我们需要自己设计函数名、参数、返回类型,需要我们自己完成函数体的具体功能,比如我们写一个函数能够找出两个数的最大值:
(1)找较大值
#include <stdio.h> int get_Max(int a, int b) { //一般方法 /*if (a > b) { return a; } else { return b; }*/ return a > b ? a : b;//比较大小,当a大于b返回a,否则返回b } int main() { int a = 0; int b = 0; printf("请输入两个数:>"); scanf("%d %d", &a, &b); int ret = get_Max(a, b); printf("%d较大\n", ret); return 0; }
这里我们自己输入两个整数,将这两个整数作为函数的参数,返回两个数中的较大值,所以函数的参数类型为整型,返回类型也为整型,用到了两种基本的方法,具体可以参照注释理解。
以5和10进行验证:
(2)void的意义
void在不同的地方究竟有什么不同的含义呢?我们观察以下代码:
#include <stdio.h> void test(void) { printf("hello\n"); } int main() { test(); return 0; }
其实很容易理解,其中第一个void表示的是该函数执行完后什么都不需要返回,第二个void表示该函数不需要、不能传任何参数。
(3)交换两个数
我们输入两个整数,调用一个函数后交换这两个数:
错误代码:
#include <stdio.h> void Swap(int m, int n) { int tmp = m; m = n; n = tmp; } int main() { int a = 0; int b = 0; printf("请输入两个数:>"); scanf("%d %d", &a, &b); printf("交换前:a=%d b=%d", a, b); Swap(a, b); return 0; }
我们将两个数传过去后,再打印两个数发现并没有改变,我们的思路没有问题,但却没有实现相应的功能。
其实在这里我们要注意一点:当函数调用的时候,实参传递给形参,形参是实参的一份临时拷贝,改变形参不影响实参。
我们来看看这段代码不成功的原因:
我们看到实际参数的两个数分别有各自的地址,形式参数同样有各自的地址,在调用这个函数的时候,只是把实际参数临时拷贝到形式参数中,Swap函数内部完成了两个数的交换,但实际参数并没有交换,因为这是两块独立的空间,互不干扰。
这时的结果为:
正确代码如下:
#include <stdio.h> Swap(int* m, int* n) { int tmp = *m; *m = *n; *n = tmp; } int main() { int a = 0; int b = 0; printf("请输入两个数:>"); 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; }
我们注意到,在传参的时候,实参前加了个&,函数形参类型也变为了int*,这是因为在进行函数操作的时候我们需要改变参数的内容,如果只把两个数值传给Swap函数,在执行Swap函数时虽然交换了两个数的值,但当函数执行完的一瞬间,它所执行的一系列操作都会返回给系统,相当于交换后又被销毁了,所以我们需要传两个数的地址,用int型的指针来接收,在交换的时候,用(解引用符号)*来进行交换,这样,即使函数调用完后被销毁,两个数地址内的内容已经被交换,函数的功能也已经实现了。
我们看一下最后的结果:
三、求素数
求100-200间的素数
代码如下:
#include <stdio.h> #include <math.h> int is_prime(int m) { int j = 0; for (j = 2; j <= sqrt(m); j++) { if (0 == m % j) { return 0; } } return 1; } int main() { int i = 0; for (i = 100; i <= 200; i++) { if (is_prime(i)) { printf("%d ", i); } } printf("\n"); return 0; }
我们用了一个循环来实现,从100-200之间,每个数都进行判断,函数的实现功能是:是素数,返回1,不是素数返回0。而素数的判断条件是:从2到这个数开平方,只要这个数能被整除,就不是素数,否则就是素数。
运行结果:
return和break:
按上述代码来看,return是直接返回,而break是跳出循环,return比break更直接、彻底。
四、判断闰年
找出1000-2000年是闰年的并打印出来
代码如下:
#include <stdio.h> int is_leap_year(int m) { if (0 == m % 4 && 0 != m % 100 || 0 == m % 400) return 1; else return 0; } int main() { int i = 0; for (i = 1000; i <= 2000; i++) { if (is_leap_year(i)) { printf("%d ", i); } } printf("\n"); return 0; }
同样使用了for循环来遍历1000-2000,逐一进行判断,而闰年的判定规则是:能被4整除且不能被100整除或能被400整除,是闰年返回1,否则返回0。
运行结果:
五、用函数完成二分查找
写一个函数,完成二分查找
1.找到了,返回下标
2.找不到,返回-1
代码如下:
#include <stdio.h> int binary_search(int arr[], int k, int sz) { int left = 0; int right = sz - 1; while (1) { int mid = (left + right) / 2; if (arr[mid] > k) { right = mid - 1; } else if (arr[mid] < k) { left = 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 len = sizeof(arr)/sizeof(arr[0]);//获取元素个数 int ret = binary_search(arr, k, len); if (-1 == ret) { printf("找不到!\n"); } else { printf("找到了,下标是%d\n", ret); } return 0; }
我们可以看出,这跟之前的简单二分查找思路基本一样,只是增加了一些参数。
运行结果:
sizeof的基本应用
我们先看一下下面的代码:
int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; printf("%d\n", sizeof(arr));//40 printf("%d\n", sizeof(arr[0]));//4 int len = sizeof(arr)/sizeof(arr[0]);//10
上面的sizeof(arr)是计算整个数组的大小,单位是字节,而sizeof(arr[0])计算的是数组中一个数的大小,单位是字节,arr[]是整型数组,内有10个元素,所以按字节来看就是40个字节,arr[0]是数组中一个元素的大小,一个整型大小为4个字节,所以我们可以通过sizeof(arr)/sizeof(arr[0])的方式来获取这个数组内的元素个数。