C语言函数间参数的传递方式(一)有返回的函数
“我们是伞兵,本来就该被包围的!”----《兄弟连》。
在战争中,伞兵天生就该被包围,而在编程语言中,函数生来就该被调用。在被调用的过程中,执行函数的指令,完成值和参数的传递。按照不同的传递方式,函数可以分为下面几类:
1、先来看返回变量、常量的函数:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int func(int a) 5 { 6 a=2*a*a; 7 printf("a=%d\n",a); 8 return a; 9 } 10 int main() 11 { 12 int b=func(10); 13 printf("b=%d\n",b); 14 return 0; 15 }
上面的函数是返回变量的值,如果把被调函数func中的变量a换成常量,程序依然能够得到正确结果。例如:
1 #include <stdio.h> 2 3 int func() 4 { 5 const char a='W'; 6 printf("A=%c \n",a); 7 return a; 8 } 9 int main() 10 { 11 char b=func(); 12 printf("b=%c \n",b); 13 return 0; 14 }
也许有人会问,辛辛苦苦敲了半天代码,就返回了一个字符,为什么不返回一个字符串那?返回字符串不是不行,可要返回字符串,就不能按照返回一般的变量、常量来处理了。我们知道,在C语言编程环境下,字符串只能用字符型数组或者字符型指针来声明和定义,不存在C++语言语言环境下的string类型。因此,当你要返回字符串的时候,其实需要返回的是字符串的地址。这就引出了我们的下一个课题:
2、返回字符串地址
函数不仅能返回值,还能返回地址。返回地址时,需要在函数的返回值数据类型前面加”*“,下面看例程:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 char* getString() 6 { 7 char str[] = "hello,world"; 8 return str; //返回数组地址 9 } 10 void test02() 11 { 12 char* p = NULL; 13 p = getString(); //接收字符串数组地址 14 printf("test02返回字符串数组: %s \n", p); 15 /* 因为p接收的是被返回的字符串数组的地址。而字符串数组在栈上, 当getString函数 16 执行完以后,str字符串已经被内存释放了,test02函数调用它的结果就不确定了。 */ 17 } 18 19 int main() 20 { 21 test02(); 22 return 0; 23 }
运行程序,我们会发现,字符串数组没有被正确输出。下面再看一例:
3、局部变量地址的返回
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int* func() 6 { 7 int a = 10; 8 return &a; 9 } 10 void test01() 11 { 12 int* p = func(); //这种调用,结果已经不重要了,因为a的内存(因该是a所指向的内存) 13 //被系统释放了,我们没有权限区操作这块内存 14 printf("test01第一次:a=%d \n", *p); //第一次输出‘10’,因为系统默认为作者保留这段内存 15 printf("test02第二次:a=%d \n", *p); //第二次输出内容就不定了,系统已经将这段内存释放了 16 } 17 18 int main() 19 { 20 test01(); 21 return 0; 22 }
如果我们用VS2015以上编辑器运行上面代码,控制台会输出注解的内容。如果用codeblock或者其他编辑器运行,则有可能无任何输出,因为你输出的内存数据是无效的。综合起来看,函数不能返回在被调函数中定义的局部变量、数组形式声明的字符串(字符串数组)。因为他们存放在栈上。随着被调函数运行结束,这些局部变量、字符串等占用的内存被释放。再访问上述内存空间就是非法访问了。那么是不是函数就不能返回地址了那?不是!函数可以返回存放在程序数据区、堆区的地址(关于内存分区的内容参见:https://www.cnblogs.com/GoldCrop/p/11030984.html)。详见下面例子:
4、堆及程序数据区地址的返回
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 char* getString01() 6 { 7 char *str= malloc(64); //在堆上定义字符串 8 memset(str,0,64); 9 strcpy(str,"hello,world"); 10 return str; //返回字符串地址 11 } 12 void test02() 13 { 14 char *q = getString01(); //接收字符串地址 15 printf("堆上字符串内容 :%s \n", q); /* 因为字符串地址存放在堆区,即使getString01 16 运行完后,字符串所在内存也不会被释放,可以返回字符串地址 */ 17 } 18 19 char* getString02() 20 { 21 char* str = "hello,world!"; //声明并定义字符串指针变量,字符串存储在数据区 22 return str; //返回指针地址 23 } 24 void test03() 25 { 26 char* p = NULL; 27 p= getString02(); 28 printf("程序数据区字符串内容 :%s \n", p); 29 } 30 int main() 31 { 32 test02(); 33 test03(); 34 return 0; 35 }
通过上面的例子可以看出,我们能返回程序数据区、堆区变量、常量的地址,但不能返回栈区的变量、常量的地址。