使用编译时计算来将一个C/C++结构体填充到固定大小,这是否可能?

3

能否在编译时计算所需的填充数组大小,以使结构体最终达到预定大小?也就是说,我想做这样的事情(虽然知道不会起作用)。

#define APPDESCTARGETSIZE       256         // Target size for the App Desc Structure

// Structure member size of
#define msizeof(type, member) sizeof(((type *)0)->member)

// Size of padding arrary
#define padsize     (APPDESCTARGETSIZE - (offsetof(custom_app_desc_t, pad) + msizeof(custom_app_desc_t, tail)))

/**  * @brief Description about application.  */ 
typedef struct {
    uint8_t   header[16];
    char      progname[16];
    char      boardname[16];
    uint8_t   chipsize;
    uint8_t   version_maj;
    uint8_t   version_min;
    uint8_t   version_iss;
    uint8_t   pad[padsize];
    uint8_t   tail[16]; 
 } custom_app_desc_t;

我可以通过手动计算来解决,例如下面的方式,但编译器为我执行数学计算会更好:

#define padsize     (APPDESCTARGETSIZE - 68)

注意,在这种情况下,重要的是填充在“tail”成员之前发生。


4
你需要决定是在C语言中(使用宏定义)还是在C++语言中(使用模板和constexpr)完成此任务。 - flowit
你想使用/访问“pad”字段吗?请编辑您的问题以回答。也许您可以使用联合。 - Bodo
假设C语言和宏等。 - user3314691
你会遇到这样的错误:lib\RR_OTA_Update\src/custAppDesc.h:46:73: error: 'struct custom_app_desc_t' has no member named 'pad' const size_t padsize = (APPDESCTARGETSIZE - offsetof(custom_app_desc_t, pad) + msizeof(custom_app_desc_t, tail)); - user3314691
该pad成员不会被访问,因为它只是为了保留未来结构成员的空间而存在。 - user3314691
1
@KamilCuk:你能展示一下在哪里使用offsetof()吗?我在尝试中遇到了上述编译器错误。因为结构体在定义之前没有声明。所以陷入了进退两难的境地!! - user3314691
3个回答

7

解决这个问题的一种方法是使用 union

struct {
    union {
        struct {
            // this is your structure, without the last element
        };
        char _reserved[APPDESCTARGETSIZE - 16]; // exclude the last tail element
    };
    char tail[16]; // the tail is outside of the padding
};

你能解释一下为什么最后一个元素没有包含在并集中吗? - Tanveer Badar
3
你可以省略内部结构体的名称,这样它的成员就可以直接被访问。 - dbush
联合体的右花括号后缺少了一个分号。 - Ian Abbott
3
_data 不必要。 - 0___________
3
@TanveerBadar:所以它是在填充后的位置,正如问题中要求的那样。 - Eric Postpischil
我错过了那一点。谢谢! - Tanveer Badar

4
最简单的方式是将其包装在另一个结构体中。
#define APPDESCTARGETSIZE       256  
#define TAILSIZE    16
#define TAILTYPE    uint8_t

typedef struct {
    struct internal{
        uint8_t   header[16];
        char      progname[16];
        char      boardname[16];
        uint8_t   chipsize;
        uint8_t   version_maj;
        uint8_t   version_min;
        uint8_t   version_iss;
    }fields;
    uint8_t   pad[APPDESCTARGETSIZE - sizeof(struct internal) - sizeof(TAILTYPE) * TAILSIZE];
    TAILTYPE   tail[TAILSIZE]; 
 } custom_app_desc_t;

或者作为_Blindy(但使用匿名结构)

#define APPDESCTARGETSIZE       256  
#define TAILSIZE    16
#define TAILTYPE    uint8_t

typedef struct {
    union {
        struct {
            uint8_t   header[16];
            char      progname[16];
            char      boardname[16];
            uint8_t   chipsize;
            uint8_t   version_maj;
            uint8_t   version_min;
            uint8_t   version_iss;
        };
        struct {
            uint8_t _dummy[APPDESCTARGETSIZE - sizeof(TAILTYPE) * TAILSIZE];
        };
    };
    TAILTYPE   tail[TAILSIZE]; 
 } custom_app_desc_t;

1

使用未命名的联合和结构成员,您可以控制布局以适应您的目的:

#define APPDESCTARGETSIZE  256    // Target size for the App Desc Structure

/**  * @brief Description about application.  */ 
typedef struct {
    union {
        struct {
            uint8_t   header[16];
            char      progname[16];
            char      boardname[16];
            uint8_t   chipsize;
            uint8_t   version_maj;
            uint8_t   version_min;
            uint8_t   version_iss;
        };
        uint8_t   pad0[APPDESCTARGETSIZE - 16];
    };
    uint8_t   tail[16]; 
} custom_app_desc_t;

您可以像访问custom_app_desc_t的成员一样简单地访问这些成员:

    custom_app_desc_t obj;
    get_app_desc(&obj);
    printf("progname: %s\n", obj.progname);

这个方便的语法技巧称为 匿名联合匿名结构体
匿名联合的大小是其每个成员的大小中最大的,即:带有命名成员和字节数组 pad0 的结构体。 由于命名成员少于 APPDESCTARGETSIZE-16 字节,因此联合大小恰好为 APPDESCTARGETSIZE-16,这是本练习的目标。
如果需要访问填充字节,则可以在内部结构体的末尾添加一个额外的成员 uint8_t pad[1]; 如果 tail 部分不是简单的字节数组,则需要为其定义一个结构类型,并减去其大小而不是 16。

@0___________:哎呀,我看错了规格。现在已经修复了。 - chqrlie
如果我想要某种通用解决方案,我不会使用硬编码的16。 - 0___________

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接