关于c下面的sizeof()

来源:百度文库 编辑:神马文学网 时间:2024/05/01 09:48:02
typedef strut a
{
    int a;
    char b;
};

为什么在dev-cpp下 sizeof(a)是8而不是5  回复人:wdscxsj 回复时间:2006-1-17 20:50:00 简单的说,就是编译器把b的大小pad到int的整数倍,这样机器存取比较高效。你如果学过微机原理就会有比较深入的理解。
   回复人:rolandlee 回复时间:2006-1-17 20:52:00 那能不能取消这种编译器优化呢。。。
   回复人:wdscxsj 回复时间:2006-1-17 21:54:00 为什么要取消呢?不是很无聊么?就像有人想知道能不能从桌面删除回收站,有这个空不如多干点正事。
在VC中,可用#pragma pack指定。详情自查。
   回复人:pcboyxhy 回复时间:2006-1-17 22:14:00 到处搜刮来的,整理了一下。

为了能使CPU对变量进行高效快速的访问,变量的起始地址应该具有某些特性,
即所谓的“对齐”。例如对于4字节的int类型变量,其起始地址应位于4字节边界上,
即起始地址能够被4整除。变量的对齐规则如下(32位系统):


Type
Alignment

char
在字节边界上对齐

short (16-bit)
在双字节边界上对齐

int and long (32-bit)
在4字节边界上对齐

float
在4字节边界上对齐

double
在8字节边界上对齐



structures
单独考虑结构体的个成员,它们在不同的字节边界上对齐。
其中最大的字节边界数就是该结构的字节边界数。
MSDN原话:Largest alignment requirement of any member
理解结构体的对齐方式有点挠头,如果结构体中有结构体成员,
那么这是一个递归的过程。
对齐方式影响结构体成员在结构体中的偏移设编译器设定的最大对齐字节边界数为n,
对于结构体中的某一成员item,它相对于结构首地址的实际字节对齐数目X应该满足
以下规则:

X = min(n, sizeof(item))

例如,对于结构体 struct {char a; int b} T;

当位于32位系统,n=8时:
a的偏移为0,
b的偏移为4,中间填充了3个字节, b的X为4;

当位于32位系统,n=2时:
a的偏移为0,
b的偏移为2,中间填充了1个字节,b的X为2;

结构体的sizeof
设结构体的最后一个成员为LastItem,其相对于结构体首地址的
偏移为offset(LastItem),其大小为sizeof(LastItem),结构体的字节对齐数为N,
则:结构体的sizeof 为: 若offset(LastItem)+ sizeof(LastItem)能够被N整除,
那么就是offset(LastItem)+ sizeof(LastItem),否则,在后面填充,
直到能够被N整除。

例如:32位系统,n=8,
结构体 struct {char a; char b;} T;
struct {char a; int b;} T1;
struct {char a; int b; char c;} T2;
sizeof(T) == 2; N = 1 没有填充
sizeof(T) == 8; N = 4 中间填充了3字节
sizeof(T2)==12; N = 4 中间,结尾各填充了3字节

注意:

1) 对于空结构体,sizeof == 1;因为必须保证结构体的每一个实例在内存中都
有独一无二的地址。

2) 结构体的静态成员不对结构体的大小产生影响,因为静态变量的存储位置与
结构体的实例地址无关。

例如:

struct {static int I;} T; struct {char a; static int I;} T1;
sizeof(T) == 1; sizeof(T1) == 1;

3) 某些编译器支持扩展指令设置变量或结构的对齐方式,如VC,
  详见MSDN(alignment of structures)

以下为Linux内核代码中的例子:

__attribute__((regparm(0))) int printk(const char * fmt, ...)\
__attribute__ ((format (printf, 1, 2)));
禁止printk使用寄存器传递调用参数,并将printk的参数1作为printf格式串,
从参数2开始检查其类型;

void __switch_to(struct task_struct *prev, struct task_struct *next)\
__attribute__((regparm(3))) ;__switch_to保留3个寄存器用作传递参数;

void __attribute__ ((__section__ (".text.init"))) mem_init();
将mem_init编绎到.text.init段;

struct tasklet_head tasklet_vec[32 ]\
__attribute__((__aligned__((32)),__section__(".data.cacheline_aligned"))) ;
将tasklet_vec[32]编绎到.data.cacheline_aligned段,并将它在32字节边界上对齐;

void do_exit(long error_code)__attribute__((noreturn));do_exit不会返回;

struct Xgt_desc_struct { unsigned short size; unsigned long\
address __attribute__((packed));};将address在结构中紧凑排列。


   回复人:pcboyxhy 回复时间:2006-1-17 22:16:00
#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n  )


Remarks

pack gives control at the data-declaration level. This differs from compiler option /Zp, which only provides module-level control. pack takes effect at the first struct, union, or class declaration after the pragma is seen; pack has no effect on definitions. Calling pack with no arguments sets n to its default value. This is equivalent to compiler option /Zp8.

Note that if you change the alignment of a structure, the structure will not use as much space in memory, but you may see a decrease in performance or even get a hardware-generated exception for unaligned access. It is possible to modify this exception behavior with SetErrorMode.

show (optional)
Displays the current byte value for packing alignment. The value is displayed by means of a warning message.

push (optional)
Puts a specified packing alignment value, n, on the internal compiler stack, and sets the current packing alignment value to n. If n is not specified, the current packing alignment value is pushed.

pop (optional)
Removes the record from the top of the internal compiler stack. If n is not specified with pop, then the packing value associated with the resulting record on the top of the stack is the new packing alignment value. If n is specified, for example, #pragma pack(pop, 16), n becomes the new packing alignment value. If you pop with identifier, for example, #pragma pack(pop, r1), then all records on the stack are popped until the record with identifier is found, and that record is popped and the packing value associated with the resulting record on the top of is the stack the new packing alignment value. If you pop with an identifier that is not found in any record on the stack, then the pop is ignored.

identifier (optional)
When used with push, assigns a name to the record on the internal compiler stack. When used with pop, pops records off the internal stack until identifier is removed; if identifier is not found on the internal stack, nothing is popped.

n(optional)
Specifies the value, in bytes, to be used for packing. The default value for n is 8. Valid values are 1, 2, 4, 8, and 16. The alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.

n can be used with push or pop for setting a particular stack value, or alone for setting the current value used by the compiler.

#pragma pack(pop, identifier, n) is undefined.