【问题标题】:How could a macro return an aligned pointer to an unaligned value?宏如何将对齐的指针返回到未对齐的值?
【发布时间】:2020-01-16 02:31:09
【问题描述】:

我一直在研究现有的 C99 代码库,该代码库在各处使用指向压缩结构成员的指针。这会导致表单出现警告。

my_file.c:xxx:yy: error: taking address of packed member of 'struct some_packed_struct' may result in an unaligned pointer value [-Werror=address-of-packed-member]

大多数时候,这些警告是在将指向结构成员的指针传递给函数时生成的。例如:


int bar(const int *val)
{ ... }

struct {
    int a;
    char b;
    int c;
} __attribute__((packed)) foo;

// &foo.c is not aligned
bar(&foo.c)

这个问题有很多不同的解决方案。立即想到的一个是将memcpy() 的值传递给相同类型的堆栈分配变量,并将指针传递给该堆栈变量。

int tmp;

memcpy(&tmp, &foo.c, sizeof(foo.c));
bar(&tmp)

虽然这行得通,但会产生很多我宁愿避免引入的样板代码。

目前,我一直在考虑使用表单的宏

#define ALIGN_VALUE_PTR(val) (&((const typeof(val)) { val }))

bar(ALIGN_VALUE_PTR(foo.c));

这适用于标量类型。但是,可以预见的是,如果 valstruct,这将不起作用。

struct inner {
    int c, d;
};

struct outer {
    int a, b;
    struct inner inner;
};

void print_inner_field(const struct inner *inner)
{
    printf("value is %d\n", inner->c);
}

struct outer outer;

print_inner_field(ALIGN_VALUE_PTR(outer.inner));

lol.c:28:30: error: incompatible types when initializing type ‘int’ using type ‘struct inner’
   28 |  print_inner(ALIGN_VALUE_PTR(foo.inner));
      |                              ^~~
lol.c:3:55: note: in definition of macro ‘ALIGN_VALUE_PTR’
    3 | #define ALIGN_VALUE_PTR(val) (&((const typeof(val)) { val }))
      |                                                       ^~~

我想知道是否有人比我的想法更好。

【问题讨论】:

  • 如果你刚刚删除了__attribute__((packed))会发生什么?
  • 一个更好的主意是放弃传递指针和传递值的约定,因为如果这样有效,那么通过引用传递是没有意义的。
  • 我认为这里也存在一些 XY 问题,但是“如何制作结构的复合文字临时副本”的问题在打包结构(它们是邪恶)。
  • 使用你的 'memcpytmp' 变体,你通过引用函数来传递变量,所以可能函数可能会修改它,因此你也应该 memcpytmp 回到未对齐的结构。或者你应该问自己“为什么函数传递一个指针?” (即使它是指向 const 数据的指针)。
  • @JonathanLeffler:是的,这也是我的想法,但只要代码是 const 正确的,const 有助于避免问题。

标签: c c-preprocessor c99


【解决方案1】:
#define ALIGN_VALUE_PTR(val) (((const typeof(val) []) { val }))

您的原始版本不起作用的原因是因为在初始化程序中如何处理大括号的微妙之处,以及复合文字总是需要至少一级大括号的事实。您可以将结构初始化为

struct s bar;
//...
struct s foo = bar;

但你不能这样做:

struct s foo = { bar };

因为,在大括号内,bar 的类型必须与 struct s 的第一个成员匹配,而不是 struct s

使用数组(或结构;也有涉及无偿结构的变体)允许您匹配大括号级别以使用您想要的结构类型作为初始值设定项。数组形式当然是从数组类型开始的,只是通过衰减变成指针。如果您想确保它始终是一个指针,请添加一个无偿的+0&* 以强制衰减。

【讨论】:

    猜你喜欢
    • 2017-07-04
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    • 2017-10-02
    • 2015-11-10
    • 2013-09-20
    • 1970-01-01
    • 2017-12-23
    相关资源
    最近更新 更多