一乐电子

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

QQ登录

只需一步,快速开始

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

搜索
查看: 3477|回复: 0

结构体与函数返回值

[复制链接]
发表于 2016-6-20 21:11 | 显示全部楼层 |阅读模式
- W+ O) Y  {/ f6 T
对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量 ,在C++中也是可以直接返回结构体变量的。& C/ v5 ~  M0 e! g9 [
- G$ c2 J  k: V6 G8 P
直接返回结构体变量示例如下;
7 \: s$ M& `. n6 {* o7 P- {" H9 V* l; {: ytypedef struct tagSTUDENT{- L" p3 S% A% u8 U$ t4 ~
char name[20];! @$ Z& z" B! u4 [8 M! S/ G, y
int age;
; i5 X& v3 ^7 Q}STUDENT;* P* U, [: @0 \; a8 R
: O2 O7 x6 _5 L$ t6 `! O
STUDENT fun();
; N' W2 {* A6 H  T9 V, ~" Aint _tmain(int argc, _TCHAR* argv[])
7 h1 [* b2 b- c7 i" V; h6 B: _* A{
3 \6 _7 x  ~4 p8 J& TSTUDENT p=fun();6 k3 B/ _* n: b
printf("p.name=%s",p.name);+ f) c, W6 I& ^6 S+ X9 m$ k
return 0;
' Q6 d, U: L9 A+ c' t0 G0 q/ M}
' g( G7 q5 g) |$ l8 I, O
* m3 y' M  s5 w! I* _4 A$ Y/ B' J" _( OSTUDENT fun()
. G: y& D6 w; X/ [+ l{  P' L1 Y) a7 W9 C0 m+ s' k" i
STUDENT stu;
% P2 Y: t/ S8 W* z: \& U; W: R# Astu.age=18;: m* B8 R" z# K: N
strcpy(stu.name,"xiaoming");
( C# p/ U+ Q9 a! A. [1 I# b" zreturn stu;7 _' S/ @! N# z$ |$ `
}% M3 B3 {- a4 f( D9 b, U' `! |; V' ~
9 P3 x* E- N$ _6 P! \$ h
以指针方式返回结构体示例如下:
" d; b, F" ~+ ]3 A# c7 W4 C- P% Dtypedef struct tagSTUDENT{
3 B, i8 Z8 E; H% m0 n7 ]5 R: Wchar name[20];
* P: _: ?  I$ `4 Q+ W8 uint age;/ A# |% B; m! q4 B3 O) ^
}STUDENT;
- o' s( V# b" e8 B: {7 `
! C( b5 f' V+ D1 R3 b! S2 D
; V7 Z7 E- \% W8 dSTUDENT* fun()% V, N+ |6 `+ a3 q4 a4 r0 B. q
{
  P* r* s) \5 a8 gSTUDENT* p=malloc(sizeof(STUDENT));
. k! j4 y! N0 tp->age=18;' e! c* R; I9 L9 @7 y/ T; v/ N# j
strcpy(p->name,"xiaoming");0 G+ c/ [$ K, h7 k
return p;# u& ^5 B! D5 k6 ~% i! c
}
) n: A' l. e) i& L5 P关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:4 n( m4 M+ n9 P2 W
有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):6 V, @5 g9 n3 t8 [. t
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
# M" s( `# L% I. J- S+ a4 j原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
- ^% g3 M3 j; D4 s原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。# z9 C5 O' x: x6 V- T( ^
9 ?# R; m2 y  Q" C2 W
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
' ^9 ?$ ~  p) a8 [( K例1:struct{
+ s1 [/ ~: ~; V4 |3 ]* Z              short a1;5 S2 K2 {! g4 y* V7 l& W: X2 ]4 q2 G
              short a2;# E9 d& S* p2 N, P5 A
              short a3;9 d: c* ~) V  A+ y4 h. z8 m' g5 T
             }A;
, u0 q8 w: \) u# q. E1 S
  B5 e- P  t$ B4 O% V        struct{
; K0 }) v' F; C$ `% ~            long a1;( @5 b! h. W% x* C5 F4 g; H& V
            short a2;: f. t9 g6 ^% S- `2 y
           }B;
5 O' ?& v5 z5 C" v4 Wsizeof(A) = 6; 这个很好理解,三个short都为2。
  z) S* ]6 U! u" x8 Z+ L" w/ a5 Jsizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
: I, p! P4 M: p. a& I; `例2:struct A{
8 e" m; Y4 ]3 i: }             int a;2 J3 x- o6 A1 B/ p
             char b;
. E& S; y1 ?  Y) M8 M             short c;
$ @9 |( P0 \8 X$ z0 u             };
" \3 p0 t+ z, i! P4 h- \. r: y: R) Y' G
        struct B{& _5 O: r2 }( L* Z/ D, b, D4 A
            char b;; j: Z( D2 m/ y9 Y8 |) ^' p
            int a;
" W$ T& _  e+ E* Z% c            short c;& C9 P  |  M" m9 v$ A3 z
             };+ r3 U7 h$ s' G: M
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。8 A8 T. {0 W6 N, v& j) {  f! t2 g# D
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。* h/ S- ~, e) ]3 |, J# j" a5 A! \: V
深究一下,为什么是这样,我们可以看看内存里的布局情况。
( n5 d+ ]9 A; d& I, m: J                            a         b         c
8 O' @6 n5 ^) b" K& KA的内存布局:1111,     1*,       11
! u. Q6 p, E% h6 {& O* c                            b          a        c
+ }7 N% l, G0 UB的内存布局:1***,     1111,   11**
$ x0 |7 k8 g# j: }- y其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。7 s/ o" E& g, \* c% k" J# L
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。3 [/ v4 U8 |$ S7 P- k5 T
再看一个结构中含有结构成员的例子:. Z# D9 y! z. }& z5 v! w( `7 A
例3:struct A{: h+ H% |- j% n* P
              int a;
/ B! D% ?! l# q5 U              double b;% Q% B# k- r4 l$ X
              float c;: f& N6 }$ I; }2 m% u
             };) Y: P; R. Q* Q$ F+ h
         struct B{
2 f) w6 ^$ M3 X1 H1 E              char e[2];
. j9 ^) b7 e3 w+ r3 D, l              int f;+ z* q2 B% [' N9 R2 T
              double g;9 m4 _7 I8 J; b; H$ s9 ~, k5 g
              short h;
+ S: @0 y0 g' O5 J* W, X              struct A i;* c. A7 I9 T( x: ]8 D* s% j) y% J6 v
             };( l* S9 x8 X% H0 F. v
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
' e" p* ]8 G0 [# J: \2 ysizeof(B) = 48; 看看B的内存布局。: N5 @: U$ P0 ]4 a( G
                          e         f             g                h                                    i
  o  m( z2 [6 K3 R8 ]9 TB的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *
* a* V5 g) H# g1 n8 Ei其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。; E- M# ?9 h! {( i1 G

0 x+ D: w* {: f4 ^1 I0 Y$ g: U( k以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;
7 D" }! F) j# ]$ C/ a  A) L有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
# l' a/ \9 W( w4 R$ o& I( X" W/ ]1 ^                           a                b             c  d* z( h( i9 C0 C" v
A的内存布局:1111,     11111111,   1111& p" r2 u3 i9 w" [
                          e        f             g          h                     i! {% t* M* j2 B. Y
B的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111
8 F8 L0 F6 i+ P2 Z2 Y那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。+ M5 m" E& H) s& d# g" U# b
: m5 O$ v/ q0 Z! w6 @" I" |

. E4 _  g: K. A& J. \2 C- G
# y! ]( ~* i" b* r# F5 C0 ]) K, B  T还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
& s: y# v* o8 v7 z& ]: `使用位域的主要目的是压缩存储,其大致规则为:( m1 E/ S: s0 G3 I: l! t: C9 C
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
* B* d" ?. q- S& k2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
, R, q' t+ F' T* I4 h4 b% Q  |9 v8 K8 Q+ n3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;1 z8 B  p( L7 z0 v% F
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
  h4 Q& t( q  d  G# ]1 B5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
2 j, b9 S) B; B$ ]. W+ b 还是让我们来看看例子。
# B( `5 E1 F& ~# N例4:struct A{
0 m% F& |3 I, T               char f1 : 3;
+ ~" x1 J4 ?! |              char f2 : 4;
# L# S9 V0 |6 D1 d( H6 d              char f3 : 5;" F$ d# E( k0 u
              };
0 d3 y& b2 L$ J0 L, |                          a         b             c
( V1 D( k* f& lA的内存布局:111,    1111 *,   11111 * * *
" P4 e% R! K" b9 Q  h" ~位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。( j& h5 ?0 g2 E* G; r0 @
例5:struct B{/ @" t$ Q  Z9 Z9 r; ~! C4 T
             char f1 : 3;. _: J6 |6 Y: K* e: `
             short f2 : 4;) w7 M2 b4 q- w  @" v" W
             char f3 : 5;
. x9 f/ G2 V  R: t6 G5 `0 r  l  }- D             };! X% B6 G2 q4 S5 f9 Z
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
* ^( L- s* {' L3 M0 D9 E例6:struct C{
* r& B  {( q+ L" G# K% j" X3 w: ~              char f1 : 3;
4 w7 E! P, h* v2 h              char f2;
7 o" g% {3 x+ K: H  p             char f3 : 5;5 Z/ m7 k' v) |
             };
8 x0 b  n# }% L: [非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。  F7 B/ o- g; g  n8 X( g' d

" S% G, D* J) [+ G; U2 \& A考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
" T0 m3 `' q. i: y4 i最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间

本版积分规则

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

GMT+8, 2026-1-11 16:57 , Processed in 0.030325 second(s), 18 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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