版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
* M' y+ m, t4 ?. }" I2 p, u7 h对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量 ,在C++中也是可以直接返回结构体变量的。8 R6 c2 i( A( r+ [( ~
: d3 J0 D6 M8 G7 |: U直接返回结构体变量示例如下;
' r1 d( S* ?' Z& Ftypedef struct tagSTUDENT{! F V( ?6 O, C# I3 \8 e3 J& r
char name[20];6 R, S5 ]$ r" e, x) Y' I
int age;
. o: L/ w! Z7 e k# a! M, y: a}STUDENT;
: _! R$ Z$ b2 Y1 \ C( ]6 w$ Q0 j3 k5 j4 {; G7 w4 i
STUDENT fun();
7 u7 B/ Q- Q5 V4 m0 l" T) }" Yint _tmain(int argc, _TCHAR* argv[])
) J. ~2 h# s$ i7 E: E{
7 _% G4 y e! x3 F; K- `+ L, |STUDENT p=fun();% p6 T4 G* Y) o$ F0 X
printf("p.name=%s",p.name);, g3 f8 k t' s/ U2 z K. C# r: S, ^
return 0;/ i; ^, b2 o# ^$ ~
}+ s* R( x) S/ |) [
d) A+ l9 s) B' y) m3 nSTUDENT fun()
/ H& z: n; j5 r4 [: i4 p{
0 o8 O3 J6 k! F$ bSTUDENT stu;8 g" a& Z7 h: S/ b R8 d. W9 \
stu.age=18;1 z" Z: N3 C& d3 j' w
strcpy(stu.name,"xiaoming");
) E! L. ]/ p$ F2 Jreturn stu;9 D* T4 C" K& \' h4 C# ?; ?, h
}
; K" _ M+ ]: `" [; o/ Y$ w" A$ ]5 H: r* m* b4 q% k" U6 g
以指针方式返回结构体示例如下:$ z [+ ?: u2 o, J `
typedef struct tagSTUDENT{* S0 X( q) \6 ?; N- s! _; Z
char name[20];
5 D) {* o- G6 [int age;
, ? D2 V- n' U) H- w}STUDENT;
6 j( U9 p1 Y; E) y0 U" l" u0 h1 x0 J+ |, s* V g
: F+ L6 E! N0 P: i n4 o+ k, s+ {
STUDENT* fun()
. t! d9 H$ t7 u' u, x3 S{
O) N0 z( o; `+ Q& SSTUDENT* p=malloc(sizeof(STUDENT));9 a% u+ K" X1 ?5 |2 v h, J1 G2 l2 j
p->age=18;
& V9 `3 f/ `7 x) fstrcpy(p->name,"xiaoming");
: [& G1 D- G2 ^+ i8 G: Kreturn p;: o5 u, R* k/ C
}* z: s/ _# C6 I# \! B/ T
关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:6 [7 N1 o0 @$ o E
有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):
- @: G! f, Q: I* S原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
, O1 q* T5 ~7 l2 _原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。); d: I0 ~) K* ^8 }- O w
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
8 q# }3 a z9 t# {) W3 h1 p w" t, {6 `7 L) H- C; R$ W
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。4 m h6 N/ W; A6 x; u
例1:struct{# y) @7 A. Q& a' ]6 F% Q1 _
short a1;
+ G2 l. ]5 E" E" |* t' g9 Z, k short a2;" e% }/ `" }' R. q2 ]) ^
short a3;4 L7 D! K1 \/ {' E. w1 F+ j$ Q; E
}A;
; I2 O' i7 Z/ d+ ?" Y' X1 T
6 G& H6 n1 w+ E struct{3 y! ?6 k* C6 h M* z
long a1;- }% B& g9 r5 \" c- z- j& c: h; @
short a2;* f9 E% e9 y- J% H, k: U* I
}B;% E8 q4 c L% u7 q1 ^! z: L
sizeof(A) = 6; 这个很好理解,三个short都为2。' `* b; D, J' h; T" ?. j$ ^; u3 j8 g
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
: f+ m2 v7 w% r. c8 O: U) |) c例2:struct A{
* h; K( K% h. v int a;
) E/ B7 ~5 V$ T& t char b;5 L2 w1 [# I# P% p
short c;
5 Y4 J, R3 U |: L* l: ~! i };
. K; {9 t% i. r% ?% H
4 e+ N3 g( l* {) Q% e$ e9 s struct B{
4 N0 U6 T# c. F char b;8 L+ O/ w0 v& Q. o
int a;. Z$ q7 ~6 `* D6 E
short c;
% f+ F7 h% m4 Q6 ?6 y- y* W };6 n0 t' I- [1 ]& K8 l5 j0 L+ r
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。
/ z- ]0 e7 S+ F3 \% Rsizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。3 Y) ^7 k1 v; V; c( M0 A3 {8 ]
深究一下,为什么是这样,我们可以看看内存里的布局情况。
6 T/ _, _/ R$ @0 U9 b7 J$ ~; D a b c
. B+ m/ X# w5 z9 DA的内存布局:1111, 1*, 11 ?2 ^9 J, u& k( ~
b a c
+ s1 M2 L% g; i4 R3 l. j4 R) B; y& JB的内存布局:1***, 1111, 11**
' e9 O& ]1 {* ^; V* {* y1 J# a其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。1 l3 [: f6 T* G# B
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。3 _1 b7 U1 p2 E$ X/ _
再看一个结构中含有结构成员的例子:
( X8 N, f* D% Z; K' h例3:struct A{
! k1 b5 R6 t; i0 a& b; c* O9 A6 d int a;' N+ h/ Q* ?' R P! w; t2 `
double b;
3 t; K0 d# H" i8 ? float c;
+ p' Z! ], ]+ H };
, K6 f% g+ E R struct B{
5 d, v) B& e2 L7 y, x4 u char e[2]; K# r7 e/ C9 m+ q' D& w: K- }
int f;2 k+ H9 W( v* P! [1 ? f. M$ }7 ]. R
double g;1 ?! c: D# i- g, G
short h;
3 l+ m2 T0 s9 |" [ struct A i;! U, A8 N9 a( M
};+ A. w& Z- U8 S6 ~
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。1 B. Y9 w8 I$ G" D7 y
sizeof(B) = 48; 看看B的内存布局。
$ r0 b3 i+ m, U+ n e f g h i
3 Y! N }- b/ K0 zB的内存布局:11* *, 1111, 11111111, 11 * * * * * *, 1111* * * *, 11111111, 1111 * * * *
$ a% Z0 I: v" p- U: Y2 p+ @# I' Hi其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。
8 I6 b4 {' y* g; ]0 T5 L* u) f9 P+ Y# W3 x. z( W4 N r2 W
以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;4 D: e6 ]3 W: ~$ a% C8 z: v
有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
( y. M9 y( o W( B5 \+ R a b c9 U: s9 C4 l7 N/ ?( n% g: ^! N- @
A的内存布局:1111, 11111111, 1111: H4 p2 b$ f0 ~& R- f7 \
e f g h i2 r+ g* v% G# t1 F0 a$ j6 X; e
B的内存布局:11, 1111, 11111111, 11 , 1111, 11111111, 1111+ c( y7 u: H! y) B/ K1 B
那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。$ F, w" J( ]- r+ y3 ~* x% h
6 j3 R" X% L+ U4 z0 k, x' k
) E8 U( X3 S7 T% W- q3 G' P" t; i! L& X& c
还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。% U/ X; p, }) \4 q
使用位域的主要目的是压缩存储,其大致规则为:
5 z( H c8 r% E- k9 G1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;: G+ G) g* D5 L' b" T- `# I
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
* V2 k0 `7 n0 V& K3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;# d7 L) {6 x% v& M
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;/ r( b0 W6 K; B v; Z! c
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。0 [6 O' Q5 f. O$ g4 e; d5 ]+ h
还是让我们来看看例子。9 E+ H7 ~0 r9 o) {
例4:struct A{! r% v8 g8 j2 a9 z9 b5 O
char f1 : 3;/ Z8 C) g# ~$ ?$ e# I7 q( n
char f2 : 4;
! X3 T$ i! x; T) o# Z: v+ p5 D char f3 : 5;! y, h3 _0 G% [$ u) @' t a
};# V* b. ?5 v y" [2 z4 L
a b c
. w$ B- x" W7 h5 {6 B% ]0 WA的内存布局:111, 1111 *, 11111 * * *& u) v2 O) J8 v2 t) ^; {: Q
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。* M: N# C5 `* {/ L4 D
例5:struct B{, y5 q8 x) t- k [
char f1 : 3;
0 t. k O/ Z( w3 y/ G" k0 T& b short f2 : 4;
: J5 [% y2 S& g: W char f3 : 5;: T7 |9 \. f8 `' Q/ t1 S, ]" E4 ^
};* N4 T+ b* u( B2 |, V/ g3 D! `
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
4 C1 a# d0 V. T! y: X. c' w) N例6:struct C{, V$ U2 A- d8 X% S. W
char f1 : 3;! R- k, M; I- h6 O
char f2;$ f1 |) E4 @/ c
char f3 : 5;, m& c8 U1 q" J/ A* B$ U
};' j+ ~- U M# x+ `8 \( h
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。" L8 p3 `/ O$ \: |
5 f, v4 A9 [" g5 N B. U% b& `
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
+ {9 y% e) r8 ~8 j最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间 |
|