【问题标题】:Portable C SWAP macro which doesn't require a 'type' argument and doesn't use memcpy不需要“类型”参数且不使用 memcpy 的便携式 C SWAP 宏
【发布时间】:2015-03-30 19:31:43
【问题描述】:

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

#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__ 会起作用,但它不再可移植)
它也可能有对齐问题取决于架构

【问题讨论】:

  • 有趣的问题,但为什么?
  • 为什么没有memcpy() 但分配没问题?
  • 由于别名冲突,您的方法也会产生未定义的行为。
  • struct 只有一个字段永远不会有填充,第一个字段保证在struct 的起始地址。
  • This 是我对这个问题最喜欢的答案。我很想选择这个线程作为重复线程,它并没有真正添加任何新内容,除非您可以以良好基准的形式发布证明,证明memcpy 版本存在问题。

标签: c c-preprocessor c89


【解决方案1】:

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

您可以为sizeof(SWAP) == sizeof(a_) 添加静态检查,如果测试失败(在SWAP 结构的末尾插入填充),则使用memcpy

也不要在宏的主体中使用简单的名称,例如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) 的检查,因为一个可怜的人试图键入检查这个过度工作的宏。

【讨论】:

  • 在我当前的宏中我有类型检查,只是从示例中删除以使其更短。
  • 根据@Jens Gustedt 的说法,该结构永远不会被填充。有兴趣了解这是否属于 C 规范 - stackoverflow.com/questions/29195926
  • 如果ab 是同一个对象,此代码将失败(因为memcpy 的源和目标重叠)。
  • @Anonymous:好点! memcpy(a_, b_, sizeof(a)); 调用未定义的行为。在这种情况下,*a_ = *b_; 呢?对于x 的所有非空数据类型,x=x; 总是可以吗?
  • 你可以只添加一个额外的分支来检查身份而不做任何事情
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-20
  • 2022-01-21
相关资源
最近更新 更多