问题
Swap macro's which take a type are fairly well known.
#define SWAP(type, a_, b_) do { \
type SWAP, *a = &(a_), *b = &(b_); \
SWAP = *a; \
*a = *b; \
*b = SWAP; \
} while (0)
also: Macro SWAP(t,x,y) exchanging two arguments of type t
Is it possible to implement this functionality while being...
- portable (no compiler specific
typeof
) - without using function calls such as
memcpy
(which isn't assured to get optimized out, it wasn't in my tests at least)
I came up with a flawed method which uses a struct, defined to be the size of the input.
#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)
... but it can fail if the temporary struct
gets padded by the compiler (Depending on GCC's __packed__
would work but then its no longer portable)
It also may have issue with alignment depending on the architecture.
回答1:
I am curious to know under which circumstances such a structure would get padded, and whether such padding would be conformant.
You can add a static check for sizeof(SWAP) == sizeof(a_)
and use memcpy
if the test fails (padding was inserted at the end of the SWAP
structure).
Also do not use simple names such as a
and b
in the body of the macro as they could be macro expanded if the user defined these identifiers as macros. Using a
and b
as macro arguments does not pose a problem.
#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)
You might want to add a check for sizeof(a) == sizeof(b)
as a poor mans attempt to type checking this overworked macro.
来源:https://stackoverflow.com/questions/29192256/portable-c-swap-macro-which-doesnt-require-a-type-argument-and-doesnt-use-me