填充字节何时复制-结构分配,按值传递,其他? [英] When are pad bytes copied - struct assignment, pass by value, other?

查看:79
本文介绍了填充字节何时复制-结构分配,按值传递,其他?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在调试问题时,出现了以下问题. (请忽略较小的代码错误;该代码仅供参考.)

While debugging a problem, the following issue came up. (Please ignore minor code errors; the code is just for illustration.)

定义了以下结构:

typedef struct box_t {
  uint32_t x;
  uint16_t y;
} box_t;

此结构的实例通过值从一个函数传递到另一个函数(显然已简化):

Instances of this struct are being passed by value from function to function (obviously simplified):

void fun_a(box_t b)
{
    ... use b ...
}

void fun_b(box_t bb)
{
    // pass bb by value
    int err = funa(bb);
}

void fun_c(void)
{
    box_t real_b;
    box_t some_b[10];
    ...
    ... use real_b and some_b[]  ...
    ...
    funb(real_b);
    funb(some_b[3]);
    ...
    box_t copy_b = some_b[5];
    ...
}

在某些情况下,像这样比较box_t的两个实例:

In some cases, two instances of box_t are compared like this:

 memcmp(bm, bn, sizeof(box_t));

在几次嵌套调用中,使用类似以下的方法来转储box_t arg的字节:

Within several nested calls, the bytes of the box_t arg were dumped using something like this:

char *p = (char*) &a_box_t_arg;
for (i=0; i < sizeof(box_t); i++) {
    printf(" %02X", *p & 0xFF);
    p++;
}
printf("\n");

sizeof(box_t)是8;有2个填充字节(在uint16_t之后发现).转储表明该结构的字段相等,但填充字节不相等.这导致memcmp失败(不足为奇).

The sizeof(box_t) is 8; there are 2 pad bytes (discovered as being after the uint16_t). The dump showed that the fields of the struct were equal, but the pad bytes were not; this caused the memcmp to fail (not surprisingly).

有趣的部分是发现损坏的"填充值来自何处.向后跟踪后,发现一些box_t实例被声明为局部变量,并被初始化为:

The interesting part has been to discover where the 'corrupted' pad values came from. After tracking backwards it was discovered that some of the box_t instances were declared as local variables and were initialized like this:

box_t b;
b.x = 1;
b.y = 2;

以上内容并未(似乎)初始化填充字节,填充字节似乎包含垃圾"(分配给b的堆栈空间中的所有字节).在大多数情况下,初始化是使用memset(b, 0, sizeof(box_t))完成的.

The above does not (appear to) initialize the pad bytes, which appear to contain 'garbage' (whatever was in the stack space allocated for b). In most cases the initialization was done using memset(b, 0, sizeof(box_t)).

问题是通过(1)结构分配或(2)通过值传递来初始化box_t的实例是否总是等效于大小为sizeof(box_t)的memcpy.是否曾经只复制了真实字段"的6个字节(而不填充字节).

The question is whether initializing an instance of box_t by either (1) struct assignment or (2) passing by value will always do the equivalent of a memcpy of sizeof(box_t). Is it ever the case that only the 6 bytes of the 'real fields' are copied (and the pad bytes are not).

从调试看来,总是执行等效的memcpy sizeof(box_t).是否有任何东西(例如,在标准中)实际指定了这一点?了解随着调试的进行可以对填充字节的处理有什么帮助.

From the debugging it appears that the memcpy sizeof(box_t) equivalent is always done. Is there anything (e.g., in the standard) that actually specifies this? It would be helpful to know what can be counted on regarding the handling of the pad bytes as debugging goes forward.

谢谢! (在Ubuntu LTS 10.4 64位上使用GCC 4.4.3)

Thanks! (Using GCC 4.4.3 on Ubuntu LTS 10.4 64-bit)

奖励积分:

void f(void)
{
    box_t ba;
    box_t bb;
    box_t bc;

为3个实例分配16个字节的空间,而sizeof()显示为8.为什么要留出额外的空间?

The 3 instances are allocated 16 bytes apart while sizeof() shows 8. Why the extra space?

推荐答案

未指定填充字节的值(C99/C11 6.2.6.1§6):

The value of padding bytes is unspecified (C99/C11 6.2.6.1 §6):

当将值存储在结构或联合类型的对象中(包括在成员对象中)时,与任何填充字节对应的对象表示形式的字节将采用未指定的值.

When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.

另请参见脚注42/51(C99:TC3,C1x草案):

See also footnote 42/51 (C99:TC3, C1x draft):

因此,例如,结构分配不需要复制任何填充位.

Thus, for example, structure assignment need not copy any padding bits.

编译器可以随意复制或不复制填充文件.在x86 [1]上,我的猜测是将复制2个尾随填充字节,但不会复制4个字节(即使在32位硬件上也可能发生,因为结构可能需要8字节对齐,例如允许原子读取). double值).

The compiler is free to copy or not copy padding as it sees fit. On x86[1], my guess would be that 2 trailing padding bytes will be copied, but 4 bytes won't (which can occur even on 32-bit hardware as structures may require 8-byte alignment, eg to allow atomic reads of double values).

[1] 未执行任何实际测量.

扩展答案:

在涉及填充字节的地方,该标准不作任何保证.但是,如果使用静态存储持续时间初始化对象,则很有可能导致填充为零.但是,如果您使用该对象通过分配来初始化另一个对象,那么所有的赌注都将再次关闭(并且我希望尾随填充字节-再一次,没有进行任何测量-是特别好的候选对象,可以从复制中忽略).

The standard doesn't make any guarantees where padding bytes are concerned. However, if you initialize an object with static storage duration, the chance is high that you'll end up with zeroed padding. But if you use that object to initialize another one via assignment, all bets are off again (and I'd expect trailing padding bytes - again, no measurements done - to be particularly good candidates to be omitted from copying).

使用memset()memcpy()-即使分配给单个成员,因为这也会使填充无效-是一种在合理的实现中保证填充字节值的方法.但是,原则上,编译器可以随时更改后面"的填充值(可能与寄存器中的缓存成员有关-再次疯狂地猜测),您可以使用volatile存储.

Using memset() and memcpy() - even when assigning to individual members, as this can invalidate padding as well - is a way to guarantee the values of padding bytes on reasonable implementations. However, in principle the compiler is free to change padding values 'behind your back' any time (which might be related to caching members in registers - wildly guessing again), which you probably can avoid by using volatile storage.

合理可移植的解决方法是,通过引入适当大小的虚拟成员来显式地指定内存布局,同时使用特定于编译器的方式进行验证,即不引入其他填充(__attribute__ ((packed))-Wpadded用于gcc).

The only reasonably portable workaround I can think of is to specify the memory layout explicitly by introducing dummy members of appropriate size while verifying with compiler-specific means that no additional padding is introduced (__attribute__ ((packed)), -Wpadded for gcc).

这篇关于填充字节何时复制-结构分配,按值传递,其他?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆