这个宏是否违反了STRICT ALIASING RULE?

时间:2016-08-17 10:06:23

标签: c macros strict-aliasing type-punning

我修复了一些非我写的代码,所以我发现了这个:

#define get_u_int16_t(X,O)  (*(u_int16_t *)(((u_int8_t *)X) + O))

如果违反规则,如何更改规则?

以这种方式调用宏:

if(get_u_int16_t(packet->payload, i) == ...) { ... }

其中有效负载const unsigned char * i unsigned int

情况是:

struct orig {
   [...]
   struct pkt packet;
}*;

struct pkt {
   [...]
   const u_int8_t *payload;
 }*;

以这种方式打电话:

struct orig * flow;
struct pkt * packet = &flow->packet;

有效负载是一个字符串

0 的值开头,并且在有效负载长度的for循环内(u_int16_t len):

for(i = 0; i < len; i++) {
   if(get_u_int16_t(packet->payload, a) == /*value*/) {
   // do stuff
}

2 个答案:

答案 0 :(得分:2)

宏本身并没有违反严格的别名规则;这取决于你如何使用它。如果您只使用它来读取已存在的u_int16_t类型的对象或兼容类型,那么它很好;另一方面,如果你用它来阅读,例如然后是64位整数或浮点对象的一部分,这将是严格的别名冲突,以及(可能)对齐违规。

与往常一样,您可以使用memcpy

使代码安全
inline u_int16_t read_u_int16_t(const void *p) {
    u_int16_t val;
    memcpy(&val, p, sizeof(val));
    return val;
}
#define get_u_int16_t(X,O)  (read_u_int16_t(((u_int8_t *)X) + O))

正如@ 2501指出,如果u_int8_t不是字符类型,这可能无效,所以你应该只使用char *进行指针算术:

#define get_u_int16_t(X,O)  (read_u_int16_t(((char *)X) + O))

答案 1 :(得分:0)

有两种方法可以编写您感兴趣的类型的代码:

  1. 使用&#34; unsigned char *&#34;类型的指针访问任何内容,从多个字节中组合值,并容忍性能损失。

  2. 如果您知道指针对齐不成问题,请使用不会在语义上消除它的C方言。如果用-fno-strict-aliasing调用,GCC可以实现这样的方言,但是因为gcc无法防止钝化&#34;优化&#34;如果不阻止有用和明智的优化,从这种方言中获得良好的表现可能需要学习如何使用restrict

  3. 有些人会认为方法#1更好,而且对于许多PC端编程目的,我倾向于同意。然而,对于嵌入式软件,我建议远离gcc的默认方言,因为不能保证gcc今天所定义的行为将在明天保持不变。我不确定gcc的作者在哪里得到标准的作者试图指定高质量微处理器实现应该支持的所有别名形式的概念,而不是为没有形式的平台建立最小基线除了&#34; unsigned char&#34;会很有用。

相关问题