C语言之函数进击
一、函数是啥
在计算机科学中,函数就是一个大型程序中的某部分代码,由一个或多个语句块组成,负责完成牟星特定任务,具备相对的独立性,各个函数的存在使得代码变得井井有条,可读性较高。
二、库函数
为了支持可移植性和提高程序的效率,一些程序员都可能用得到的基础功能就会被C语言的基础库中一系列类似的库函数所提供,方便进行软件开发。倘若没有库函数,那每个程序员都要整一套自己的基础功能函数,会加快秃头速度👱👱♀
想要了解库函数,必要的软件少不了,在这里博主向大家强烈推荐以下网站或者软件
http://en.cppreference.com (如果想要将该英文网站改成中文的模式,将en改成zh即可)
以及MSDN(Microsoft Developer Network)
话不多说,咱来了解库函数先,简单总结,库函数的类型大致分为这几种
- IO函数(即输入输出函数) printf scanf getchar putchar
- 字符串操作函数 strcmp strlen
- 字符操作函数 toupper
- 内存操作函数 memcpy memcmp memset
- 时间/日期函数 time
- 数学函数 sqrt pow
- 其他库函数 …
注:库函数的使用必须包含#include对应的头文件,毕竟用了别人的库函数总得要打声招呼先
举个栗子✒️
strcpy (字符串复制函数)
代码举例:
#include <stdio.h>
#include <string.h>//strcpy函数的头文件
int main()
{
char arr1[20] = { 0 };
char arr2[20] = "Fighting!";
strcpy(arr1,arr2);//将arr2里的内容复制到arr1当中
printf("arr1:%s\n",arr1);//打印arr1
printf("arr2:%s\n",arr2);//打印arr2
return 0;
}
代码效果:
memset (内存初始化函数)
代码举例:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "Fighting!";
printf("arr:%s\n",arr);//原来的arr打印
memset(arr,'*',5);//将arr前五个字节替换为'*'
printf("arr:%s\n",arr);//后来的arr打印
return 0;
}
代码效果:
三、自定义函数
但是库函数无法解决所有问题,因为这要是啥都能干,程序员就要失业了
因此在这里更加重要的是自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数。 但是不一样的是这些都是我们自己来设 计,这给程序员一个很大的发挥空间。
函数语法:
ret_type fun_name(para1, * )
{
statement;//语句项,也就是函数的主体
}
ret_type 返回类型
fun_name 函数名
para1 函数参数,可以是多个,用,隔开,在这里我用*代表其他参数
举个栗子✒️
写一个函数可以找出俩个整数中的最大值
int get_max(int x, int y)
//x和y是形式参数
{
return (x > y)?(x):(y);
//这里用到了条件操作符,x大于y吗,如果是真的返回x,假的返回y
}
int main()
{
int a = 10;
int b = 20;
//函数的调用
int max = get_max(a,b);//a和b是实参
printf("max = %d\n", max);
return 0;
}
代码效果:
实参?形参?
实际参数:真实传给函数的参数叫做实参(就比如本题目中的a,b),其类型包括常量、变量、表达式、函数等。不管是哪种类型的,在进行函数调用时,它们必须有确定的值,一遍吧这些值传送给形参
形式参数:函数名后括号中的变量就叫做形参(就比如本题目中的x,y),形参在函数被调用的过程中才会分配内存单元,当函数调用完成后就会自动销毁了,所以形参只在函数中有效
写一个函数可以交换俩整型变量的内容
传值调用?传址调用?
#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
void Swap2(int *px, int *py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int num1 = 1;
int num2 = 2;
Swap1(num1, num2);//传值调用,实参传给形参,其实形参是实参的一份临时拷贝
printf("Swap1:num1 = %d num2 = %d\n", num1, num2);
Swap2(&num1, &num2);//传址调用
printf("Swap2:num1 = %d num2 = %d\n", num1, num2);
return 0;
}
代码效果:
从这个结果中,深刻了解到传值调用和传址调用区别还是非常大的
函数解析:
因此
Swap1函数进行传值函数,只是将num1和num2的值传入了,形参x和y接收了值,通过中间变量tmp交换了x和y的值,然而形参x和y在函数调用结束后就会被销毁,所以num1和num2的值并没有被改变。
Swap2函数进行传址调用,将num1和num2的地址传给了整形指针变量px和py,通过中间变量tmp交换了px和py里的数值,也就是交换了num1和num2的值,所以俩整形变量里的内容交换任务得以实现
四、函数的嵌套调用和链式访问
函数与函数之间是可以有机结合的
嵌套调用
举个栗子✒️
#include <stdio.h>
void new_line()
{
printf("加油\n");
//在new_line()函数中打印加油二字
}
void three_line()
{
int i = 0;
for(i=0; i<3; i++)
{
new_line();
//在three_line()函数中嵌套调用new_line()函数
}
}
int main()
{
three_line();
//在main()函数中嵌套调用three_line()函数
return 0;
}
代码效果:
链式访问
把函数的返回值作为另一个函数的参数
举个栗子✒️
代码1:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[20] = "Hello";
int ret = strlen(strcat(arr,"world"));
//strcat函数调用后,返回arr里的内容"Helloworld"
//strlen函数调用后,返回字符串的字符个数10
printf("%d\n",ret);
return 0;
}
代码效果:
说明:
strlen函数是计算字符串长度的函数
size_t strlen( const char *string )
从字符串string的某个位置(可以是字符串的开头,中间位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符’\0’为止,然后返回计数器size_t的值(不包括’\0’)
其头文件是#include <string.h>
strcat函数是字符串连接函数
char strcat(char *strDestination , const char*strSource)
把strSource所指向的字符串(包括’\0’)复制到strDestination所指向的字符串后面(删除*strDestination原来末尾的’\0’),要保证*strDestination足够的长,以容纳被复制进来的*strSource,返回指向strDestination的指针的内容
其头文件是#include <string.h>
代码2:
#include <stdio.h>
int main()
{
printf("%d",printf("%d",printf("%d",43)));
//疯狂套娃之中
//先输出43
//因为43的字符个数是2,所以再输出2
//因为2的字符个数是1,所以再输出1
return 0;
}
代码效果:
说明:
printf函数是格式输出函数
int printf(const char *format [, argument]…)
该函数返回一个int类型的数值,代表输出的字符总数
其头文件是#include <stdio.h>
五、函数的声明和定义
函数声明
- 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。其他具体是不是存在,无关紧要。
- 函数的声明要满足先声明后使用。
- 函数的声明一般要放在头文件中。
int Add(int x,int y);
函数定义
函数的定义是指函数的具体实现,交代函数的功能实现
#include "test.h"
//在头文件里声明了Add函数,引用自己的头文件要用""而不是<>
int Add(int x,int y)
{
//Add函数的实现
return x + y;
}
六、函数递归
什么是函数递归!请听下回讲解…