一乐电子

一乐电子百科

 找回密码
 请使用微信账号登录和注册会员

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

搜索
查看: 2081|回复: 0
收起左侧

结构体与函数返回值

[复制链接]
发表于 2016-6-20 21:11 | 显示全部楼层 |阅读模式

' w9 Y. w# u4 R+ |+ c* {; {0 ?对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量 ,在C++中也是可以直接返回结构体变量的。
. E- w1 j' z0 A" u7 x: s% _8 A
% b5 D% r3 X. B5 s; G直接返回结构体变量示例如下;+ W+ g5 ^4 N' ^
typedef struct tagSTUDENT{
, |$ q7 f7 f. M0 Xchar name[20];; j/ _: _3 \! m2 h
int age;
# G  L, {( K5 J/ x8 y+ |}STUDENT;( N* \6 v  v, P! G; ^0 q4 u% t
! F- C4 n/ \( _
STUDENT fun();
- x+ O- W, y( c3 p# I9 R# m- ^int _tmain(int argc, _TCHAR* argv[])5 o1 [+ s8 Z9 e+ u, P
{. F! {: b4 g  W  Y- Q
STUDENT p=fun();* q1 R* r" J; V& V( q" m
printf("p.name=%s",p.name);
! t0 K+ B. e& Q& h8 g5 {return 0;. @8 f2 W2 w" m+ i; u
}, M4 m/ ?1 s! Z: t6 T/ Q

' D) \$ _% l& g, ?STUDENT fun()
- l+ [: g0 T  E& O6 J$ Z- F  x{
3 {$ X8 w. v( ]. J+ Z  M8 X( fSTUDENT stu;
8 G4 m7 F5 d6 N* u' K9 o/ Jstu.age=18;7 ], C3 a; [: i. ^1 F/ ]$ v0 p
strcpy(stu.name,"xiaoming");3 f' N* W0 D" z- q
return stu;$ k8 h( ?$ {8 v0 Y# D3 P3 h
}( ]$ z3 L2 M/ M# O7 c, l6 l3 O
5 s4 i2 K& D3 s
以指针方式返回结构体示例如下:
4 Q+ _0 U) R% ?2 [8 b( Mtypedef struct tagSTUDENT{
- k) }% X/ d* schar name[20];) G! [; q4 r3 E  _
int age;
  H/ T4 D  l8 b# ^9 F}STUDENT;1 U' e$ |! p$ Y
$ ?4 ?( V8 h: y. E2 K0 o. `

9 b) G3 R- G2 B3 l  i3 |STUDENT* fun()
9 r% ]9 q+ y+ Z4 l5 A, B  X{) I2 A% }( y" I
STUDENT* p=malloc(sizeof(STUDENT));
' W2 e5 j% ~/ ~% W- d" ap->age=18;& h2 |' v% c9 z$ v# p- h* f
strcpy(p->name,"xiaoming");
6 o, N. H- n) c! v! |/ lreturn p;
( d  M7 N; _! g+ L0 _}' n) a/ n7 }7 ?0 X& K) l8 r; N( [- N
关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:
# l4 y+ l. _" i有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):5 b4 Q* M: w/ c3 t2 F- X
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。9 _9 V: _. u2 i7 N4 Z: B
原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)! b. K* w- K7 _' f% N, n
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。. }$ {0 F  z0 q8 Z
9 Q+ E2 b3 C5 F4 y% V* r, j
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
/ z* ?9 b# v' m! k例1:struct{
9 k% ]) L1 Z( s) [$ y              short a1;
9 Y' L$ c, C! p3 S. k7 e5 B              short a2;
% R% w7 c: w0 X( [& Z! q% P' W: g2 t              short a3;
7 y% S. O/ d. G. _             }A;
* W) g- F9 _8 W0 b0 W
' H: K- I) |1 Z        struct{
* u6 {7 J, t6 d            long a1;. d# _3 q# @- M$ ^- \+ T
            short a2;
* ?3 y! [; \9 y: @! r           }B;8 T3 C. b$ q* [- u  z
sizeof(A) = 6; 这个很好理解,三个short都为2。+ b+ E; t$ {/ q5 m5 `5 M
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。! b% e% g, B6 C! Z0 d, }
例2:struct A{; R5 z/ Z, n! |3 M. F9 Q
             int a;. y6 E6 ]0 p* G2 h; I
             char b;, v6 N, Q  z+ K4 C, ]
             short c;  p% B7 e  Q: c! l$ [8 Z
             };0 H' f$ ~7 g5 g6 a9 z8 z0 k' U
9 t# S* [# y* u
        struct B{
6 G; L  v6 i: E            char b;. a* V) @2 _; e. S# f, Y
            int a;
6 B( _1 S, W6 R6 V8 y) \            short c;7 C3 W4 o' H' r( C
             };, N/ ]) S) w5 B7 a2 s! P2 u6 P
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。4 K& c. C4 ]( ]+ ?( w( P
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。0 ^! P3 O- C/ A5 Z% B6 A
深究一下,为什么是这样,我们可以看看内存里的布局情况。8 g; Q( H0 q& @! v) r; t) Z
                            a         b         c! b4 M3 c$ {' u. J" Z  [& P
A的内存布局:1111,     1*,       11
) }" l4 }% c4 ?+ t0 L+ H# c% S                            b          a        c
2 J' A3 m4 P; e. wB的内存布局:1***,     1111,   11**- T) Q9 V. @" y$ l' i) ^% B' G! ]
其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
4 h0 f, z7 v" mB中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。9 A2 f+ T( B7 ]$ n" t5 L! ?& U: S
再看一个结构中含有结构成员的例子:, `; i" W8 I+ w( G8 y, x8 M9 U, e
例3:struct A{
5 K( \/ X0 _5 c5 Y              int a;, d7 W2 H) r, G7 z
              double b;
$ z5 {1 f: c  o2 k1 O+ T& m) S              float c;( e9 ?0 A2 p/ x& N8 D; z" x
             };
, @0 B( v  b' A+ f         struct B{* F$ \) r; {% J/ k# k1 ^" U
              char e[2];
7 Z- R" T* {$ m/ @; \* f              int f;
4 }4 p  @6 A  {6 H1 l              double g;
2 K5 v& l" Z/ N. ]2 n( d& v              short h;
. w7 Y. W( ~7 y, m              struct A i;
3 {4 s3 }- h2 W# n# ?             };
. x! J0 E; j& Xsizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
( u9 d$ d8 C5 s" T! U+ j* C1 q$ |9 nsizeof(B) = 48; 看看B的内存布局。
- F# A% O. O; w/ p                          e         f             g                h                                    i  W/ t! z2 ?9 m; m
B的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *, Y# V' z7 m% n& C& M
i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。
9 G3 B0 @# u- u) E. f+ @$ V% c8 ^6 y5 }: M
以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;0 y) Z  f3 I9 D9 g8 p' d: |6 t- J4 ~
有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。  [# z+ T4 F; t! P. }
                           a                b             c
' m9 f- ]  q3 M) L9 ^9 J( f* _A的内存布局:1111,     11111111,   1111
( J& P; J' w1 t% a' O% B                          e        f             g          h                     i" d; d" W$ W; {% G" ^
B的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111
+ |; a! {+ T+ H8 `" P那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。
$ ~3 N* j- G" @4 `
6 r2 l% W/ e" h+ ?
$ P9 q) w* c/ U4 }4 \- P7 o7 o5 I: Z/ k  |( O/ q5 E
还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
4 w4 f' S" h+ A' z9 [9 C/ m. ^% U使用位域的主要目的是压缩存储,其大致规则为:( d5 v. i0 N0 V1 u$ v  W
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;% H- m% _$ W4 u! W/ K0 ]
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
7 ]$ l9 T. k, r2 t& R) w, m& i3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;3 A8 ~6 T' @' f
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;9 O' ?% m3 r* z
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
- T  A: Q- A) D/ Z! y 还是让我们来看看例子。0 `2 [/ ~) i% J
例4:struct A{4 h  ~- p- m6 a7 @5 |# r  D
               char f1 : 3;/ I) Q: o; O! {
              char f2 : 4;& ]3 Y6 @3 z& b0 s2 e, E
              char f3 : 5;
' _+ p. k1 r, o# g8 v6 z              };' e3 i. `/ E! ^; P* S# G5 O
                          a         b             c
1 k2 a, u" u. S( ~8 C) o6 C- f$ sA的内存布局:111,    1111 *,   11111 * * *
% E3 T7 X  P0 V! S0 Z) `位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。
6 ?8 w; d% ~+ k8 _2 L. t7 {例5:struct B{
' j6 c% N5 j, |             char f1 : 3;9 z* _% `6 _, z2 q
             short f2 : 4;
$ g. s& ^& G7 I% L! \- ~             char f3 : 5;
1 r! L- U- Z6 F. C             };# d8 \# n+ Q6 e
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。, o$ Z3 a* M% y4 l' |- l% f
例6:struct C{0 D1 }3 B0 J3 c4 A. }$ g8 O5 c
              char f1 : 3;
) h+ i; ^" S; a# V* \9 A1 Y* |              char f2;, ^. J7 \; g" U6 ]0 J; U
             char f3 : 5;
# H  }' a9 O+ N- _% W$ u             };
* W) [  s- N0 G% c4 O非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
" s  S- b3 @0 _' H/ d
2 k- y) K4 ]! h" y( A考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
" h9 L: y/ r- t# S" A最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间

本版积分规则

QQ|一淘宝店|手机版|商店|电子DIY套件|一乐电子 ( 粤ICP备09076165号 ) 公安备案粤公网安备 44522102000183号

GMT+8, 2024-4-27 10:43 , Processed in 0.043389 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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