【问题标题】:How does this C code-block get resolved to an integer assignment?这个 C 代码块如何解析为整数赋值?
【发布时间】:2013-08-03 20:54:01
【问题描述】:

好的,在编写 C 代码的 15 年中,我从未见过这样的代码,也不知道它是如何工作的。它以一些 C99 代码为中心,其中多行代码以某种方式解析为整数值。它来自 libplist,特别是 here。好的,它从第 394 行开始,其中结构的 uint64_t 成员被分配了宏 UINT_TO_HOST 的结果

data->intval = UINT_TO_HOST(&data->intval, size);

UINT_TO_HOST 是一个宏,它接受一个指向无符号整数的指针,以及整数的字节大小。宏定义了一组不同 uint 大小的指针的联合,然后将地址设置为传递给宏的 uint 指针。然后它继续对齐内存中的 uint,以便正确地将其与另一个宏进行字节交换。 UINT_TO_HOST 在第 172 行定义

#define UINT_TO_HOST(x, n) \
({ \
    union plist_uint_ptr __up; \
    __up.src = x; \
    (n == 8 ? be64toh( get_unaligned(__up.u64ptr) ) : \
    (n == 4 ? be32toh( get_unaligned(__up.u32ptr) ) : \
    (n == 3 ? uint24_from_be( __up ) : \
    (n == 2 ? be16toh( get_unaligned(__up.u16ptr) ) : \
    *__up.u8ptr )))); \
})

get unaligned 使用 struct 和打包的 gcc 属性来实现一个技巧,它返回基于类型在内存中对齐的指针值。

  #define get_unaligned(ptr)              \
  ({                                              \
    struct __attribute__((packed)) {          \
      typeof(*(ptr)) __v;             \
    } *__p = (void *) (ptr);              \
    __p->__v;                     \
  })

be64toh 只是做了一些就地掩蔽和移位。

#define be64toh(x) ((((x) & 0xFF00000000000000ull) >> 56) \
                | (((x) & 0x00FF000000000000ull) >> 40) \
                | (((x) & 0x0000FF0000000000ull) >> 24) \
                | (((x) & 0x000000FF00000000ull) >> 8) \
                | (((x) & 0x00000000FF000000ull) << 8) \
                | (((x) & 0x0000000000FF0000ull) << 24) \
                | (((x) & 0x000000000000FF00ull) << 40) \
                | (((x) & 0x00000000000000FFull) << 56)) 

问题是 UINT_TO_HOST 如何实际将值返回到 data->intval,因为 UINT_TO_HOST 基本上可以解析为一组花括号 {} 内的 3 行代码。 get_unaligned 内部似乎也发生了同样的事情,大概是最后一条语句

__p->_v;

返回的是什么。这个功能叫什么,任何人都可以指出关于这个“功能”的一些文档吗?

【问题讨论】:

    标签: c macros block c99


    【解决方案1】:

    这称为语句表达式,它是 GNU 扩展。 Here's 一些您可能想阅读的文档。

    【讨论】:

    • 太棒了!谢谢H2CO3,这回答了我的问题。取自您在语句表达式上提供的链接:复合语句中的最后一件事应该是一个表达式,后跟一个分号;这个子表达式的值作为整个构造的值。
    • @hokey 不客气。看到您是一位资深的 C 程序员(并且可能比我更有经验):您可能对习惯良好实践感兴趣/敏感。在过去的一个半年里,你没有看到这个结构是有充分理由的。这不是标准的,也不是可移植的,因此,我建议您完全避免它。
    • 我同意。本周尝试移植代码让我非常头疼。宏有它们的位置,但它们缺乏类型安全性和其他弊端link 只会使代码难以维护、阅读并且容易出错。更不用说调试这个了。
    【解决方案2】:

    正如其他人所指出的,该宏使用“表达式中的语句”的 GCC 语法。

    但宏本身的格式不正确。 小心点。该宏的代码可以被破坏,因为内部存在{ }
    例如:

    union plist_uint_ptr __up;
    __up.src = 3;
    UINT_TO_HOST(__up.src, 8);
    

    宏展开后得到语句:__up.src = __up.src;,它只能给出一个垃圾值,因为对象__up已经在宏的块范围内重新定义了。

    最近在这里讨论过这种麻烦(第1节的第一部分,然后跳到第5节):

    How much is it possible to create fake-functions with macros in C?

    问题是内部变量__up隐藏了外部变量__up的值,宏不能按预期工作。
    因此,它是不可重用的代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-31
      • 1970-01-01
      • 1970-01-01
      • 2020-06-03
      • 1970-01-01
      • 2011-05-20
      相关资源
      最近更新 更多