Contents
  1. 1. C没有引用传递
  2. 2. 其实都是值传递
  3. 3. 返回数据的方式

C没有引用传递

C没有引用!C没有引用!C没有引用!重要的事说3遍。

1
2
3
4
5
//file: refer.c
void swap(int &a,int &b){
}
int main(){}

用C编译器编译:

1
2
3
4
$ cc refer.c
refer.c:1:15: error: expected ‘;’, ‘,’ or ‘)’ before ‘&’ token
void swap(int &a,int &b){
^

C++中才有引用

1
2
$ g++ refer.c
$

很多博文C与C++不分,误导了不少人(例如我)。

其实都是值传递

  教材中通常讲值传递,地址传递,其实都是值传递,本质没有区别。所谓地址传递,不过传递的是地址值而已,遵循同样的法则,没有任何特殊。

  比如传递进入一个指针,如果你又对指针值进行了修改,那么修改之后的任何改动都不会影响到外部了,因为这也是值传递,形参不过是实参的一个copy。所以如果想要通过指针对外部数据进行修改,而不要修改指针p本身,而是对*p进行操作。   

返回数据的方式

  由于C值传递的特性,再加上其自动变量都保存于栈中,作用域都只限于函数或语句块内,所以返回于函数中创建的数组、结构指针等都是没有意义的。

1
2
3
4
int* func2(){
int a[3] = {1,2,3};
return a;
}

尝试编译, 将发出警告:

1
2
3
4
test.c: In function ‘func2’:
test.c:20:5: warning: function returns address of local variable [-Wreturn-local-addr]
return a;
^

  所以我们经常可以看到C函数参数经常带有很多指针,指向空的或者待修改的结构/数据,它们的作用不是用于传入信息,而主要是为了输出结果。
  比如上一篇中提到的编译正则表达式的regcomp函数:

1
int regcomp(regex_t *preg, const char *regex, int cflags);

  第一个参数就是用来传入一个空的regex_t结构的指针,这个函数的作用就是将这个结构填入编译结果,这个指针才是真正的”返回值”,而return则只是一个状态码。
  其它一些高级OOP(如JAVA)语言是很不一样的,例如Java的对象(数组也是对象)保存在堆中,在哪里创建对象并不影响其生存期。所以Java可以直接返回在任何方法在创建的对象,而不用担心作用域问题。

  1、直接返回值。适合基本类型,理论上返回结构也可以,但是返回大型结构会导致大量copy操作影响执行效率。
  2、传入指针。这是C中常用的方法。
  3、效仿高级语言,返回在局部方法中创建的堆指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdlib.h>
#include<stdio.h>
int* func(){
int *a= malloc(3*sizeof(int));
return a;
}
main(){
int *p = func();
int i;
for(i = 0 ; i<3;i++){
p[i] = i;
printf("%d\n",p[i]);
}
}

  方法3听起来错,不用在参数中传递一大堆指针。理论上也是可行的(如上),但是有一个问题,即内存的分配和释放会不在同一地点(一个函数内,一个在函数外,可能位于不同源文件),易导致内存泄漏,所以并不推荐使用。
  实际上

1
int regcomp(regex_t *preg, const char *regex, int cflags);

的例子,也是在外部分配空间

1
regex_t *r = malloc(sizeof(regex_t));

后将指针传入的,那么如果这样行不行呢?

1
2
3
4
5
6
7
//主函数中
regex_t *r;
//regcom函数中
int regcomp(regex_t *preg, const char *regex, int cflags){
preg = malloc(sizeof(regex_t));
//other codes
}

  这很有迷惑性,地址传递嘛,应该可行吧?
  实际不行,因为malloc之后,preg就指向不同的地址了,与实参preg失去了任何关联。或者说赋值 的是形参,实参仍然是未定义值。需要注意的一点是不仅C中存在这个问题,Java中也有这个问题,如果你想修改传入的对象,一定不要再次用new赋值。
  
  如果一定要在函数外声明,在函数内malloc,那么只能传入指针的指针,然后赋值给(*p)了。

1
2
3
4
5
6
7
//主函数中
regex_t **r;
//regcom函数中
int regcomp(regex_t **preg, const char *regex, int cflags){
*preg = malloc(sizeof(regex_t));
//other codes
}

  很复杂不是吗?所以,传入指针以接收结果是C中相对最好的方式了。

  

Contents
  1. 1. C没有引用传递
  2. 2. 其实都是值传递
  3. 3. 返回数据的方式