版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
& i; R: F- Q1 t* C( w7 K. \' ?对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量 ,在C++中也是可以直接返回结构体变量的。7 A6 U$ K+ D9 w
3 f, H& @* g% N, F6 N7 m
直接返回结构体变量示例如下;# N0 D4 i' g7 g+ J' c% a
typedef struct tagSTUDENT{
* C' F$ }- {: E# k# F1 z( \: a/ O8 ]. m7 fchar name[20];9 o" U; j+ Z2 \
int age;. R3 U2 {5 u' h4 \7 o0 x
}STUDENT;% q) C/ E/ p& L, ~# u5 e
5 P0 ^8 }. G" g: ^3 [$ WSTUDENT fun();
) |! ]. m% ^5 p1 F7 l1 N- iint _tmain(int argc, _TCHAR* argv[]). u9 @ S4 `9 J+ W
{
; r, u# r1 H9 X0 t7 xSTUDENT p=fun();
1 p9 S9 j9 E( N0 {' }printf("p.name=%s",p.name);$ j. g) i! c* s8 X1 L4 m% @
return 0;9 s- G; D. s; l" j5 j4 S
}8 X5 s' Z1 X4 f4 K+ X+ o* X
4 Z, d% L- z2 P q7 }STUDENT fun(): E; R! w& o2 l! B! b+ F; s
{1 a' z" f7 I; u) r
STUDENT stu;
, D1 g% i) E0 }stu.age=18;
l8 P. d0 M9 n2 V0 X6 S4 lstrcpy(stu.name,"xiaoming");: U7 Y3 s6 v* [0 S) U
return stu;% ^% z$ B" ~& s2 ]7 J- c
}( i' s9 S9 X$ A1 O0 f4 ^
- t& M# L7 l! V
以指针方式返回结构体示例如下:
. B. `# J4 l: |3 Atypedef struct tagSTUDENT{
0 K* J* H. l6 ~6 u7 M* Hchar name[20];* w/ U" e0 V/ N
int age;4 R9 A8 h( ~! {
}STUDENT;
, P3 J1 F9 [6 B+ {% \; G+ d2 a+ g8 P1 e% B# h
% t& x# X; S8 X2 E
STUDENT* fun()
^: y) m! B/ u. u1 e, [; I{, l2 j$ u0 W; W( A9 q, O8 T" [2 {7 m
STUDENT* p=malloc(sizeof(STUDENT));
! }& U" W! t1 l. P4 B5 Rp->age=18;2 }! M& w( }( T" |+ [
strcpy(p->name,"xiaoming");7 T3 a' T, q! J' g) q" @5 T
return p;
' D8 a) j3 ~- a) e* T _}" B2 M1 r/ J5 x8 {
关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:
0 t2 C! C- @/ L" m有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):
1 e: z/ e& \; o& P. s9 m& J原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。& d( d6 @) L; Z; u
原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
6 h0 P, j* L, \7 P原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。+ L( T; W6 o" @6 _
) y4 w8 O L( Z z* q这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
/ S2 n' S" P' G: p i例1:struct{
6 `" V" A) p% D/ I short a1;8 D2 c" v6 b( W7 Q- n7 o: a
short a2;
7 z+ A7 l! n r" s* r6 t& s. B short a3;# y' |! l. D$ ?, X6 Z, s: M' Q
}A;( D: _. ]% j, I+ ~" ~
6 o. } q" X7 I& J! w& c/ j struct{
( P/ C' e' _; l( h% t S long a1;
3 s3 ~* C. x4 l% s* K" H short a2;& Y; i( T( K: Z N* w- f9 h# @/ U
}B;& e7 c; @. x- X8 Y0 L5 p
sizeof(A) = 6; 这个很好理解,三个short都为2。
% I7 e3 I, |1 }9 k9 u1 msizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。% n0 a8 Y- F. j( W) P$ Y
例2:struct A{! L* x% J0 d0 l2 A( D- |7 B$ w @
int a;
5 r9 F2 `0 H, t char b;
; W' u$ D4 M+ ~7 K$ c short c;2 `' M& [, g( y' ~' ~& C
};: k" p4 x$ M, Y( l- V
4 O0 `2 x- _$ q- U- k* m5 B. T5 F struct B{& k, h) F7 s3 ~7 @4 q" j
char b;
+ N: [' z4 ^' _ int a;
+ E( z! F, d/ I! K5 h2 z2 c2 L. D short c;
0 Z( M; ]& U- L6 w# @( j. m. T {9 \ };
1 N- H: d0 q4 H$ p# L7 vsizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。, Q2 Z5 F" K( n" e2 y, i2 c3 P3 j1 _
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
0 `* D$ P: }& c+ F$ ~深究一下,为什么是这样,我们可以看看内存里的布局情况。
- h! P$ [" B2 \& A1 k, p# X a b c' B- J0 |/ t' v, P/ F7 u
A的内存布局:1111, 1*, 11
) d& G$ ~7 c7 a: x3 e+ J$ w7 f b a c
" D2 O4 X( B8 J N8 I. ?B的内存布局:1***, 1111, 11**
9 d: L/ D. n! c }5 V其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。' b2 @7 H. I5 _8 O1 y
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
, V3 s2 M$ |) D6 L. O3 v+ H再看一个结构中含有结构成员的例子:2 B0 K2 R" h9 O" b* m6 F4 D
例3:struct A{
/ |7 q# q" z1 n. m2 |, T6 M int a;
5 ?0 q' d7 S- K9 W double b;
4 D, q. U( l) A- `: x* \- }; Q float c;# r( J$ K6 D' t! C
};
* |0 Q. m" W5 Y5 V struct B{1 |: Z! r$ @: l$ I
char e[2];
4 Z2 L w' X4 ] int f;5 i+ M/ _* X: F* k9 N
double g;
' X, K: S+ m3 k- P short h;: N. X6 \: P. Y) I
struct A i;8 ? x Y5 W9 v/ Z/ T
};
1 g$ ` W% K1 Q1 I9 C2 rsizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。4 A9 p. f4 _3 J: r
sizeof(B) = 48; 看看B的内存布局。6 P; \( I2 Q$ c1 J6 R
e f g h i
# O7 N3 D0 J! ^- G' B4 T7 i0 eB的内存布局:11* *, 1111, 11111111, 11 * * * * * *, 1111* * * *, 11111111, 1111 * * * *% H2 h! Z& I$ s
i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。 p' M$ `9 ]- |1 d' N9 B: J
3 t$ R0 q, m) H( y9 J# V以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;, K8 A! C, L ^
有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
0 G# N2 I7 C: U/ c8 y' S' s a b c
! |6 g8 g( @9 e' ^/ Q$ CA的内存布局:1111, 11111111, 1111
6 n9 C2 l0 z, E$ `! ?6 k* N. v e f g h i! [, r" q) ^ q( P9 r2 P
B的内存布局:11, 1111, 11111111, 11 , 1111, 11111111, 1111; c$ I0 [1 C* s$ C- O% A
那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。
0 R# S; p) _/ p; ]& I0 V# n- G: p, ^, \" o
# W9 v. d, r% _' i
3 B. J3 K9 G5 n: \还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
$ G# n# G3 h% L- z使用位域的主要目的是压缩存储,其大致规则为:; A% `; H4 B9 k3 l
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;& V; A- \* F* e
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;) G l% w3 ~' K7 N3 @; F$ }6 I
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;/ d+ p! e" ?- p8 q% x( p
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;1 k% O6 _$ t+ I
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
6 {5 L2 R& N9 i; e1 N ?3 c 还是让我们来看看例子。
3 D. F, a* b( c, q例4:struct A{
6 s3 W) [; V- l3 s char f1 : 3;7 N& f, p& f1 z! L0 `( K7 O) h
char f2 : 4;
; b3 Z& A1 E+ _6 H char f3 : 5;, ~6 [0 H5 w. c3 q0 i
};
0 ?4 `% S; k4 w; X8 ^/ y+ i0 v' w a b c0 ` Z& ?! d$ I) G4 C
A的内存布局:111, 1111 *, 11111 * * *0 |2 H. W: `7 x
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。
% P2 ~% q: M5 d# @$ j+ Z( r例5:struct B{+ h4 p. i$ k1 l- Q
char f1 : 3;
. ~8 s$ Y( ~' z short f2 : 4;
7 u) O5 d( ~2 U0 w9 d* g char f3 : 5;% X ]) |+ v# v8 z
};2 ^- P X, ~) S$ w( l* ?3 ^
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。5 v- `# ]' g2 T1 j
例6:struct C{
4 _) u* l7 d/ Z/ E* Q1 X3 D char f1 : 3;
9 G' n9 @3 ^& \: @. M) Q char f2;4 K; b' G- ]+ f
char f3 : 5;, v& d- q9 y4 h5 z+ E) `
};+ y: L) d# v( R% _1 t+ P7 A/ I* t
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。) K# x, N) \( x, v
. P+ q+ r$ n* k$ {+ ~0 D
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
) J$ W+ G! L4 I3 Q6 \6 @- J. M4 R# h最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间 |
|