一乐电子

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

QQ登录

只需一步,快速开始

微信扫码登录

搜索
查看: 3134|回复: 0

结构体与函数返回值

[复制链接]
发表于 2016-6-20 21:11 | 显示全部楼层 |阅读模式
& z' b4 I0 Q2 A" o  p; a+ H$ t
对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量 ,在C++中也是可以直接返回结构体变量的。
$ `6 ~  q7 v, y8 f! p
$ B/ `8 g7 y; W, V$ v5 E直接返回结构体变量示例如下;0 C" Y# b  A7 C) b% J
typedef struct tagSTUDENT{
4 G3 z+ k5 O4 J0 mchar name[20];" ~5 f0 G* l. U  Z6 X. \
int age;- V: \5 E0 f) B- V: M( ~- z
}STUDENT;/ P" l" h3 _3 j: {, U! d; B" z

; k3 A- X: T$ p# s, {# }STUDENT fun();
5 z2 c3 I$ ~. l; h3 {int _tmain(int argc, _TCHAR* argv[])
* n# W6 c# G8 F7 W! J2 [9 |{" G  M/ _; P3 l
STUDENT p=fun();9 p' G9 ~( @1 b0 r, J
printf("p.name=%s",p.name);
* p7 b$ V3 y/ ?6 `5 Y4 |' Creturn 0;
, @' d- n8 X2 \# Y}
' X- s' j& k& n* g. e, Y
, E3 Y/ v, f4 ASTUDENT fun()
$ v8 t. y- I. K: k+ L0 J% v{
5 p/ f' w: D* ^% }( TSTUDENT stu;# T5 H% ~& n' l* |9 Q% I
stu.age=18;( ]! \  ?0 b. m2 P% Y1 F/ I* q3 l5 Q
strcpy(stu.name,"xiaoming");
3 D4 Z# p- S' k; e! D( O  _3 a) s* Xreturn stu;
9 n  W  p: ?& O( U: k# S+ K}8 b) W  e' z8 o5 A5 i

" [2 a! D: i) X7 z, W4 J6 ~以指针方式返回结构体示例如下:
1 K2 L2 i0 |" q; N. e0 [4 v: qtypedef struct tagSTUDENT{7 X: ]% v; u  E
char name[20];
  q& p* F6 y2 a" u$ l; g6 @int age;
" X. d. C0 Q+ @3 d: r! t/ ?8 n1 X}STUDENT;  D% z. J! D1 w; L9 X$ }
: \$ L' z  `; l0 G$ g- ~0 h
9 O' t6 G. p( V. t( R
STUDENT* fun()
' t1 I% a; ~3 D. _* A{
3 `' g! u6 @1 NSTUDENT* p=malloc(sizeof(STUDENT));
3 `7 q# l3 o6 j" \- Kp->age=18;
+ W/ H" `* f  c- I0 R9 mstrcpy(p->name,"xiaoming");
5 m. ~5 j  {  r" [return p;
! W% p5 s" j# B8 K: `! _! a}7 O5 q  Z  Y& n6 s- H- C/ M9 _
关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:
& T2 B. N; C0 P2 P$ n有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):. L. M) R$ g# U* X
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。$ k8 P- Y/ Y5 b
原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)) c, R0 U& i  x2 U# E
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
% y# ~+ s5 N6 Z/ ~+ S6 W! Y
5 `4 J% Y( P% p! M, u这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。! F* j2 Q" k! K' I0 ~8 T6 [
例1:struct{* F( |% e6 C$ u  |3 _& t9 Z; [
              short a1;9 [4 I  F$ D8 T2 ]; S
              short a2;/ |) l/ v' H3 s: {1 f4 d
              short a3;
  |! s6 P8 L" Y, s             }A;4 n$ E  _. }/ {
, C- h0 V# x+ m, @/ G- n
        struct{
: `7 c9 ^% @& P" V" U8 H            long a1;
" P. S0 C7 |, s4 M6 s$ i- \, ~$ H            short a2;3 I1 S4 H# J) |$ Q1 I1 W+ m
           }B;; v' Z; _3 K5 h; C5 O
sizeof(A) = 6; 这个很好理解,三个short都为2。1 J8 u, k& w/ j" N8 [/ I
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
. V1 D1 Y5 O& Z6 E# A' ~3 R例2:struct A{3 j4 l  t1 v. z; x: r
             int a;
: h& }7 V  M* f# P             char b;1 }1 f4 q0 G. j! B9 C; |
             short c;$ u% o/ e7 U# @/ C0 D3 \8 n
             };
9 T/ P+ X; n3 M4 l
* m9 `0 y* Y# B2 Y( H        struct B{0 h3 x: M0 Y0 p% R  O
            char b;
' d* m! P9 f5 Z: n7 r$ a" I            int a;
# ?; E; Q7 |1 O$ D/ w* |            short c;2 ^) i0 l( e- u& ?/ z9 {
             };; m' \/ `- n& U, q: A1 s
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。& b; e& h! r( \$ P
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
% L( f, D4 d$ w( @( {' h- Y; `深究一下,为什么是这样,我们可以看看内存里的布局情况。
7 `6 R! ~# p0 |; N& F' `                            a         b         c
* T' E- K% `. jA的内存布局:1111,     1*,       11. O4 f3 m5 K3 ~$ g# X
                            b          a        c& H! N* G4 v* c1 b. j; i" n
B的内存布局:1***,     1111,   11**
. f' v. T) k! x5 r其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
8 W0 n; ]- b7 ]) K( {% F1 B. f7 v+ IB中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
( ^; U* z5 Y, x/ M4 y5 _再看一个结构中含有结构成员的例子:
  |! t  \% `# s: a# O  E例3:struct A{
: B* Q9 ?* {( Y% O( j              int a;! Z; i4 {0 M; O' m8 Y/ e' ?
              double b;2 y+ h" ~( j/ o& K) L# {  R
              float c;/ k) t7 H7 g  I, Y
             };
  Y& F& H; O) E) d; `, d: E         struct B{
6 L6 l# X! |5 \2 g8 M              char e[2];
8 H1 ]8 y) s: W% I7 @4 V9 A              int f;
, P9 C  z- N: ?, h, a              double g;
0 m) Q0 ?+ b+ O+ V. ?0 w, ]              short h;
+ g# a% v& @2 L& g              struct A i;
6 G4 b) Y) b* P1 Q7 E             };
$ n( c3 ^8 D( y. p; Q8 M/ wsizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
+ m/ N; Y0 J: I/ A9 ^2 Xsizeof(B) = 48; 看看B的内存布局。$ z6 h5 y+ ~9 d- d
                          e         f             g                h                                    i4 Z& h1 O. s/ p0 S: s
B的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * ** S1 n. m2 k' `- B
i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。. }& w! y; }' P, G. ~
3 {2 h2 L. G( H7 y
以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;
$ E: d, r9 Z6 J有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。1 x  l6 A+ N' r, I. o
                           a                b             c
7 s! n) @! ~( \" oA的内存布局:1111,     11111111,   1111- z: T) i6 x; N- @& \: H1 `+ N
                          e        f             g          h                     i
5 k/ o) P( c5 s7 b  ?: C1 oB的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111, r. {1 ~: k8 c7 l* T
那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。, x1 f- u6 y- i! P- W4 y
. W$ _6 w  b6 u: D; M1 Q) P

5 }/ s& Q. z6 E! b7 c. L9 I
; S2 I0 \& d/ i2 @6 D; H6 T& g还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
- ]  Y7 b5 ]& K5 W. _使用位域的主要目的是压缩存储,其大致规则为:
* s( `6 P' Q( i5 O, V8 |$ [2 z1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
- F( r* u$ m) O( e8 x  t6 Q) M2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;( |9 B* K5 G9 i7 F6 ^' |& B$ D3 ~! s
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;6 E! J% o! X% i$ ~6 R5 ?" B5 F
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
9 Q0 C7 J% z) h  x: x5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
. k. v3 V& _" P; g! d! R 还是让我们来看看例子。$ r; Q( n( y) {0 W
例4:struct A{
7 K3 @0 K. d. I, p               char f1 : 3;
% }, s3 j6 w" V6 @              char f2 : 4;
+ v, A/ X. L9 R# D2 V5 @8 v              char f3 : 5;8 z  P- m3 A  B# S. [
              };+ E3 F7 l: n8 ]  G% \
                          a         b             c
% y' K" u  {8 x+ ?A的内存布局:111,    1111 *,   11111 * * *  G% P4 u1 W; N: ^
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。# f4 N2 A4 t1 D0 p9 m. ^/ c/ I
例5:struct B{( s) d; v& x: Z  }$ f$ ]2 C
             char f1 : 3;
) ~& n) K: o9 y) w3 O             short f2 : 4;
5 U4 P7 _7 C3 m8 _- g" \  G/ C             char f3 : 5;) z$ I5 n5 L4 @* G; X
             };
7 u, ^  B* I/ G3 x由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
/ v2 h% S# r) \. }7 x7 u# M例6:struct C{
# y: y2 ?5 ^6 l+ ?+ Z8 `              char f1 : 3;
, Z; S( T1 L# f3 C, [              char f2;
7 j& `; u2 Z) l- ~& w8 K             char f3 : 5;
( [7 a6 J6 N- Z9 l2 l1 `& a             };
% N: |( `- k" E+ k2 O5 J非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。% S' w  ?2 O3 e% b1 A

1 _; f$ s9 a8 g4 @考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。. c2 p5 c" V" W; N/ |, k4 r' C
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间

本版积分规则

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

GMT+8, 2025-8-20 09:00 , Processed in 0.031434 second(s), 18 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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