一乐电子

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

QQ登录

只需一步,快速开始

微信扫码登录

手机号码,快捷登录

手机号码,快捷登录

搜索
查看: 3478|回复: 0

结构体与函数返回值

[复制链接]
发表于 2016-6-20 21:11 | 显示全部楼层 |阅读模式
' r" Q; H% n! p
对于某些版本的C语言编译器,返回值仅能为基本数据类型如int、char以及指针,因此结构体作为一种组合数据类型,不能以值的方式返回,而在有些版本的C编译器中又可以直接返回结构体变量 ,在C++中也是可以直接返回结构体变量的。' g) ?2 H: N& W! S2 N1 D
- B% f; [* c6 I7 s/ M, ~. {
直接返回结构体变量示例如下;# f% M$ \% o0 m
typedef struct tagSTUDENT{1 c: S' O2 f: R+ j6 X. P* p1 \
char name[20];
% j1 p9 l5 h6 |- |! mint age;
' {- X  x. P- u+ s6 ]}STUDENT;6 {, ^9 }+ K2 z1 O8 v
  N0 H# V3 E* k0 K) |
STUDENT fun();9 g+ t: v( R0 c( F$ t( |5 U
int _tmain(int argc, _TCHAR* argv[])
- P( b& D( f4 J% p: D/ C{- i- D8 O: @9 h: ?& S
STUDENT p=fun();2 v! _) Y' _$ Y
printf("p.name=%s",p.name);
! ^' K& y/ B8 }! qreturn 0;. v3 t9 f! P" S* |9 q
}
4 V$ `% T- ]3 K
( ], _! x; L3 ~7 b! Q6 `( y$ vSTUDENT fun()
8 b: y% @( N# L$ a% D- P{
/ `; v# k9 z# R" H1 USTUDENT stu;, s  _$ x" s  q" s$ s. J
stu.age=18;
2 b( H9 Y: m5 o: U- v% l2 lstrcpy(stu.name,"xiaoming");
* I' W1 B% `9 C* f0 Hreturn stu;
& R+ G3 O) [1 i0 f2 G/ h}
+ s  V0 i3 g7 q
  l0 s7 `+ E, t% c8 D1 P3 z- q8 S以指针方式返回结构体示例如下:
& R/ U; L+ U8 T+ g( ntypedef struct tagSTUDENT{
/ \$ j5 V- s  O4 @+ [* |" Jchar name[20];
" O, W. a5 k5 I* I1 @; aint age;
9 R# C6 T& D% j' l  K2 g8 Z}STUDENT;
& L- ]- h) C2 e1 y9 ]( Z9 q  u, _) {
* N4 u& z+ G$ Z( v* @. r
STUDENT* fun()
9 V' J6 Y+ z7 G* }, J: i{
0 m9 O# ^0 T& `; D& @STUDENT* p=malloc(sizeof(STUDENT));
' I( @* i, l; F. z, y1 Np->age=18;
9 g3 l+ W" ~# N8 T8 jstrcpy(p->name,"xiaoming");8 O5 P# \% t1 w  b1 N1 z1 {2 Y( @
return p;9 j" q2 R+ J  P/ f* s
}1 l& e; d  ~/ J" H3 I
关于结构体,看内核又遇到了,关于赋值中存在·的奇怪用法,在网上没有找到,却把以前一直弄的比较模糊的对齐问题给翻出来了。如下为转发内容:
5 t' Z9 w7 F: Z1 ?有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):  a2 W9 S1 l! Q
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
; S" t& Q/ U& p: j/ p$ F; U原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)/ ]* E# f- D6 E0 h; r% ^
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
7 g5 ^* f: S6 |- k+ m- S- R$ V; G7 B2 G$ ^) N5 q% I
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。* _5 C* S4 |! V7 t
例1:struct{( U3 p8 N& S4 P: `  L: s: \
              short a1;
: \' [9 c! X+ y              short a2;  m9 J3 o/ K% ?; K1 T) r
              short a3;
6 X, i! @! K" O* E             }A;
, ^2 g8 f% ]7 h! I/ X/ `6 P# q0 R0 ?8 p
        struct{9 \! w# }) x3 P# e9 H7 ^
            long a1;8 w4 l9 _" q% D5 W2 A
            short a2;8 }: K5 K: T# _: W- M
           }B;* i; F2 d, m3 j7 V9 Y' G
sizeof(A) = 6; 这个很好理解,三个short都为2。1 Y0 Q1 R4 w+ ~& C
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
# g- z* N4 x" [* ~0 V2 R8 V# ]例2:struct A{
, ^" X7 x4 Z6 s) T! m' x             int a;
1 g: J; C+ M( c( q1 H* S             char b;& R, h9 y+ ^$ V, s# ^
             short c;
" s& b9 K& m& k8 E6 E' k+ j# l! M             };. ~4 M% E! j/ z9 `/ B( G( z
. ^2 F; h# h; R
        struct B{8 _+ w: Y0 T  j/ L- B$ V
            char b;" Y: ~; e8 K5 Z
            int a;% V7 I: l) m3 U  W8 \
            short c;
: }( L; x+ |; l; b) o# O             };
& U& \" o0 \& |  Ssizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。
/ G) P" D7 e0 ?7 G9 y5 X# Esizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。0 R' S6 ^* G+ l0 P% [+ s3 g7 H
深究一下,为什么是这样,我们可以看看内存里的布局情况。
  D$ ^& x1 z1 k, z  D                            a         b         c
# {# e! l# L8 r# |; Z* N+ q/ nA的内存布局:1111,     1*,       11
* |* q7 G5 a/ y# H! m# Q# ]                            b          a        c+ l- B, v+ A; @$ N8 B
B的内存布局:1***,     1111,   11**
4 ]0 T7 l. f& x$ j; N其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。4 K; j5 B4 f/ t) X5 C3 t3 J
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。) z6 K& U' g/ s
再看一个结构中含有结构成员的例子:2 a7 G# E  v+ c; Q7 Z
例3:struct A{& n7 d& Z8 E. j2 u% S: i! {) d
              int a;7 G* A+ {& \( r' U+ G2 j
              double b;) `6 |* w5 [7 e
              float c;
. A7 P% e+ B: j5 Z& l3 Z% A6 o             };
" e: H" _! o" M. q1 _         struct B{
5 ]; y; Q" \+ P* }  m              char e[2];3 H5 Z1 u5 n) I3 C2 l: K' |
              int f;
, w: l; m0 R* z/ b: W' [+ Y              double g;, h5 P" Z3 x% ]. y
              short h;
/ j6 Y3 E1 E1 u. K+ ^( o' Z: x              struct A i;1 @$ Q. j: A: L: O- B" z; ^& Z
             };) x# W& H/ s) T7 ~3 q& {
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。! f# F: O$ F: c: ^# a( E
sizeof(B) = 48; 看看B的内存布局。- j- z2 S! l. I
                          e         f             g                h                                    i
& G6 [) {0 r/ Q+ Z4 ^% h$ F" f% J# YB的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *
  Z* k# `) I1 ]: Ri其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。9 i6 U' n$ B) C; t
: A. X, K, }7 x7 d& Z! Y
以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;
1 [- H/ E: }" x# _  O( p有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
4 p9 L9 c+ |" Q+ P. G: p7 K' |$ O2 Q                           a                b             c7 ?! r( e' O( R& k  T
A的内存布局:1111,     11111111,   1111- M- I: B2 d$ a" `4 t* S" o
                          e        f             g          h                     i
. q+ w  i+ q; i/ V# [/ Z5 {B的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111
( r" h3 w9 A4 D& X6 f9 F3 O4 Q3 J那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。- [0 d0 d7 I- u
% b7 D$ i, h# ~) D4 N5 Q" U
- F# k& s& K! z

; u! p7 V% M5 g: ]! L还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。) v9 `2 D  V! D; f& Y' V' I/ b
使用位域的主要目的是压缩存储,其大致规则为:% Q6 C  ^* l% F! {# q" x* ?
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;" H) l! M8 C, l; N
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
5 x/ U( S1 \# u3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
1 O3 y, Z! q4 o" @: u  `3 M, v4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
7 a. y* B# e% I! @) E8 u  L5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
8 b& D4 `; r* F. F 还是让我们来看看例子。
; {- N6 \0 A" T5 O例4:struct A{
1 e4 w/ h* {5 _) h+ J0 h8 C" n  V               char f1 : 3;
3 }* k' t7 M- }, v# l& m7 _8 v              char f2 : 4;
, p4 `7 o2 o7 I: j' E              char f3 : 5;  a6 C3 v  h& K& h9 u
              };
: C* r0 p5 p3 x                          a         b             c
0 O; |6 @' o5 G9 N, bA的内存布局:111,    1111 *,   11111 * * *
# o; J# v- Z- O9 O位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。9 F9 _! I* c- Q5 ]1 G$ }- Z' A- k$ S
例5:struct B{" n0 T0 i+ C: [) L, x+ E: X
             char f1 : 3;3 C1 s1 }- P0 H" }* x, }$ u
             short f2 : 4;% A4 O/ n* @. W3 ~2 K" \
             char f3 : 5;
. W! n; i) Q- D8 l. G3 S6 j; i             };
: T2 p0 ~: Z& F' \由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。" ]7 K1 P/ J0 b  W
例6:struct C{$ K0 y! q3 t. c6 B0 e
              char f1 : 3;" ]7 s$ W! M1 U" P) i
              char f2;
. x5 F, F5 e% {9 Z: M3 m$ ]             char f3 : 5;
$ r4 W5 w2 t* K             };7 T  A" k( s) E1 q
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
& L9 _/ x+ @9 {) ~
# R% s; C! }7 E7 i# m, }+ j考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
9 [, Q' y: b; g最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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