C语言函数间参数的传递方式(二)没有返回,仅仅靠形参、实参传递参数的函数

这一篇我们来看看没有返回,只靠形参、实参传递参数的函数,先来学习最简单的一种:

1、传值调用(赋值传递)

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 void swap(int a,int b)
 6 {
 7     int temp;
 8     printf("交换前,a=%d  b=%d \n",a,b);
 9     temp=a;
10     a=b;
11     b=temp;
12     printf("交换后,a=%d  b=%d \n",a,b);
13 }
14 int main()
15 {
16     int x=10,y=20;
17     printf("交换前,x=%d  y=%d \n",x,y);
18     swap(x,y);
19     printf("交换后,x=%d  y=%d \n",x,y);
20     return 0;
21 }

从运行程序结果可以看出:被调函数swap只对形参操作,实参无变化。显然,传值调用属于单向值传递,函数运行结果不影响、不改变调用函数的实参。

看过了最简单的,下面来看看复杂一点儿的————引用调用(指针传递、赋地址传递)。C语言中,这个词也被叫做“指针传递”、“赋地址传递”。在C++语言中,则有另外的含义了。用起来,也会方便许多,但是,C语言不支持。引用调用(指针传递、赋地址传递)分为同级指针传递,和二级指针传递2种方式。同时,根据主调函数是否申请内存,又分两种情况来分析。下面先来看:

2、主调函数申请内存条件下,同级指针传递:

     2.1  主调函数申请内存,同级指针传递(传递变量地址)

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 void swap(int * a,int *b)
 6 {
 7     int temp;
 8     printf("a地址:%p    b地址:%p    \n",a,b);
 9     printf("交换前,a=%d  b=%d \n",*a,*b);
10     temp=*a;
11     *a=*b;
12     *b=temp;
13     printf("交换后,a=%d  b=%d \n",*a,*b);
14 }
15 int main()
16 {
17     int x=10,y=20;
18     printf("x地址:%p    y地址:%p\n",&x,&y);
19     printf("交换前,x=%d  y=%d \n",x,y);
20     swap(&x,&y);
21     printf("交换后,x=%d  y=%d \n",x,y);
22     return 0;
23 }

本例中,main主函数申请了2个变量——x和y,并把它们的地址——&x和&y传递给被调函数swap,被调函数的形参定义了2个指针变量int *a,int *b来接收。由于a,b,&x,&y都是地址,相当于a=&x,b=&y。

如果把&x和&y也看成指针,则有*a=*(&x)=x,*b=*(&y)=y。显然,对其解引用后,所得值为变量的值,而不是地址。所以称之为同级指针传递(因为a,b和&x,&y都是只需一次解引用操作即可求得变量的值,我认为称之为“一级指针传递”更为贴切)。

从程序运行结果来看,被调函数修改了实参的值。所以,我们能得出结论:主调函数申请内存,同级指针传递实参变量地址,被调函数能够修改实参的值。

下面我们来看看堆和栈上字符串地址的传递:

      2.2  主调函数申请内存,同级指针传递(传递堆上、栈上字符串地址)

 1 void func(char* p)
 2 {
 3     strcpy(p, "Hello,World!");
 4 }
 5 void test01()
 6 {
 7     //分配到栈上
 8     char buf[1024] ="I like C语言!";
 9     printf("栈上初始字符串:%s \n", buf);
10     func(buf);
11     printf("主调函数申请内存,同级指针传递,栈上字符串返回:%s \n", buf);
12 }
13 
14 void printString(char* str)
15 {
16     strcpy(str, "Hello,World!");
17     printf("主调函数申请内存,同级指针传递,堆上字符串返回:%s \n", str);
18 }
19 void test02()
20 {
21     //分配到堆区
22     char* p = malloc(sizeof(char) * 64);
23     memset(p, 0, 64);    
24     strcpy(p, "I like C语言!");
25     printf("堆上初始字符串:%s \n", p);
26     printString(p);
27     printf("字符串长度=%d \n", strlen(p));
28 }
29 
30 int main()
31 {
32     test01();
33     test02();
34     system("pause");
35     return EXIT_SUCCESS;
}

这里,由于我们可以把字符串名“buf”和指针“p”看作地址,显然,这也是同级指针传递。并且,通过程序运行结果,我们可以得出结论:主调函数申请内存,同级指针传递字符串地址,被调函数能够对堆和栈上字符串进行赋值,修改操作。

 下面再来看一段代码:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 void buildString(char* pp)
 6 {
 7     char* temp = "被调函数修改字符串";
 8     pp = temp;
 9     printf("%s \n", *pp);
10 }
11 void test01()
12 {
13     char* p = "主调函数定义初始字符串";
14     printf("%s \n", p);
15     buildString(p);
16     printf("%s \n", p);
17 }
18 
19 int main()
20 {
21     test01();
22     return 0;
23 }

上面这段代码无法正常运行,再次证明了一个事实:以指针形式声明的字符串,保存在程序数据区,不能被改动。

通过上面3个例程,我们可以得出结论:主调函数申请内存,同级指针传递地址(无论是变量地址还是堆或者栈的地址)条件下,被调函数能够修改实参的值。当然,程序数据区的字符串依然不能修改!

       2.3  在这里,我们提出一个问题:如果,在主调函数申请内存,二级指针传递地址(无论是变量地址还是堆或者栈的地址,甚至是程序数据区变量、常量地址)条件下,会发生什么那?也许,这将是我们下一篇随笔的命题!

 

3、被调函数申请内存,同级指针传递地址

     3.1  被调函数申请内存,同级指针传递变量地址,这种情况不存在。或者说我不知道如何用C语言来就这种情况编程。

     3.2  被调函数申请内存,同级指针传递字符串地址

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 
 6 void getString01(char* pp)  //传递数组地址
 7 {
 8     char sTring[] = "Hello,world!";
 9     printf("字符串数组地址是:%p \n", sTring);
10     pp = sTring;
11 }
12 void test01()
13 {
14     char* p = NULL;
15     getString01(p);
16     printf("返回数组地址得到: %p, p=%s \n\n", p, p);
17 }
18 
19 void getString02(char* pp)  //传递指针地址
20 {
21     char* pOint = "Hello,Kitty!";
22     printf("字符串指针地址是:%p \n", pOint);
23     pp = pOint;
24 }
25 void test02()
26 {
27     char* p = NULL;
28     getString02(p);
29     printf("返回指针地址得到:%p, p= %s \n\n", p, p);
30 }
31 
32 void getString03(char* pp)  //传递堆地址
33 {
34     char* hEap = malloc(20 * sizeof(char));
35     if (hEap == NULL) return;
36     memset(hEap, 0, 20 * sizeof(char));
37     strcpy(hEap, "Hello,Miss!");
38     pp = hEap;
39     printf("堆地址是:        %p \n", pp);
40 }
41 void test03()
42 {
43     char* p = NULL;
44     getString03(p);
45     printf("返回堆地址得到:  %p, p=%s \n", p, p);
46 }
47 int main(void)
48 {
49     test01();  //调用函数,得到输出:“传递同级指针得:(null)”
50     test02();
51     test03();
52     system("pause");
53     return EXIT_SUCCESS;
54 }

运行上面例程,结论是: 被调函数无论是在堆上,还是在栈上,还是以指针形式在程序数据区申请内存,其对形参的定义都无法传递给实参。

     3.3  被调函数申请内存,二级指针传递字符串地址

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 
 6 void getString01(char** pp)  //传递数组地址
 7 {
 8     char sTring[] = "Hello,world!";
 9     printf("字符串数组地址是:%p \n", sTring);
10     *pp = sTring;
11 }
12 void test01()
13 {
14     char* p = NULL;
15     getString01(&p);
16     printf("返回数组地址得到: %p, p=%s \n\n", p, p);
17 }
18 
19 void getString02(char** pp)  //传递指针地址
20 {
21     char* pOint = "Hello,Kitty!";
22     printf("字符串指针地址是:%p \n", pOint);
23     *pp = pOint;
24 }
25 void test02()
26 {
27     char* p = NULL;
28     getString02(&p);
29     printf("返回指针地址得到:%p, p= %s \n\n", p, p);
30 }
31 
32 void getString03(char** pp)  //传递堆地址
33 {
34     char* hEap = malloc(20 * sizeof(char));
35     if (hEap == NULL) return;
36     memset(hEap, 0, 20 * sizeof(char));
37     strcpy(hEap, "Hello,Miss!");
38     *pp = hEap;
39     printf("堆地址是:        %p \n", pp);
40 }
41 void test03()
42 {
43     char* p = NULL;
44     getString03(&p);
45     printf("返回堆地址得到:  %p, p=%s \n", p, p);
46 }
47 int main(void)
48 {
49     test01();  //调用函数,得到输出:“传递同级指针得:(null)”
50     test02();
51     test03();
52     system("pause");
53     return EXIT_SUCCESS;
54 }

运行上面例程,结论是: 被调函数在堆上,或者以指针形式在程序数据区申请内存,其对形参的定义都可以传递给实参。在栈上定义的字符串数组,由于栈上变量的生命周期的原因,无法正确传递给实参。

 

posted @ 2019-06-21 16:21  大老菜  阅读(1038)  评论(0编辑  收藏  举报