c - 不需要 'type' 参数且不使用 memcpy 的可移植 C SWAP 宏

标签 c c-preprocessor c89

采用类型的交换宏是众所周知的。

#define SWAP(type, a_, b_) do {        \
    type SWAP, *a = &(a_), *b = &(b_); \
    SWAP = *a;                         \
    *a = *b;                           \
    *b = SWAP;                         \
} while (0)

还有: Macro SWAP(t,x,y) exchanging two arguments of type t

是否可以在...的同时实现此功能

  • 可移植的(没有编译器特定的 typeof)
  • 不使用函数调用,例如 memcpy
    (不能保证会被优化掉,至少我的测试中没有)

我想出了一个有缺陷的方法,它使用一个定义为输入大小的结构。

#define SWAP(a_, b_) do \
{ \
    struct { \
        char dummy_data[sizeof(a_)]; \
    } SWAP, *a = (void *)(&(a_)), *b = (void *)(&(b_)); \
    /* ensure sizes match */ \
    { char t[(sizeof(a_) == sizeof(*a)) ? 1 : -1]; (void)t; } \
    /* check types are compatible */ \
    (void)(0 ? (&(a_) == &(b_)) : 0); \
    SWAP = *a; \
    *a = *b; \
    *b = SWAP; \
} while (0)

...但是如果临时 struct 被编译器填充 它可能会失败(取决于 GCC 的 __packed__ 可以工作但它不再可移植)
根据架构,它也可能存在对齐问题

最佳答案

我很想知道在什么情况下会填充这种结构,以及这种填充是否符合要求。

您可以为 sizeof(SWAP) == sizeof(a_) 添加静态检查,如果测试失败则使用 memcpy(在SWAP 结构)。

也不要在宏的主体中使用简单的名称,例如 ab,因为如果用户将这些标识符定义为宏,它们可能会被宏扩展。使用 ab 作为宏参数不会造成问题。

#define SWAP(a, b) do {                             \
    struct {                                        \
        char a_[sizeof(a)];                         \
    } SWAP, *a_ = (void *)&(a), *b_ = (void *)&(b); \
        if (sizeof(SWAP) == sizeof(a)) {            \
            SWAP = *a_; *a_ = *b_; *b_ = SWAP;      \
        } else if (a_ != b_) {                      \
            memcpy(&SWAP, a_, sizeof(a));           \
            memcpy(a_, b_, sizeof(a));              \
            memcpy(b_, &SWAP, sizeof(a));           \
        }                                           \
    } while (0)

您可能想添加对 sizeof(a) == sizeof(b) 的检查,因为穷人试图对这个过度工作的宏进行类型检查。

关于c - 不需要 'type' 参数且不使用 memcpy 的可移植 C SWAP 宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29192256/

相关文章:

c - 错误: 'for' loop initial declarations are only allowed in c99 mode

c - luaL_loadfile 还会预加载 lua 文件中的所有 dofile() 吗?

c++ - 字符串化运算符失败

c - 主要有一个参数

c - 显然 NULL 在包含的文件中是未知的

c++ - #定义: why uppercase?

c - 在调用 va_end 之前进行 longjmp 可以吗?

c - "a[0]"的意思不仅仅是 "a"吗?

c - 如何在C中正确进行SQLite SELECT查询?

c - 删除链表中的节点