基础篇
为什么学习函数?
1.代码看起来特别多,不简洁
2.修改起来非常麻烦,需要所有用到的地方都修改
函数就可以解决上述这两个问题
函数可以理解为一个打包带,就是把一段代码打包起来,用到的时候只要写一下这个打包带的名字就可以了
- 函数定义:
既可以定义含参数的函数,也可以定义不含参数的函数,下面以一段含参数的函数定义为例子
int add(int a,int b) // 返回值类型 函数名(形式参数)
{
return a+b; //功能是返回a和b的和
}//{}内是函数主体
返回值既可以是常规数据类型,int,float也可以是结构体,如果不需要返回值,可以写作
void fun()
{
return ;
}
- 函数声明:
自定义函数要写在主函数之前,若写在后面需要在宏之后声明函数,告诉编译器这个函数会在后面写到,防止主函数用到时,编译器不知道函数在哪。
函明声明就是把定义时的第一行直接复制一下后面加;就可以了,如果函数后面有参数,那么参数也要复制过来。
- 函数调用:
调用也分为有参,和无参数,大体相似,注意参数类型一致,参数个数一致就可以了。
有参数add(3,5); 无参数fun();
进阶篇
形式参数与实际参数:
个人理解形式参数就是被调用函数里的参数,实际参数就是我们调用时手动传入的参数。
说到形参实参就不得不说一下函数的副本机制,以一个引例子来具体说明:
#include<stdio.h>
int swap(int *c , int *d)
{
int *q;
q = c;
c = d;
d = q;
printf("%d %d\n", *c, *d);
}int main ()
{
int a=5, b=10;
swap(&a, &b); printf("%d %d\n" , a, b);
return 0;
}/*
这个得出结果是
10 5
5 10
*/
****由结果来看的话,是在 swap 里边是交换成功了的。
这个原因是因为主函数调用 swap(c,d); 之后这个函数在栈上开辟了一段空间,形式参数(int a, int b) 是将原来主函数的(&a, &b) 完全的拷贝了一份,里边存储的是主函数的 a,b 的地址然后定义一个中间指针变量,进行交换,此时形参 c, d 内部存储的地址值确实发生了交换,以至于c ,d 的值确实是10, 5; 但是并未对主函数中数据块a,b 产生任何影响。当swap打印完毕之后,此函数生命周期结束,int c , int d 被释放掉。 然后回到主函数里,由于数据块 a, b并没有 发生任何改变所以打印出来的值才会发生没有交换。
所以呢我们由此得出结论我们应该实实在在的交换数据块 a,b 才能成功
本着这个想法我们做出了如下改变。
#include<stdio.h>
int swap(int *c , int *d)
{
int q;
q = *c;
*c = *d;
*d = q;
printf("%d %d\n", *c, *d);
}int main ()
{
int a=5, b=10;
swap(&a, &b); printf("%d %d\n" , a, b);
return 0;
}/*
这个得出结果是
10 5
10 5
*/
二级指针交换
这个在主函数里边呢,调用swap(&a, &b); 之后 在swap函数中 , 用*c, *d ,去直接访问了a ,b 所在的数据 并把他们交换, 这样在swap生命周期结束之后, main 函数 数据块a,b 中的值发生了交换最后的结果就是被改变了的。
所以本着这样的想法,那么我们就由这个形参 (int **c, int ** d) 把他们降级两次就可访问到数据块a,b。再对它进行交换即可。
指针和引用区别
指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。(这里是在说实参指针本身的地址值不会变)
而在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址(int &a的形式)。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。
为了进一步加深大家对指针和引用的区别,下面我从编译的角度来阐述它们之间的区别:
程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。