【问题标题】:How to prevent macros from hiding 'const' when casting (C)如何防止宏在投射时隐藏'const'(C)
【发布时间】:2014-06-24 14:34:49
【问题描述】:

我有一个宏,它需要 2 个参数,一个值被修改并获得我必须转换的偏移量。

#define MY_MACRO(dst, src) \
    do_something((char *)dst + offset, (char * )src + offset)

在上面的示例中,dst 将被修改,但如果值为 const,则转换为 (char *) 将隐藏此。

但是,我不希望这个宏默默地隐藏const 成员。

static void my_function(const float *a, const float *b)
{
    MY_MACRO(a, b);  /* <-- this should warn because 'a' is const */
}

我想知道是否有一种好方法可以确保在宏中转换的变量不会隐藏const

将示例解决方案移动到自己的答案中 - https://stackoverflow.com/a/25072965/432509

【问题讨论】:

  • 我怀疑你会找到更好的解决方案(使用宏),因为预处理器对const 之类的东西一无所知。另请注意,not_const 可能会被优化掉,使用一定程度的优化,编译器将不再发出警告。
  • 添加函数void do_something_helper(void *dst, const void *src, size_t offset) { do_something((char *)dst + offset, (char * )src + offset) } 并制作宏#define MY_MACRO(dst, src) do_something_helper(dst, src, offset)
  • @chux, (char *)dst 从目的地隐藏const
  • @ideasman42 do_something_helper(void *dst, ...) 中的void *dst 已经捕获了const,因此后续隐藏不是问题。
  • @chux 您的评论将非常适合作为答案。

标签: c macros casting constants


【解决方案1】:

你不能用const 类型做的一件事是什么?分配给他们。因此,如何让编译器注意到我们不应该在这个位置使用const 指针?尝试通过他们分配!

在定义中多加一行:

#define MY_MACRO(dst, src) \
    ((void)(0 ? ((void)(*(dst) = *(dst)), *(src) = *(src)) : 0), \
    do_something((char *)dst + offset, (char * )src + offset))

由于0 ? ...,插入的行将永远不会真正做任何事情(并且使用void 强制转换也不应该触发警告),但它是一个 C 级表达式,这意味着编译器 必须在优化开始前检查它;它从不运行、没有任何影响并且将在代码生成之前被删除的事实并不意味着它可以跳过类型检查。只有指向非常量的指针才会通过。

被赋值的值来自同一个指针,因此它适用于任何类型;由于这条线永远不会运行,因此我们不会从名称的多次出现中遇到任何多重评估问题。使用?: 而不是if 意味着我们可以将它放在逗号表达式中,以防do_something 需要返回一个值。

【讨论】:

  • 虽然这可行,但它会让-Wself-assign 发出警告,所以我宁愿坚持我目前的解决方案(添加到自己的答案中)。
  • 可以做一些技巧来避免-Wself-assign 例如:((void)(*(dst) = *(dst) + 1) 但是这个答案也假设值是指针,但情况并非总是如此。
  • @ideasman42 也许我今天速度很慢,但是在这种情况下你会使用什么非指针类型?想不出其他有什么意义的投到char*
【解决方案2】:

指针赋值(简单)

您可以通过分配一个虚拟的void 指针来确保指针变量不是const

/* reusable macro to ensure a var's not const */
#define CHECK_TYPE_NONCONST(var)  do {     \
    void *not_const = (0 ? (dst) : NULL);  \
    (void)not_const;                       \
} while(0)

#define MY_MACRO(dst, src) do { \
    CHECK_TYPE_NONCONST(dst); \
    do_something((char *)dst + offset, (char * )src + offset); \
} while(0)

这适用于 gcc,警告:initialization discards 'const' qualifier from pointer target type0 ? (dst) 表示我们不会将 dst 实例化为类型检查的结果(以防它是函数调用或包含增量或赋值)。


指针赋值(使用 typeof)

dst 可能是一个函数调用,在这种情况下编译器可能不会优化它,所以我们可以使用typeof 扩展(如果可用)。

/* reusable macro to ensure a var's not const */
#define CHECK_TYPE_NONCONST(var)  do {     \
    void *non_const = ((typeof(var))0);    \
    (void)non_const;                       \
} while(0)

#define MY_MACRO(dst, src) do { \
    CHECK_TYPE_NONCONST(dst); \
    do_something((char *)dst + offset, (char * )src + offset); \
} while(0)

C11_Generic

如果支持 C11 并且输入类型已知,则可以确保不会在强制转换中分配 const
通过省略 const struct SomeStruct 大小写。

#define MY_MACRO(dst, src) do { \
    (void)_Generic(dst, struct SomeStruct *: 0); \
    do_something((char *)dst + offset, (char * )src + offset); \
} while(0)

这具有对宏进行类型增强的结果(这也可能有用,具体取决于具体情况)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 2019-09-25
    • 1970-01-01
    相关资源
    最近更新 更多