传参分类
从传参类型上看
传参分为传值和传指针。
传指针本质上也是传值,只不过传的是指针值。由于传一般值和传指针值还是有点区别,所以分成两种说法。传值一般就指传一般值,传指针专门用于描述传指针值。
从传参行为上看
传参分为值复制和传自己。
传自己本质上也是值复制,只不过是取地址传无名指针,形参直接接上;而不是直接传有名指针。例如:
1
2
3
4
5
6
7
// 函数调用
func1(id, index, flag, &count);
func2(arr);
// 函数定义头
func1(int id, int index, int flag, int *p_count)
func2(int *arr)
其中,id
,index
,flag
是值复制,这毫无疑义。arr
是数组名,这里退化为指针,直接进行了传参,也就是我说的“直接传有名指针”,所以我将其归为值复制。&count
是取地址传无名指针,然后形参直接接上,是传自己。
传参分析
传值是值复制。
传指针有两种情况,第一种是值复制,第二种是传自己,如图所示。
以下是一些经验,不一定正确,只能说这些场景遇到的比较多,看个眼熟就成。
- 值复制倾向于使用,传自己倾向于赋值。
- 一般而言,实参直接传为值复制,实参取地址为传自己。多数情况下,实参取地址可以理解为赋值。
- 形参中的值变量属于值复制,形参中的指针变量有上图两种情况。(其实就是传参分析的两条,这里着眼于形参。该经验一定正确。)
- 无论是为了使用还是赋值,传数组一般直接传数组名。
- 以
char
为例。函数形参中,经常能看见const char *
和char *
的使用。const char *
显然不会更改指针所指向内存的内容,但是char *
就可能更改指针所指向的内容,这一点在实践中值得注意,即我们的内存传递给char *
后有可能会被更改。
这里对经验4多说几句。我曾经以为,若是为了给数组赋值,应该对数组名取地址传参。一是因为输出参数一般都加&,写成类似下面这样,一眼就能看出哪些是出参,可读性高。二是觉得平时数组指针的概念都没怎么用过,在这里正好用起来,岂不美哉。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 这是问题写法
#include <stdio.h>
void func(int, int (*)[], int *);
int main()
{
int i = 0;
int arr[10] = {0};
int count = 5;
int sum = 0;
func(count, &arr, &sum);
for (i = 0; i < count; i++)
{
printf("%d\n", arr[i]);
}
printf("sum = %d\n", sum);
return 0;
}
void func(int count, int (*parr)[10], int *p_sum)
{
int i = 0;
int sum = 0;
for (i = 0; i < count; i++)
{
(*parr)[i] = i;
// parr[0][i] = i;
sum += i;
}
*p_sum = sum;
}
然而后来我发现了自己的错误,不要这样传参。
- 原因一:没人这么用。
- 原因二:
memcpy()
,snprintf()
这种库函数,平时使用的时候都是直接传数组名的。 - 原因三:可读性未必好。出参都加了&,算是满足了强迫症,但是函数定义里面就要写成
(*parr)[i] = i
或者parr[0][i] = i
,这显得复杂。 - 原因四:性能不佳。使用的时候,
(*parr)[i] = i
或者parr[0][i] = i
要取值两次。 - 原因五:数组指针有用,但压根不是这么用的。直接传二维数组名的时候,形参就得定义数组指针,这才是数组指针正常的用法。
所以给数组名取地址传参是多此一举。
上述例子的正确写法应为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 这是正常写法
#include <stdio.h>
void func(int, int *, int *);
int main()
{
int i = 0;
int arr[10] = {0};
int count = 5;
int sum = 0;
func(count, arr, &sum);
for (i = 0; i < count; i++)
{
printf("%d\n", arr[i]);
}
printf("sum = %d\n", sum);
return 0;
}
void func(int count, int *p, int *p_sum)
{
int i = 0;
int sum = 0;
for (i = 0; i < count; i++)
{
p[i] = i;
sum += i;
}
*p_sum = sum;
}