普通变量作为函数形参
(1)形参额外申请内存,实参传过来的是值,函数执行结束之后,形参的内存会自动释放。
数组作为函数形参
(1)函数名作为形参传参时,实际传递不是整个数组,而是数组的首元素的首地址,所以在子函数内部,传进来的数组名就等于是一个指向数组首元素首地址的指针。所以sizeof得到的是4.
(2)在子函数内传参得到的数组首元素首地址,和外面得到的数组首元素首地址的值是相同的。很多人把这种特性叫做“传址调用”
(3)数组作为函数形参时,[ ]里的数字是可有可无的,因为数组名做形参传递的实际只是个指针,根本没有数组长度这个信息。
指针作为函数形参
(1)这和数组作为函数形参是一样的,这就好像指针方式访问数组元素和数组方式访问数组元素的结果一样是一样的。
结构体变量作为函数形参
(1)结构体变量其实也是普通变量而已。
(2)因为结构体一般都很大,所以如果直接用结构体变量进行传参,那么函数调用效率就会很低。(因为在函数传参的时候需要将实参赋值给形参,所以当传参的变量越大调用效率就会越低)。因此,不要传变量,传实参的指针(地址)。
(3)结构体因为自身太大,所以传参应该用指针来传;回想一下数组,为什么C语言设计的时候数组传参默认是传的数组首元素首地址而不是整个数组?
传值调用与传址调用
(1)传值调用描述的是这样一种现象:x和y作为实参,自己并没有真身进入swap1函数内部,而只是拷贝了一份自己的副本(副本具有和自己一样的值,但是是不同的变量)进入子函数swap1,然后我们在子函数swap1中交换的实际是副本而不是x、y真身。所以在swap1内部确实是交换了,但是到外部的x和y根本没有受影响。
(2)在swap2中x和y真的被改变了(但是x和y真身还是没有进入swap2函数内,而是swap2函数内部跑出来把外面的x和y真身改了)。实际上实参x和y永远无法真身进入子函数内部(进去的只能是一份拷贝),但是在swap2我们把x和y的地址传进去给子函数了,于是乎在子函数内可以通过指针解引用方式从函数内部访问到外部的x和y真身,从而改变x和y。
(3)结论:这个世界上根本没有传值和传址这两种方式,C语言本身函数调用时一直是传值的,只不过传的值可以是变量名,也可以是变量的指针。
void swap1(int a, int b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
printf("in swap1, a = %d, b = %d.\n", a, b);
}
void swap2(int *a, int *b)
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
printf("in swap1, *a = %d, *b = %d.\n", *a, *b);
}
输入型参数与输出型参数
(1)函数其实就是一个对数据的加工坊,其实如果没有形参列表和返回值,函数也能对数据进行加工,用全局变量即可。
(2)用全局变量来传参和用函数参数列表返回值来传参各有特点,在实践中都有使用。总的来说,函数参数传参用的比较多,因为这样可以实现模块化编程,而C语言中也是尽量减少使用全局变量
char str[32] = "linux";//可修改
char* str = "linxu"; //不可通过指针直接访问修改
内存是分区域的。你字符存放在内存中的区域直接关系到我们是否能够对它进行操作。char *Petr ="linux"是把它放在代码段。编译器不允许你对它修改。而数组你是定义在栈或者data段编译器是允许你对它进行修改的。
形参中的const
(1)const一般用在函数参数列表中,用法是const int *p;(指针变量p本身可变的,而p所指向的变量是不可变的)。
何为“可变”?
const int *p;//指针变量p本身可变的,而p所指向的变量是不可变的
p1 = &a;// 编译无错误无警告,可变
*p1 = 3;// error: assignment of read-only location ‘*p1’,不可变
int * const p3; // p本身是cosnt的,p指向的变量不是const的
p3 = &a; // error: assignment of read-only variable ‘p3’
*p3 = 5; // 编译无错误无警告
int * const p3 = &a;//初始化时赋值即可.
(2)const用来修饰指针做函数传参,作用就在于声明在函数内部不会改变这个指针所指向的内容,所以给该函数传一个不可改变的指针(char *p = “linux”;这种)不会触发错误;而一个未声明为const的指针的函数,你给他传一个不可更改的指针的时候就要小心了。
如何多个返回值
(1)一般来说,函数的输入部分就是函数参数,输出部分就是返回值。问题是函数的参数可以有很多个,而返回值只能有1个。这就造成我们无法让一个函数返回多个值。
(2)现实编程中,一个函数需要返回多个值是非常普遍的,因此完全依赖于返回值是不靠谱的,通常的做法是用参数来做返回(在典型的linux风格函数中,返回值是不用来返回结果的,而是用来返回0或者负数用来表示程序执行结果是对还是错,是成功还是失败)。
(3)普遍做法,编程中函数的输入和输出都是靠函数参数的,返回值只是用来表示函数执行的结果是对(成功)还是错(失败)。如果这个参数是用来做输入的,就叫输入型参数;如果这个参数的目的是用来做输出的,就叫输出型参数。
(4)输出型参数就是用来让函数内部把数据输出到函数外部的。
#inlcude <stdio.h>
int multip5_3(int a, int *p)
{
int tmp;
tmp = 5 * a;
if (tmp > 100)
{
return -1;
}
else
{
*p = tmp; //其实就是扔一个地址过来,接收处理后的值,充当返回值
return 0;
}
}
int main()
{
int a, b = 0, ret = -1;
a = 30;
ret = multip5_3(a, &b);
if (ret == -1)
{
printf("出错了\n");
}
else
{
printf("result = %d.\n", b);
}
}
输入型、输出型参数总结
(1)看到一个函数的原型后,怎么样一眼看出来哪个参数做输入哪个做输出?函数传参如果传的是普通变量(不是指针)那肯定是输入型参数;
如果传指针就有2种可能性了:
有const:输入型参数
无const:输出型参数
参考strcpy
char *strcpy(char *dest, const char *src);