映月读书网 > C语言解惑 > 9.2 结构作为函数参数及函数的返回值 >

9.2 结构作为函数参数及函数的返回值

结构可以作为函数的参数,函数可以不返回值(void类型),也可以返回结构或者结构指针。

【例9.6】下面是把结构d的域值加到f的域值上的例子,分析没有实现预定目标的原因。不修改Add函数的类型,实现程序功能。


#include <stdio.h>
struct LIST{
          int a
,b
;
}d={3
,8}
;
void Add
(struct LIST
,struct LIST
); 
void main
()
{
     struct LIST f={5}
;
     Add
(d
,f
);           
     printf
(\"a=%d
,b=%dn\"
, f.a
,f.b
);
}
void Add
(struct LIST d
,struct LIST f
)
{
     f.a=d.a+f.a
;
     f.b=d.b+f.b
;
}
  

【解答】程序将Add函数的结构f作为传数值的方式传递,当函数返回时,主程序里的参数不会被修改。因为要求不修改Add函数的类型,实现程序功能,所以应该修改参数传递方式,即将传数值改为传地址值(将这个参数以指针方式传递)的方式。


//
修改后的程序
#include <stdio.h>
struct LIST{
          int a
,b
;
}d={3
,8}
;
void Add
(struct LIST
,struct LIST*
); 
void main
()
{
     struct LIST f={5}
;
     Add
(d
,&f
);               //
传结构变量f
的地址
     printf
(\"a=%d
,b=%dn\"
, f.a
,f.b
);
}
//
将结构f
作为参数,以传地址值的方式传递这个参数
void Add
(struct LIST d
,struct LIST *f
)
{
     f->a=d.a+f->a
;
     f->b=d.b+f->b
;
}
  

因为f.b=0,所以f的f.a=f.b=8。

【例9.7】下面是把一个结构域的值加到另一个结构相应的域值中,编译没有错误,但没有实现预定功能。请分析原因并修改程序。


#include <stdio.h>
struct LIST{
       int a
,b
;
}d={3
,8}
;
struct LIST Add
(struct LIST
,struct LIST
); 
void main
()
{
     struct LIST f={5}
;
     Add
(d
,f
);          //
改为f=Add
(d
,f
);
     printf
(\"a=%d
,b=%dn\"
, f.a
,f.b
);
}
//
将结构作为参数,以传数值的方式传递这个参数
struct LIST Add
(struct LIST d
,struct LIST f
)
{
     f.a=d.a+f.a
;
     f.b=d.b+f.b
;
     return f
;
}
  

【解答】这实际是上一个例子的解决方案。不改变参数传递方式,让Add函数返回结构f,实现对主函数结构f的修改。因为语句“Add(d,f);”中的参数f在调用结束之后就消失了,所以没有改变主程序的f值。应该将Add的返回值接收下来,即用结构f接收返回值。


f=Add
(d
,f
);
  

【例9.8】下面是将上例的函数Add改为返回指针的函数,找出存在的问题。


#include <stdio.h>
struct LIST{
       int a
,b
;
}d={3
,8}
;
struct LIST *Add
(struct LIST
,struct LIST
); 
int main
()
{
        struct LIST f={5
,3}
,*p=&f
;
        p=Add
(d
,f
);            
        printf
(\"a=%d
,b=%dn\"
, f.a
,f.b
);
        return 0
;
}
//
将结构作为参数,以传数值的方式传递这个参数
struct LIST *Add
(struct LIST d
,struct LIST f
)
{
        struct LIST *p=&f
;
        f.a=d.a+f.a
;
        f.b=d.b+f.b
;
        return p
;
}
  

【解答】这个程序的错误很典型。在Add函数里定义的指针变量是自己局部所有的变量,在Add里,p->a=8,p->b=11,p有自己的指向地址。在退出Add函数之后,这个局部变量p消失,主函数里仍然是原来的p,“p=Add(d,f);”只是将主程序里p的指向改变为在Add函数里指向的地址,但是&f没有变化,仍然是原来的值,而Add传递的参数是f的值,所以不改变主程序里的值。但这时p的指向地址里是不可预测的值。

不要以为使用


f=*p
;
  

语句就能使f的内容与Add返回值一样。其实*p是不确定的值,只有p是确定的值时,才可以使用这种方法。下面的程序就是演示了这个问题。

如上所示,在Add函数里设计的局部指针变量p是使用参数f初始化,一旦退出程序就消失了。其实这是个不稳定因素,容易产生怪七怪八的错误。稳妥的设计是申请一块内存。为了便于理解,下面的程序输出它们的地址和数值供比较。


#include <stdio.h>
#include <stdlib.h>
struct LIST{
       int a
,b
;
}d={3
,8}
;
struct LIST *Add
(struct LIST
,struct LIST
); 
int main
()
{
        struct LIST f={5
,3}
,*p=&f
;
        printf
(\"
系统分配p=0x%x
,&f=0x%xn\"
, p
,&f
);
        p=Add
(d
,f
); 
        printf
(\"
调用后p->a
:%d
,p->b
:%dn\"
, p->a
,p->b
);
        printf
(\"
调用后a
:%d
,b
:%dn\"
, f.a
,f.b
);
        printf
(\"
调用后p=0x%x
,&f=0x%dn\"
, p
,&f
);
        f=*p
;
        printf
(\"
执行f=*p
;语句的结果如下。n\"
);
        printf
(\"p->a
:%d
,p->b
:%dn\"
, p->a
,p->b
);
        printf
(\"a
:%d
,b
:%dn\"
, f.a
,f.b
);
        printf
(\"p=0x%x
,&f=0x%xn\"
, p
,&f
);
        return 0
;
}
struct LIST *Add
(struct LIST d
,struct LIST f
)
{
        struct LIST *p
;
        p=
(struct LIST *
)malloc
(sizeof
(struct LIST
));
        p->a=d.a+f.a
;
        p->b=d.b+f.b
;
        printf
(\"
在Add
函数中p=0x%xn\"
, p
);
        return p
;
}
 

程序运行结果如下。


系统分配p=0x12ff78
,&f=0x12ff78
在Add
函数中p=0x4300c0
调用后p->a
:8
,p->b
:11
调用后a
:5
,b
:3
调用后p=0x4300c0
,&f=0x1245048
执行f=*p
;语句的结果如下。
p->a
:8
,p->b
:11
a
:8
,b
:11
p=0x4300c0
,&f=0x12ff78
  

由此可见,当要返回指针时,在被调函数里申请动态内存,不如直接使用一个指针参数。因为&f就是地址,所以在主程序里根本不需要再设计指针,只要将Add函数传递的f改为传递指针即可,这样一来,程序就变得非常简单了。


//
将f
作为指针传递给Add
函数的源程序
#include <stdio.h>
struct LIST{
          int a
,b
;
}d={3
,8}
;
struct LIST *Add
(struct LIST
,struct LIST*
); 
int main
()
{
             struct LIST f={5
,3}
;
             Add
(d
,&f
); 
             printf
(\"a
:%d
,b
:%dn\"
, f.a
,f.b
);
             return 0
;
}
struct LIST *Add
(struct LIST d
,struct LIST *f
)
{
             f->a=d.a+f->a
;
             f->b=d.b+f->b
;
             return f
;
}    
  

显然,Add函数可以简化为void类型的函数。

【例9.9】改正如下程序中的错误。


#include <stdio.h>
#include <stdlib.h>
typedef struct student {
             char name[10]
;
             int studnem
;
} *STUDNT
;
int main
()
{
    STUDNT PT
;
    if 
((PT=
(STUDNT
) malloc 
(sizeof
(STUDNT 
)) 
) == NULL
)
        return 1
;
    printf
(\"
输入姓名和学号:\"
);
    scanf
(\"%s%d\"
, PT->name
,PT->studnem
);
    printf
(\"
姓名:%s 
学号:%dn\"
, PT->name
,PT->studnem
);
    return 0
;
}
  

【解答】这里的STUDNT是定义的新结构类型指针,所以用它定义的PT是结构指针变量,由于sizeof()要求的是结构类型,所以不能再使用sizeof(STUDNT)而使用sizeof(struct student)。同理,malloc()前面不是使用(STUDNT*)而应使用(STUDNT)。

scanf要求的是地址,name是字符串,可以不用加&,但studnem是整数类型,所以必须冠以地址符号&。


//
修改后的程序
#include <stdio.h>
#include <stdlib.h>
typedef struct student {
            char name[10]
;
            int studnem
;
} *STUDNT
;
int main
()
{
    STUDNT PT
;
    if 
(( PT=
(STUDNT
) malloc 
(sizeof
(struct student 
)) 
) == NULL
)
        return 1
;
    printf
(\"
输入姓名和学号:\"
);
    scanf
(\"%s%d\"
, PT->name
,&PT->studnem
);
    printf
(\"
姓名:%s 
学号:%dn\"
, PT->name
,PT->studnem
);
    return 0
;
}
  

程序运行示范如下。


输入姓名和学号:
王银英 20983
姓名:王银英 
学号:20983