我们先来看看函数的“调用”。
在 C 中,函数的参数通常是“按值”。下面,在调用sqrt(x) 之后,x 的值没有改变,因为sqrt() 收到了值x 的副本,因此永远无法影响x。
y = sqrt(x);
// x did not change
printf("%f is the square root of %f\n", y, x);
函数的数组参数似乎是“通过引用”的。在调用fgets() 之后,预计buf 会被更改。 fgets() 没有收到char 数组的副本,而是“引用”。因此fgets() 可能会影响数组的内容。
char buf[80] = { 0 }; // array 80 of char
printf("Before <%s>\n", buf); // "<>"
fgets(buf, sizeof buf, stdin);
printf("After <%s>\n", buf); // "<Hello World!>"
现在让我们看看“接收”部分:函数。
double、int、struct类型的函数参数按值接收。这里f的值是上面x的copy。对f 所做的任何更改都不会影响调用代码中的x。
double sqrt(double f) {
double y;
... // lots of code
return y;
}
现在是棘手的一点,这就是 C 语言中的大量讨论。
下面fgets() 中的参数s 没有分配到上面的buf(char 的数组80),而是为s 分配了从buf 派生的指针类型: buf 的第一个元素的地址。通过知道buf 的数组元素的地址(通过s),fgets() 会影响上面打印的内容。
char *fgets(char * restrict s, int n, FILE * restrict stream) {
// code
s[i] = ThisAndThat();
// code
return s;
}
现在让我们看看如何调用fgets():
char *p = malloc(80);
*p = '\0';
printf("Before <%s>\n", p); // "<>"
fgets(p, 80, stdin);
printf("After <%s>\n", p); // "<Hello World!>"
在第二个示例中,p 是“指向 char 的指针”。它通过值传递给fgets()。 fgets() 通过s 收到p 的副本。
中心思想:fgets() 不知道它是否收到“数组第一个元素的地址”或“指向 char 的指针”的副本。在这两种情况下,它都将s 视为“指向字符的指针”。
假设fgets() 做了以下事情。在s 被更改之前,它会影响s 指向的数据。然后s,指针,改变了。 s 是一个本地 变量,这个赋值不会改变调用例程变量buf 和p。
char *fgets(char * restrict s, int n, FILE * restrict stream) {
// lots of code
s[0] = '\0';
// more code
s = NULL;
return s;
}
注意:还有其他问题需要考虑,例如将函数作为参数传递以及将数组封装在结构中。