在C中实现通用交换宏

Moh*_*per 27 c generics macros function

可能重复:
c中是否有等效的std :: swap()

嗨伙计,

我试图在C中编写一个通用交换宏的问题,我的宏看起来像这样:

#define swap(x,y) { x = x + y; y = x - y; x = x - y; }
Run Code Online (Sandbox Code Playgroud)

它适用于整数和浮点数,但我不确定它是否有任何捕获.如果通用宏意味着交换指针,字符等怎么办?任何人都可以帮我写一个通用宏来交换每个输入吗?

谢谢

ada*_*amk 77

这只适用于整数.

对于浮子,它将失败(例如,尝试使用非常大的浮子和非常小的浮子运行它).

我建议如下:

#define swap(x,y) do \ 
   { unsigned char swap_temp[sizeof(x) == sizeof(y) ? (signed)sizeof(x) : -1]; \
     memcpy(swap_temp,&y,sizeof(x)); \
     memcpy(&y,&x,       sizeof(x)); \
     memcpy(&x,swap_temp,sizeof(x)); \
    } while(0)
Run Code Online (Sandbox Code Playgroud)

当在编译时知道要复制的数量时,memcpy非常优化.此外,无需手动传递类型名称或使用特定于编译器的扩展.

  • 如果我的变量名是`swap_temp`,这将无声地失败. (14认同)
  • 为什么不做那个`swap_temp [sizeof(x)== sizeof(y)?sizeof(x): - 1]`.您将获得通用缓冲区和编译时检查它们是否相同. (8认同)
  • 问题不是"如何实现交换宏",而是"如何在C中实现交换宏".在C++中,您不实现交换宏.就这么简单.这个问题与C++无关或与C++有关.谈论C/C++,特别是在这个特定的上下文中,当C++对同一个问题采取完全不同的方法时,是非常错误的. (5认同)
  • +1是唯一正确的答案,而不是要求将类型作为参数传递给宏. (3认同)
  • @GMan,是的,这是一个真实的场景,我已经看到它发生了(不是特别与交换,但与其他宏最终有冲突).开发人员A:使用您指定的名称编写解决方案.开发人员B:看到开发人员的解决方案并说"哇,很棒的临时名称"并重新使用它.开发人员C将B的宏与A结合使用.繁荣. (2认同)
  • @Jared:那么开发商B就是白痴.为什么在地球上,实现一些*其他*的东西,我会使用名称`_swap_temp_do_not_use_this_name_`作为"伟大的临时名称"; 它里面有`swap_temp`!责备开发者A"让它无声地失败"就像它得到的那样倒退.保护马基雅维利,只有墨菲,这不是他的工作. (2认同)
  • @adamK:就像现在一样,数组绑定的表达式可以默默地将`-1`提升为`SIZE_MAX`.你必须将第二个`sizeof(x)`强制转换为有符号的类型才能使这个技巧发挥作用. (2认同)

Pau*_*l R 59

你可以这样做:

#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0)
Run Code Online (Sandbox Code Playgroud)

你会像这样调用它:

SWAP(a, b, int);
Run Code Online (Sandbox Code Playgroud)

要么:

SWAP(x, y, float);
Run Code Online (Sandbox Code Playgroud)

如果您乐意使用特定于gcc的扩展,那么您可以这样改进:

#define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0)
Run Code Online (Sandbox Code Playgroud)

然后它只会是:

SWAP(a, b);
Run Code Online (Sandbox Code Playgroud)

要么:

SWAP(x, y);
Run Code Online (Sandbox Code Playgroud)

这适用于大多数类型,包括指针.

这是一个测试程序:

#include <stdio.h>

#define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0)

int main(void)
{
    int a = 1, b = 2;
    float x = 1.0f, y = 2.0f;
    int *pa = &a;
    int *pb = &b;

    printf("BEFORE:\n");
    printf("a = %d, b = %d\n", a, b);
    printf("x = %f, y = %f\n", x, y);
    printf("pa = %p, pb = %p\n", pa, pb);

    SWAP(a, b);     // swap ints
    SWAP(x, y);     // swap floats
    SWAP(pa, pb);   // swap pointers

    printf("AFTER:\n");
    printf("a = %d, b = %d\n", a, b);
    printf("x = %f, y = %f\n", x, y);
    printf("pa = %p, pb = %p\n", pa, pb);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • @You:它是宏的规范形式,它解决了if/else结构中无效`;`的问题.如果你带走`do` /`while(0)`那么表达式如`if(foo)SWAP(x,y); 否则SWAP(a,b);`将生成错误,因为它扩展为`if(foo){...}; else {...};`和`};`紧接在`else`之前会产生编译错误. (9认同)
  • @ ideasman42:是的,这是没有这么多的阴影,这种杂牌试图解决虽然 - 问题是,如果该参数被称为一个说"TEMP"和当地的临时变量也被称为"临时",那么它打破因此需要使用尽可能接近唯一的名​​称.(当然,这可能发生在临时命名的"SWAP"中,但在这种情况下,任何人都不太可能拥有一个名为"SWAP"的变量.) (4认同)
  • @Paul R,调用变量`SWAP`,令人困惑但工作:) (2认同)

Jen*_*edt 11

GMan开始了这种尝试,将inline函数和宏结合起来进行编码.此解决方案假设您具有支持C99的现代C编译器,因为它使用复合文字:

inline void swap_detail(void* p1, void* p2, void* tmp, size_t pSize)
{
   memcpy(tmp, p1, pSize);
   memcpy(p1, p2, pSize);
   memcpy(p2 , tmp, pSize);
}
#define SWAP(a, b) swap_detail(&(a), &(b), (char[(sizeof(a) == sizeof(b)) ? (ptrdiff_t)sizeof(a) : -1]){0}, sizeof(a))
Run Code Online (Sandbox Code Playgroud)

这具有以下属性:

  • 它评估每一个ab唯一的一次.
  • 它具有正确大小的编译时检查.
  • 它没有隐藏变量的命名问题.
  • 临时变量的大小是在编译时计算的,因此复合文字不是动态数组.

(ptrdiff_t)需要演员阵容,-1以免被无声地提升SIZE_MAX.

该解决方案仍然存在两个缺点:

  1. 它不是类型安全的.它只检查类型的大小,而不是它们的语义.如果类型不同,比如说double大小为8和a uint64_t,那你就麻烦了.

  2. 表达式必须允许&运算符适用.因此,它不适用于使用register 存储类声明的变量.


Mr.*_*ama 9

简单地说:你不能在C中创建一个通用交换宏,至少,没有一些风险或头痛.(见其他帖子.解释如下.)

宏很好,但是你遇到的实际代码问题就是数据类型问题(如你所说).此外,宏在某种程度上是"愚蠢的".例如:

使用示例宏#define swap(x,y) { x = x + y; y = x - y; x = x - y; },swap(++x, y)转入{ ++x = ++x + y; y = ++x - y; ++x = ++x - y;}.

如果你跑了,int x = 0, y = 0; swap(++x, y);你会得到x=2, y=3而不是x=0, y=0.除此之外,如果宏中的任何临时变量出现在您的代码中,您可能会遇到一些烦人的错误.

您正在寻找的功能是作为模板在C++中引入的.你可以在C中得到的最接近的是对每种可以想象的数据类型使用内联函数或者是一个相当复杂的宏(参见前面的宏问题和之前的帖子).

以下是使用C++中的模板的解决方案:

template<typename T>
inline void swap(T &x, T &y)
{
    T tmp = x;
    x = y; y = tmp;
}
Run Code Online (Sandbox Code Playgroud)

在C中你需要这样的东西:

inline void swap_int(int *x, int *y) { /* Code */ }
inline void swap_char(char *x, char *y) { /* Code */ }
// etc.
Run Code Online (Sandbox Code Playgroud)

或者(如提到的那样)一个相当复杂的宏,它有可能是危险的.

  • +1"不是没有风险或头痛" (5认同)