一乐电子

一乐电子百科

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

QQ登录

只需一步,快速开始

快捷登录

手机号码,快捷登录

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

结构体与函数返回值

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

, K4 B# T  C2 R; N对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量 ,在C++中也是可以直接返回结构体变量的。
! K. k# a2 h. A3 h8 S2 x: H$ F( a7 t$ V6 z. U4 t4 i# v! l
直接返回结构体变量示例如下;
4 P. t: j7 B8 Q+ H7 z5 vtypedef struct tagSTUDENT{
; ?+ a/ ~9 T( E$ x# rchar name[20];
$ x+ j$ e' T! K$ z# q, l. Nint age;
" a; g  n% Y- |/ l# ^2 g}STUDENT;
) o/ ~! d; t1 k# E4 ^/ T5 f4 z1 Y$ R- K, F' h+ H; Y' g- z3 L$ }& k
STUDENT fun();, q3 @& l9 ^! c) o$ S* U0 E
int _tmain(int argc, _TCHAR* argv[])
6 T  q) v9 `8 F* @% G  `8 ^/ s  P{. F( z1 |! I1 J2 n( r
STUDENT p=fun();# e, R1 }  [; n6 T9 m
printf("p.name=%s",p.name);
6 J9 _# I( O* d& `5 b$ zreturn 0;+ C0 V% o; o. [5 n: s5 {2 s! M
}1 V# f4 v9 x7 A9 P* b) k

$ h: p: g9 S' d0 I( ASTUDENT fun()5 \4 Y6 ^; `1 i7 G3 ^6 Y
{0 a/ f* G- X* U6 U& J# t
STUDENT stu;
1 T' f3 {3 A' v8 S9 {stu.age=18;
/ A" I: G4 X# h1 |$ R8 ustrcpy(stu.name,"xiaoming");
9 W0 S2 T: z6 Q! B+ qreturn stu;7 a8 ~# |4 Z0 G3 Y% b7 K0 `
}
7 E  z0 |0 X  Y9 Z9 W
" D' l+ e4 g" r1 N6 e6 t0 H1 W: S以指针方式返回结构体示例如下:* o5 H( A: e* c# ~
typedef struct tagSTUDENT{: ]  k- ~3 l- B3 a& l" o
char name[20];
$ E5 y' d' _3 S+ p+ l, _0 z! l: q+ {int age;1 s: _1 K+ {) U- K" D
}STUDENT;
0 Y6 ~9 w7 T7 C5 u% N( S
' T8 D/ M. j" {( O0 O6 h
- a0 u: f/ G# f' n1 p0 |* z/ sSTUDENT* fun()
2 {3 I. r* J6 B! Y6 a* ?2 k{
' I' k( I/ I3 w4 F$ Q& DSTUDENT* p=malloc(sizeof(STUDENT));" F- a) _9 ?9 K( b8 b# o+ q
p->age=18;1 S# n( ?  ~- b% x
strcpy(p->name,"xiaoming");  ?  @+ _- T, O7 Z$ m+ i
return p;
# m1 B" D  P4 P, q, h}
" P, s% U, E& u" v关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:3 r1 Q& q& I$ b. K; [% X
有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):# P5 Y4 m5 M4 x+ Y( z
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
7 s7 T! C% `$ J7 ~. J; W# C& ~原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
3 ~( @7 ~/ o5 t3 K- e. P5 g原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
2 W/ {- U0 [6 Z8 T8 z8 x+ i7 s+ l8 L( @( q
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
0 V& E5 {& c' l6 K例1:struct{
2 i/ K& ]9 J! m# @# S  y              short a1;
$ q8 l" V, @) ?* v" L              short a2;: F1 O0 T% u( n1 [, Y
              short a3;4 Q1 y, e/ ]# J3 t! k, m1 u
             }A;
( t% P' G7 X* c4 b& B+ a: }
4 E3 `; A" T/ {        struct{
  p, T1 ~! c1 v; C/ o7 r" t            long a1;0 r( Q% \( X& q
            short a2;
$ X, q; X5 t9 s$ A$ h  Q           }B;
- ]& i) U. U- o$ K3 \7 v6 R0 Asizeof(A) = 6; 这个很好理解,三个short都为2。/ S3 m: x& _9 N, A
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
# r' a6 s$ Z& B8 l9 j; [6 i* B例2:struct A{4 D1 O5 U6 C7 O& j9 b0 |% W' w
             int a;
; i4 k% Y  }3 H+ B1 N8 \             char b;
+ g  U" a5 Q5 M7 M8 F             short c;
1 ~2 f3 J; X' f$ m6 E             };
  c+ Z3 K* _& n, Y, ], `' u. Z/ |( c+ O: E
        struct B{9 v2 }) R+ f' M0 s9 ^% P
            char b;
4 p; {7 M% D. S( n3 T" C8 r7 h' v1 [            int a;" N8 y8 B7 E; W% E/ r2 M* u
            short c;0 Y: b" V( X$ p& B; g; T4 }
             };, F; b% n4 u0 L; j! C
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。8 W  o8 z# k. D" {- Z  m
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。3 `  X. m4 N5 W+ h* _1 V
深究一下,为什么是这样,我们可以看看内存里的布局情况。( Z% \1 `4 D) x0 p5 n9 s
                            a         b         c
$ K) z0 Y: L/ l7 O3 BA的内存布局:1111,     1*,       111 v! J* n% z; y$ X# i8 N
                            b          a        c( g8 P0 S! ^% y6 L0 c# R
B的内存布局:1***,     1111,   11**
  p' L; o- l' X其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。* Q! x. P5 y8 w
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
2 P2 k+ d( H% h再看一个结构中含有结构成员的例子:' P+ n( ]* q( E% A+ n$ E4 V- D/ O
例3:struct A{
$ T+ o. V" U" ^              int a;
$ ~2 S2 U$ [  p6 @, E. g              double b;
1 F) ?7 E' q' S              float c;
9 q( i0 c% |/ n* c) [8 D7 I  q             };7 W& i. X$ Q/ r& F7 k. g, W' [
         struct B{/ v; E+ L6 @) f$ p" X8 w
              char e[2];$ P  p9 J5 V* X7 a4 F, {
              int f;
' @' o- {7 c: P5 p' a( _' u              double g;
7 u* P+ M) {# }# o# {              short h;
7 |7 ?$ Z& u( b, {& ^' e              struct A i;4 ~) s$ l' B1 ~
             };! ?% o0 }# v# Z1 ~- O1 ?! w
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
4 h) I% z- k  A; r/ B; ?8 Y3 gsizeof(B) = 48; 看看B的内存布局。; v5 h6 s! r+ C2 I
                          e         f             g                h                                    i
% n2 I) C. ]  [4 _B的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *
" h$ f  c6 f8 F: A" hi其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。
9 P* G3 ~: b# W* g# b9 U' s
* N9 K% Y% d9 C; }2 `6 r2 a1 Q以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;
! r7 S. t& I# e# i1 P+ Y  f有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
3 J' H% s  ^+ u: `* |- e                           a                b             c1 I0 D* |4 x- C6 v/ }9 U& y
A的内存布局:1111,     11111111,   1111
" \& N* |, B* Z3 S/ F' H                          e        f             g          h                     i* }8 W9 f1 y1 c; f
B的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111& ^* k, C; {+ I( A. V9 r( p( Z
那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。6 N5 K$ W8 z) `) o# n

4 O4 @9 Z$ ]' y$ h
! @+ r. p2 P. @7 I  {; d; u* I* v+ \. v" z- H3 {7 a
还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。; B- P) E0 C9 y1 N
使用位域的主要目的是压缩存储,其大致规则为:
& b/ L7 V, l! a( l: k+ Q/ n1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;  K$ M" c" V# b2 n( k
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;: v( M8 s6 O  i# h, h5 Y& u
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
) P# m# L& `7 |: t5 H" f4) 如果位域字段之间穿插着非位域字段,则不进行压缩;: t; Y# h' o2 g
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
/ \. {+ i6 c/ g* C1 W1 E+ x- f 还是让我们来看看例子。
& {( Z# L& W* [' N# o9 Y& k2 u例4:struct A{. \' b4 z7 u6 }8 W" k9 }  }
               char f1 : 3;
- Q+ W* |! ]- B2 v# K; c0 v3 |3 O              char f2 : 4;
* }" g8 a' L3 S( z+ o2 P              char f3 : 5;
. O/ v' N! q! c; p1 o( O              };
% b. @2 x9 _/ M" h" @                          a         b             c" g# \+ ^. P; O, u- f* @8 C2 z
A的内存布局:111,    1111 *,   11111 * * *
% }! {. L7 [; |位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。
; ^$ G3 U/ y) m: v+ w& s例5:struct B{* ^1 T! s5 I9 _; m: B
             char f1 : 3;* Z' q9 J- z/ _, C1 R# R1 Q' i- X
             short f2 : 4;" T1 F& k6 ]% P' r3 {! w
             char f3 : 5;
1 u, B# z: O8 p2 |" O             };# e" I7 }$ l6 ~8 m
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。/ E* J! R, @: e3 h) K
例6:struct C{& `" U0 Q7 z# @( W2 L- l3 c) b
              char f1 : 3;, \# \, T. d# `/ C
              char f2;
6 x8 y6 X, D. F4 e/ j9 s, r             char f3 : 5;
. P" j# j: ^2 C% d# V9 I             };
* e* x9 {+ A4 p% i% I非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
( Y6 d1 {$ i% E8 k& }
$ c& x, i$ g5 |& x# |( v" I考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。) N3 b2 p6 O% E
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间

本版积分规则

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

GMT+8, 2024-5-3 08:57 , Processed in 0.045674 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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