目录
臨前言
臨1.函数与函数参数
臨1.1函数是什么
臨1.2函数的分类
臨1.2.1库函数
臨1.2.2自定义函数
臨1.3函数的结构
臨1.3.1函数声明
臨1.3.2函数定义
臨1.4函数的参数
臨1.4.1引例:两数交换
臨1.4.2 实际参数(实参)
臨1.4.3 形式参数(形参)
臨1.4.4总结
臨1.5函数调用
臨1.5.1传值调用
臨1.5.2传址调用
臨1.5.3sizeof相关
臨1.5.4函数嵌套
臨1.5.5链式访问
臨1.5.6注意
臨2.函数递归
臨3.函数栈帧问题
臨敬请期待更好的作品吧
臨前言
数学中我们常见到函数的概念。但是你了解C语言中的函数吗?
本文主要分享个人关于C语言函数的学习见解,水平有限,难免会有纰漏,读者各取所需即可。
给你点赞,加油加油!
数学中我们常见到函数的概念。但是你了解C语言中的函数吗?
本文主要分享个人关于C语言函数的学习见解,水平有限,难免会有纰漏,读者各取所需即可。
臨1.函数与函数参数
臨1.1函数是什么
维基百科中对函数的定义:子程序
在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组成。
它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
维基百科中对函数的定义:子程序
在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组成。
它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
臨1.2函数的分类
臨1.2.1库函数
有一些功能可能会频繁地使用到,并且代码相对不太复杂,为了方便就把对应代码封装起来,集成起来,形成所谓的库,里面的函数就是库函数。
我们在开发的过程中每个程序员都可能用的到, 为了支持可移植性和提高程序的效率,C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
臨1.2.2自定义函数
用户自己根据需求设计定义的函数。库函数无法支持所有需求。
函数要具有足够的独立性和特异性,功能要明确专一,比如说判断一个数是否是素数,那就单纯判断,不要加上打印之类的额外功能,以提高函数可移植性和普适性。
臨1.3函数的结构
整体情况如图:
臨1.3.1函数声明
1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3. 函数的声明一般要放在头文件中的。
臨1.3.2函数定义
1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3. 函数的声明一般要放在头文件中的。
告知编译器函数具体实现什么样的功能,函数头与函数声明类同,而函数体则放入相关代码指令。
臨1.4函数的参数
臨1.4.1引例:两数交换
#include
//实现成函数,但是不能完成任务
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 = %dn", num1, num2);
Swap2(&num1, &num2);
printf("Swap2::num1 = %d num2 = %dn", num1, num2);
return 0;
}
#include//实现成函数,但是不能完成任务 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 = %dn", num1, num2); Swap2(&num1, &num2); printf("Swap2::num1 = %d num2 = %dn", num1, num2); return 0; }
分析:
Swap1()中的x,y变量是被另外开辟的内存空间存放的,与main()中的a,b地址不同,它们之间唯一的联系就是x,y拷贝了a,b的值,而对x,y的修改自然也就不会影响a,b了。
Swap2()中对指针px和py解引用,由于分别存放的是a和b的地址,所以间接访问到了a和b,对其进行操作会改变a和b的值,也就可以实现两数交换。
臨1.4.2 实际参数(实参)
真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形
参。
臨1.4.3 形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。
形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
臨1.4.4总结
当实参传递值给形参的时候,形参是实参的一份临时拷贝,此时对形参的修改不会影响实参。
当实参传递值给形参的时候,形参是实参的一份临时拷贝,此时对形参的修改不会影响实参。
一个工程项目里可以有多个.c文件,但是只能有一个main()。
一般怎么引用变量的值?
直接引用:a = 20;
间接引用:*p = 20;
我们就可以通过对指针的解引用操作来改变变量的值
臨1.5函数调用
函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。也就是在一个函数的定义里面用一下别的函数。
臨1.5.1传值调用
将实参的值拷贝一份到形参上,传递的是值。
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
臨1.5.2传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操
作函数外部的变量。
需要对函数外的变量进行操作改变的时候一般考虑传址引用。
臨1.5.3sizeof相关
用sizeof()计算主调函数数组大小时不要在被调函数内计算,应因为传递数组其实就是传数组首元素地址,本质上是指针,计算得到的结果是4或8,而不是整个数组。
应该先在main函数里面算出数组大小后再作为参数传入函数。
例子:二分查找的函数设计
int BinarySearch(int* a, int del, int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (del > a[mid])
{
left = mid;
}
else if (del < a[mid])
{
right = mid;
}
else
{
return mid;//找到目标并返回对应数组下标
}
}
return -1;//找不到目标
}
少用全局变量,安全性较低。
臨1.5.4函数嵌套
函数的调用也可以“套娃”,也就是嵌套调用。
int add(int x, int y)
{
return x + y;
}
int print(int n)
{
int a = 10;
int b = 20;
int c = add(a, b);
printf("%dn", c);
}
int main()
{
print();
return 0;
}
而函数间的地位平等,允许互相之间嵌套调用,但不允许嵌套定义。
臨1.5.5链式访问
把一个函数的返回值作为另外一个函数的参数。
例子:
#includeint main() { printf("%d", printf("%d", printf("%d", 43))); //结果是啥? //注:printf函数的返回值是打印在屏幕上字符的个数 return 0; }
结果是:4321
臨1.5.6注意
函数原型不要漏了返回值类型,虽然说不报错能正常运行,但是非常不好。不写上返回值类型会默认返回值类型为int。
写了返回值类型(非void)但是函数没有写上return语句返回值的话,会自动返回最后一条指令执行的结果(但是只是部分编译器会这样做)。
函数原型中没有写明参数类型的话默认可以接收任何类型,如果这时候还传值进函数的话没有意义,因为无法使用传进去的值。
函数原型中圆括号中写上void就能明确该函数没有参数,传值的话会报警告。
数组名一般代表首元素地址,在传递数组时可以直接传数组名,函数形参用数组或指针接收都没问题。
函数原型不要漏了返回值类型,虽然说不报错能正常运行,但是非常不好。不写上返回值类型会默认返回值类型为int。
写了返回值类型(非void)但是函数没有写上return语句返回值的话,会自动返回最后一条指令执行的结果(但是只是部分编译器会这样做)。
函数原型中没有写明参数类型的话默认可以接收任何类型,如果这时候还传值进函数的话没有意义,因为无法使用传进去的值。
函数原型中圆括号中写上void就能明确该函数没有参数,传值的话会报警告。
数组名一般代表首元素地址,在传递数组时可以直接传数组名,函数形参用数组或指针接收都没问题。
臨2.函数递归
详情请跳转至另一篇博客:
臨3.函数栈帧问题
详情请跳转至另一篇博客:一文带你深入浅出函数栈帧http://t.csdn.cn/4UIY4
臨敬请期待更好的作品吧~
感谢观看,你的支持就是对我最大的鼓励,阁下何不成人之美,点赞收藏关注走一波~



