填充C 中C结构的填充字节?(不关心结构包装!)



我正在使用带有3个正式记录成员的结构的库,但实际上包含4个。最后一个是用于填充的字节数组。这是C保持ABI兼容的常见技术:最后添加一堆字节,如果以后的版本将成员添加到结构中,则该填充区域会相应地缩小。总共,结构的大小保持不变。

,但是现在我必须在C 中使用此结构,并且我将其用于静态const值,因此我需要使用初始化器列表初始化它。因此,这样:

static const foo_struct foo = { 1, 2, 3 };

由于这不会初始化第四个值,因此GCC打印出来:

warning: missing initializer for member ‘foo_struct::padding’ [-Wmissing-field-initializers]

像foo(1,2,3)这样的构造函数语法是行不通的,因为这是C结构。并且将其设置为{0}也不是选项,因为我必须初始化前三个成员。

是否有C 11/C 14符合此警告的方式?

编辑:简单地使用{1、2、3、0}可能起作用,但由于填充区域没有记录,但不稳定。另外,如果将来版本添加一个会员,则最多将总结5个成员,并且警告将返回。

您只能写下这样的函数:

template <class ... T>
constexpr foo_struct make_foo_struct(T ... t) {
    return foo_struct{t..., 0};
}
static const auto foo = make_foo_struct(1, 2, 3);

您无需禁用任何警告。作为奖励,如果将另一个字段添加到结构中,则警告将返回(因为然后您将有5个成员,并且只能初始化4)。这也很方便,因为如果您要创建很多foos,并且添加了一个新领域,您不在乎(假设是布尔人,您始终想成为true),则可以更改make_foo_struct以按照方式初始化它您想要,不修改所有呼叫网站。

您当然可以在make_foo_struct中写出类型和参数名称,而不是使用T...;它使事情变得更加明确,但也需要更多的维护和较小的灵活性。

如果删除了填充物,则应该无法编译,并且您只需要修复此一个功能即可。但是,如果您不喜欢那样,另一个选择是在函数中以编译器的pragma进行警告。

template <class ... T>
constexpr foo_struct make_foo_struct(T ... t) {
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
    return foo_struct{t...};
    #pragma GCC diagnostic pop
}

和地狱,我将给出第三种选择。如果您有3个命名成员,并且他们的名字稳定,并且您只想初始化它们并将其零零,那么您可以做:

constexpr foo_struct make_foo_struct(int x, int y, int z) {
    foo_struct f{};
    f.x = x; f.y = y; f.z = z;
    return f;
}

编译器应愉快地将其优化。

c 是与C相兼容的...

由于库结构 a c struct,因此与您在C中的字节完全相同。当填充字节未记录时,它们是结构定义的一部分,使用第四个初始器(零)会很好,需要。实际上,您别无选择,只能填充PAD字节为零。

示例:

// in the library .h
struct foo_struct
{
    int first, second, third;
    unsigned char padding[256 - 3 * sizeof(int)];
};
// in your cpp
// your could do this:
static const foo_struct foo1 = { 1, ,2, 3, 0 }; // add 0, since it's needed.
// or if you really want to create this boiler plate... 
static const foo_struct foo2;   // note that it is initialized to zero at startup
                          // as all static variables are, unless a value is specified
static bool InitFoo2();
static bool fooInitialized = InitFoo2();  // it does waste some data space...
static bool InitFoo2() 
{
    p = const_cast<foo_struct*>(&foo2);
    memset(p, 0, sizeof(foo2));      // not needed in this particular case
                                         // but doesn't hurt to do be explicit.
    p->first = 6;
    p->second = 7;
    p->third = 42;
    return true;
}
// Top keep your code compatible with future version, you have no choice, 
// but to clear the padding bytes of any foo_struct before using the library.
//  Let's look at dynamic  allocation,  the required data space is the 
//  exact same size as for static memory allocation.
//
foo_struct* bar()
{
    // sizeof(foo_struct) = 256 bytes, padding MUST be reset, 
    // so your app is compatible with future versions of the library.
    //
    foo_struct* p = (foo_struct*)malloc(sizeof(foo_struct));
    if (p)
    {
        // By clearing the foo_struct this way, you do not ever need to 
        // the undocumented members.
        memset(p, 0, sizeof(*p));
        p->first = 6;
        p->second = 7;
        p->third = 42;
     }
     return p;
}

我个人去寻求此解决方案:

static const foo_struct foo = { 6, 7, 42, 0 };

最新更新