OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 7385|回复: 12

C语言函数返回结构体实体

[复制链接]

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
发表于 2016-8-10 22:39:08 | 显示全部楼层 |阅读模式
本帖最后由 ianhom 于 2016-8-10 22:38 编辑

本人学艺不精,一直以来都认为C语言中函数只能返回结构体指针,而不能返回结构体实体,今天出于好奇试验了一下,在本人的编译环境下(IAR for ARM 6.7)果然可以,就把试验过程记录下来分享给大家。

首先是对函数返回结构体的编译结果
[mw_shl_code=c,true]
typedef struct _T
{
    uint8 u8a;
    uint8 u8b;
}T_STRUCTURE;

T_STRUCTURE g_tB;

T_STRUCTURE test1(void)
{
    T_STRUCTURE tC;
    tC.u8a = 5;
    tC.u8b = 6;
    return tC;
}

int main (void)
{
    g_tB = test1();    /* 返回值为结构体实体 */
    printf("g_tB.u8a = %d\n", g_tB.u8a);
    return 0;
}[/mw_shl_code]
编译无告警或错误。T_STRUCTURE是一个结构体类型,在这里用T_STRUCTURE定义了全局结构体g_tB,test1()是一个返回值为T_STRUCTURE结构体实体的函数,其内容为创建一个临时结构体实体tC,对tC的成员赋值后直接返回tC。main函数中g_tB = test1()来获取test1()的返回值并赋值给g_tB。写到这里相信很多朋友都有一个疑问,就是这个tC存在在栈上,直接返回给调用者不是会出问题吗。而实际上通过汇编发现,这里返回的就是结构体实体成员的“值”,而不是结构体的“指针”,这些值随后就赋值给g_tB,所以这样使用没有问题。
来看一下汇编程序:

1

1

2

2

(1)test1()函数体内在栈上创建了一个结构体tC,并分别为其两个成员赋值数值5和6
(2)当执行到return tC的之后,将整个tC结构体的值赋值到R0上,然后退出函数
(3)退出函数后,将g_tB的地址赋值给R1,然后将R0中代表tC结构体的值赋值给R1中所指向的g_tB,实现整个结构体实体的返回。
(注:其实一般返回值也通过R0返回,不过因为为了简化试验,试验中的结构体成员只有2个字节,当结构体成员size比较多时,编译结果会复杂一些,有兴趣的朋友可以试一下结构体size为8字节和20字节时编译的汇编程序)

以上返回的结构体实体是存在于栈中,同时也试验将全局结构体g_tA的值返回给g_tB,也是同样的方法通过R0将值返回给g_tB。

再回到我们之前的做法,如果一个结构体的内部成员的值想通过调用某个函数被修改,通常是把该结构体指针作为入参来调用该函数。
[mw_shl_code=c,true]
typedef struct _T
{
    uint8 u8a;
    uint8 u8b;
}T_STRUCTURE;

T_STRUCTURE g_tB;

void test3(T_STRUCTURE *ptD)
{
    ptD->u8a = 3;
    ptD->u8b = 4;
    return;
}

int main (void)
{   
    test3(&g_tB);      /* 通过结构体指针作为入参修改结构体值 */
    printf("g_tB.u8a = %d\n", g_tB.u8a);
    return 0;
}
[/mw_shl_code]
上面的代码是最熟悉的方式,test3()是一个无返回值,入参为T_STRUCTURE结构体指针的函数,函数内容为给结构体成员赋值数组3和4,然后直接返回。来看下汇编程序:
3.png
4.png
(1)调用test3()之前,将g_tB的地址赋值给R0,作为参数传入函数
(2)在函数中直接对R0中的g_tB成员进行赋值3,4
(3)直接返回。
在试验的编译环境下,对比一下“返回结构体实体”和“结构体指针作为参数”两种方式的汇编程序,可以看出返回结构体实体有更多的汇编语句,代码空间和运行时间都不讨巧,或许也是这个原因才会被大家慢慢遗忘“函数可以返回结构体实体”这件事。
-------------------------------------------------------------------------------------------------------------------------------------------------------
在这个试验中,也发现该编译器支持结构体实体A整体赋值给结构体实体B的方式,即g_tB = gt_A;下面在做个试验:
[mw_shl_code=c,true]
typedef struct _T
{
    uint8 u8a;
    uint8 u8b;
    uint8 u8c;
    uint8 u8d;
    uint8 u8e;
    uint8 u8f;
    uint8 u8g;
    uint8 u8h;
}T_STRUCTURE;

T_STRUCTURE g_tA, g_tB;

int main (void)
{      
    g_tB = g_tA;               /* 结构体整体赋值 */

    g_tB.u8a = g_tA.u8a;   /* 结构体成员依次赋值 */
    g_tB.u8b = g_tA.u8b;
    g_tB.u8c = g_tA.u8c;
    g_tB.u8d = g_tA.u8d;
    g_tB.u8e = g_tA.u8e;
    g_tB.u8f = g_tA.u8f;
    g_tB.u8g = g_tA.u8g;
    g_tB.u8h = g_tA.u8h;

    return 0;
}
[/mw_shl_code]
这次增加了结构体成员数量到8个,占2个32位空间,来对边一下汇编程序:

5

5



6

6

上图是整体赋值的汇编代码,下图是成员依次赋值的代码,可见结构体整体赋值时,是按照4字节搬运的,这样搬运2次就可以完成8个字节的赋值;而成员依次赋值则是逐个字节依次搬运,空间和时间不讨巧。

总结一下:
1、函数不仅可以返回结构体指针,还可以返回结构体实体;
2、如果想通过函数来修改结构体实体的值,“函数返回结构体实体”的方式在代码空间和运行时间上不及“结构体指针入参”的方式;
3、结构体可以整体赋值给另一个结构体;
4、在结构体成员比较多且零散的情况下,结构体整体赋值的方式在代码空间和运行时间上优于结构体成员依次赋值的方式;
5、在下的C语言白学了。。。。。。


P.S: 本人能力有限,汇编功底不足,分析有误之处望大神指点。上述实验现象可能比较依赖编译器,不一定完全成立。
本次试验仅分析了简单的情况,更复杂的方式望各位大神进一步分析、分享,下面是“函数返回结构体实体”的试验代码。
[mw_shl_code=c,true]
typedef struct _T
{
    uint8 u8a;
    uint8 u8b;
}T_STRUCTURE;

T_STRUCTURE g_tA, g_tB;

T_STRUCTURE test1(void)
{
    T_STRUCTURE tC;
    tC.u8a = 5;
    tC.u8b = 6;
    return tC;
}

T_STRUCTURE test2(void)
{
    return g_tA;
}

void test3(T_STRUCTURE *ptD)
{
    ptD->u8a = 3;
    ptD->u8b = 4;
    return;
}

int main (void)
{   
    g_tA.u8a = 7;
    g_tA.u8b = 8;
    g_tB = test1();    /* 返回值为结构体实体 */
    printf("g_tB.u8a = %d\n", g_tB.u8a);
    g_tB = test2();    /* 返回值为结构体实体 */
    printf("g_tB.u8a = %d\n", g_tB.u8a);
    test3(&g_tB);      /* 通过结构体指针作为入参修改结构体值 */
    printf("g_tB.u8a = %d\n", g_tB.u8a);
   
    g_tB = g_tA;       /* 结构体整体赋值 */
}
[/mw_shl_code]

机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
发表于 2016-8-10 23:13:23 | 显示全部楼层
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

117

主题

598

帖子

1

精华

资深版主

Rank: 8Rank: 8

积分
1694
金钱
1694
注册时间
2012-5-10
在线时间
437 小时
发表于 2016-8-11 08:56:20 | 显示全部楼层
这种操作我只用指针解决,一个指针4个内存,如果你结构体的大小大于4个就亏了
如有技术问题,微信扫头像交流,STM32,JAVA,服务器
回复 支持 反对

使用道具 举报

15

主题

786

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3223
金钱
3223
注册时间
2015-7-26
在线时间
811 小时
发表于 2016-8-11 09:13:51 | 显示全部楼层
谢谢分享,一般都用指针没留意到这点
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

11

主题

1041

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3696
金钱
3696
注册时间
2011-5-23
在线时间
2008 小时
发表于 2016-8-11 09:21:45 | 显示全部楼层
本帖最后由 aozima 于 2016-8-11 09:23 编辑

返回栈里面的数据,相当于刻舟求剑。
没有生命周期的概念么
RT-Thread RTOS 音频,WIFI,蓝牙
回复 支持 反对

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
 楼主| 发表于 2016-8-11 11:41:36 | 显示全部楼层
aozima 发表于 2016-8-11 09:21
返回栈里面的数据,相当于刻舟求剑。
没有生命周期的概念么

这里是“值”返回,栈里的数据已经赋值给了R0,退出函数后栈中的数据是否有效已经没有关系。
返回栈的“地址”倒是相当于刻舟求剑,我喜欢这个比喻。
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复 支持 反对

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
 楼主| 发表于 2016-8-11 11:44:19 | 显示全部楼层
xsx127 发表于 2016-8-11 08:56
这种操作我只用指针解决,一个指针4个内存,如果你结构体的大小大于4个就亏了

对于有些情况是必须用到把结构体的全部成员赋值到另一个结构体的(从ROM赋值到RAM中),当结构体成员很多时,编译器会使用类似memcpy这样的函数来搬运
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复 支持 反对

使用道具 举报

117

主题

598

帖子

1

精华

资深版主

Rank: 8Rank: 8

积分
1694
金钱
1694
注册时间
2012-5-10
在线时间
437 小时
发表于 2016-8-11 11:57:51 | 显示全部楼层
ianhom 发表于 2016-8-11 11:44
对于有些情况是必须用到把结构体的全部成员赋值到另一个结构体的(从ROM赋值到RAM中),当结构体成员很多 ...

自己用memecpy复制啊,如果一个结构体有几百字节,你还要返回结构体么,当然是返回指针
如有技术问题,微信扫头像交流,STM32,JAVA,服务器
回复 支持 反对

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
 楼主| 发表于 2016-8-11 12:23:02 | 显示全部楼层
xsx127 发表于 2016-8-11 11:57
自己用memecpy复制啊,如果一个结构体有几百字节,你还要返回结构体么,当然是返回指针

大神可能误会了,我说的是g_tB = g_tA结构体整体赋值的情况。
在总结里已经说明返回结构体实体方式不如指针方式好,这个帖子只是想说明函数可以返回结构体实体
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复 支持 反对

使用道具 举报

10

主题

271

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1236
金钱
1236
注册时间
2015-5-14
在线时间
352 小时
发表于 2016-8-18 08:54:56 | 显示全部楼层
楼主威武,顶起。
30年众生牛马,60年诸佛龙象!
回复 支持 反对

使用道具 举报

9

主题

538

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3371
金钱
3371
注册时间
2015-1-7
在线时间
794 小时
 楼主| 发表于 2016-8-18 10:05:53 | 显示全部楼层

大神别闹
机器生汇编,汇编生B,B生C,C生万物.... 经过长期对C语言的研究,目前只有两个方面不懂:这也不懂,那也不懂
https://github.com/ianhom
回复 支持 反对

使用道具 举报

2

主题

20

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
212
金钱
212
注册时间
2016-5-7
在线时间
89 小时
发表于 2016-9-13 10:30:42 | 显示全部楼层
涨姿势了
回复 支持 反对

使用道具 举报

10

主题

147

帖子

0

精华

高级会员

Rank: 4

积分
602
金钱
602
注册时间
2015-7-11
在线时间
94 小时
发表于 2016-12-18 21:15:02 | 显示全部楼层
学习赞谢谢!!!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 09:51

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表