版主
主题
回帖0
积分10609
阅读权限200
注册时间2008-11-22
最后登录1970-1-1
在线时间 小时
|
1 b5 A7 ?, e K
对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量 ,在C++中也是可以直接返回结构体变量的。: Z$ k& _# i, K' a U
! I7 A4 G/ b0 i2 Z) z. P7 w: o+ `直接返回结构体变量示例如下;
% {" ~8 B! l t# D$ Y4 ~0 ftypedef struct tagSTUDENT{
' |+ Z0 V _4 b( Y; Y2 Uchar name[20];
5 X: w1 ~3 d% p3 \2 Aint age;
" Y2 J8 x! C" G}STUDENT;) v! k* a# \4 z6 A0 J" G
0 n! K2 t% f4 \- ?; b$ [& z
STUDENT fun();
1 W8 r8 ?5 a- x' |: }int _tmain(int argc, _TCHAR* argv[])
! S8 z; F5 _3 X( d) J{
5 n0 v3 N) ]) ?( J1 l* QSTUDENT p=fun();6 A. ?; q& V+ C
printf("p.name=%s",p.name);
4 T k; H# s( K# |8 J$ ireturn 0;% p2 Z4 m1 o; l# M
}
+ B8 q) }1 Q! J7 B; [* i( S; i6 ^) \# N2 ]* G0 G3 j! X
STUDENT fun()# V" C1 d. f. ?$ v% A4 f. K
{
' @4 ?. U& |- o* QSTUDENT stu;
$ ?+ M( r9 w/ c6 y* W% j, [stu.age=18;4 z* I- y4 o& h& j: F
strcpy(stu.name,"xiaoming");
& ]" f+ `9 e3 y; e: g' y7 J. Rreturn stu;& {: V; w1 A/ K' g% a2 z
}
% k- ^: M. G7 b4 h) \
* T3 I+ G e8 r* a以指针方式返回结构体示例如下:
4 U7 e9 F3 k, Qtypedef struct tagSTUDENT{* O0 A6 p6 }( ~1 U8 V
char name[20];
7 b7 i# Q$ R% s$ M. nint age;8 l( Q, n! f" q( @! b, _7 Q
}STUDENT;
+ ]3 M0 T% a+ q5 m1 `) ~* v
5 r5 t# `% T: f* Y
- g# P- j9 M; G( B; eSTUDENT* fun()
$ `9 ?+ y( S/ v9 d+ S; _( ?{
, c; _5 x& j/ Y9 v2 ~$ u1 _( TSTUDENT* p=malloc(sizeof(STUDENT));
- S# v/ M, _- s5 X' g% Z* v ]p->age=18;
]: z0 I; U6 l5 c& |; c8 ostrcpy(p->name,"xiaoming");
8 I- Z3 w+ Q& t6 i* h5 qreturn p;
% Q, Q+ n# P' ]6 I# U' k5 g}
6 t+ C6 V- Q) @1 ]关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:* F: y0 L7 E* u; X
有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):; ^; F, b: K6 z8 ]* o+ J/ Y) w& F
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
: N3 z2 s2 y! m3 Y# r原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。) m/ B! l, q# `2 r0 m* R, v
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
# B _+ p. x) W1 {
. B3 N/ b0 B% H这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。# w, S, ~0 k- d2 L) j
例1:struct{. q1 `2 d# q) u; P
short a1;! g4 a( l; f$ ]3 s* z3 E# G7 R+ z
short a2;
2 z; K) O* ?$ Y8 [ short a3;
9 B1 f9 Q' v/ [. h* }% ] }A;7 C4 ^$ d% W1 j
3 h/ @( Q) _4 Y. `
struct{8 ^6 O7 z$ U+ W$ r4 ?( u' V5 O4 G3 J
long a1;
( x2 g. m, H( Y5 Y) x short a2;" M5 m7 ^2 W1 d3 G2 n- Q" W* P
}B;+ ^& L+ d# ^* h, F: o: A
sizeof(A) = 6; 这个很好理解,三个short都为2。
( ~! P: w! t& @5 \sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
( z7 F' f; ?! x0 y. g例2:struct A{
$ u; x) P! y6 E4 { int a;% b4 f3 o" w& S( G: E. _0 {5 i4 c
char b;
4 m5 q" Q( D$ O2 d short c;
0 W1 U$ r2 P0 G };
8 q* M4 e& z9 j7 O, d, E5 g$ u
0 V) J' d) n# x3 w% U' V0 | struct B{1 y: }- @9 I8 H) f* @
char b;
5 g% D- W8 g& T. U; H3 B7 S7 | int a;0 X h% Y6 P9 [3 ~* Z
short c;, X" K! s9 N5 v4 y: K
};
: M; \$ |6 s4 I4 y% Zsizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。: Y7 i5 f/ h, \+ i$ T
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
, B8 l+ O' Q e" e% O) l% {8 Y深究一下,为什么是这样,我们可以看看内存里的布局情况。6 D( W) Y$ L( E1 h. w; K
a b c
* P$ r& x- E5 N2 Z* \A的内存布局:1111, 1*, 11$ E' J" {( ]' L4 J8 J; F" |
b a c3 q# l( K Y' R( D& u' r
B的内存布局:1***, 1111, 11**
- j! f5 D- L+ x& \7 C( r其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
/ x. o/ v9 g; S% W: u; gB中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
$ e$ Q8 N; I! h" h3 L9 x再看一个结构中含有结构成员的例子:) I$ A& n- h5 o
例3:struct A{
6 r8 x- Z; L, }) X int a;
4 ?' Y, v6 S% W. e2 S9 e3 q double b;
: q8 |, j0 R; J1 v8 O/ }3 y, U1 p float c;8 i Q+ m( b& L- j% d7 B
};$ ?) o, b+ w4 K6 [. o& q: o: h5 M
struct B{- c* x+ P1 O9 i) ~2 q! a
char e[2];5 ?- L& \" L0 }% p$ R) L
int f;9 c$ E+ }0 s9 {5 _. ?, Q8 J
double g;
# D0 a3 M! m' U" }1 z3 o: U# T short h;5 x0 s4 w- O" K5 C
struct A i;2 x. d7 P' h, d+ C! s
}; ` h: G9 [6 b& B v7 m" ]
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
; B: G+ f$ \& z3 V! O2 Z# psizeof(B) = 48; 看看B的内存布局。
9 m: S0 W. y+ n6 `1 m* Q e f g h i/ l; X/ ^" v* ? h _
B的内存布局:11* *, 1111, 11111111, 11 * * * * * *, 1111* * * *, 11111111, 1111 * * * *7 A6 b. _* b# G K
i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。- M! @, u) U+ U0 c4 J. W# H
% R4 ?; h1 ] G: B% S* F
以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;' k) H) F7 T. B ?7 Q0 s
有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。' Z5 N) e* C7 s; H! E0 J
a b c
, i7 P2 P0 }$ R( w ^A的内存布局:1111, 11111111, 11118 b' h' `& z! E8 {# J. C6 x
e f g h i+ ` u( t% E/ X" M I9 A
B的内存布局:11, 1111, 11111111, 11 , 1111, 11111111, 11119 o+ x+ y% p& ^ |- H, U$ k% h
那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。
$ ^( i/ w( A- ~: `
- Q/ ?! ?2 m3 T$ ]) f, X9 w0 x- T- Y9 A! d% o4 P: |% [, {
1 G; S T# w: J/ X
还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
/ S# ^7 I8 i- T3 S" l使用位域的主要目的是压缩存储,其大致规则为:! w7 A+ p) u0 X. q/ E$ N
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止; G& p% a/ b6 R6 I
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;4 r; {7 g* h% ?, y0 ^7 Z
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;: R* x2 j) J+ n8 i
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
, I0 o2 f a5 N# _5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
8 t* j7 V& t3 `! @7 T& I 还是让我们来看看例子。. X" J7 d9 J5 S9 Z, n
例4:struct A{
# P' H0 ~1 U* h4 T* G3 f- ^; I& a char f1 : 3;
/ |- g7 S& X+ {3 I( Q; B char f2 : 4;
# \# l6 `; Q8 X* I8 B* | char f3 : 5;/ X0 Q# E- i& x+ \: m* I
};! q* o" s/ Z6 v" |- m: V: M
a b c
: d3 E6 e5 E9 R; r1 Y( rA的内存布局:111, 1111 *, 11111 * * *) ?" r6 r: b; w" \/ m# v- r
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。
( [2 N: h9 j0 Q6 v0 J例5:struct B{$ g! w( u! G, e3 ]: `, S( E6 @1 P2 l
char f1 : 3;0 d( g H Y: g1 |6 U! a8 Z
short f2 : 4;) i& l+ _% @7 P
char f3 : 5;
* J! e1 N5 }! _4 J };
0 @$ {4 h' }) k) a) j p9 T4 b由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
J5 Q/ Y. t; A2 W7 @例6:struct C{
4 s, o3 n" {5 f, ]" s9 b8 Q4 r char f1 : 3;
6 a6 l# u4 @4 }* |$ n char f2;8 W, D! A; {0 f- ^; W( J0 y( Y1 \8 F
char f3 : 5;6 z8 ^1 F! a4 b# w) D6 D6 T
};9 M0 V M O8 u" H; S8 j
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
0 v$ I: S4 ?' R9 Z3 o+ V9 L% d$ @
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
; V6 z( F! |! O$ r( Z最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间 |
|