Внедрить общий макрос свопинга в C
Возможный дубликат:
есть эквивалент std:: swap() в c
Привет, люди,
Я пытался создать общий макрос свопа в C, и мой макрос выглядит следующим образом:
#define swap(x,y) { x = x + y; y = x - y; x = x - y; }
Он отлично работает для целых чисел и поплавков, но я не уверен, есть ли в нем какой-либо улов. Что делать, если под общим макросом они подразумевают подмены указателей, символы и т.д.? Может ли кто-нибудь помочь мне написать общий макрос для замены каждого входа?
Спасибо
Ответы
Ответ 1
Это хорошо работает только с целыми числами.
Для поплавков это не сработает (например, попробуйте запустить его с очень большим поплавком и очень маленьким).
Я бы предложил что-то следующее:
#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)
memcpy довольно оптимизирован, когда количество копий известно во время компиляции.
Кроме того, нет необходимости вручную передавать имя типа или использовать специальные расширения для компилятора.
Ответ 2
Вы можете сделать что-то вроде этого:
#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0)
который вы затем вызываете следующим образом:
SWAP(a, b, int);
или
SWAP(x, y, float);
Если вы счастливы использовать gcc-специфические расширения, вы можете улучшить это следующим образом:
#define SWAP(x, y) do { typeof(x) SWAP = x; x = y; y = SWAP; } while (0)
и тогда это будет просто:
SWAP(a, b);
или
SWAP(x, y);
Это работает для большинства типов, включая указатели.
Вот тестовая программа:
#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;
}
Ответ 3
GMan начал эту попытку, чтобы закодировать это в комбинации функции inline
и макроса. Это решение предполагает, что у вас есть современный компилятор C, который поддерживает C99, поскольку он использует составной литерал:
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))
Это имеет следующие свойства:
- Он оценивает только каждый из
a
и b
один раз.
- У него есть проверка времени компиляции для
правильные размеры.
- У него нет проблемы с именами со скрытым
переменная.
- Размер временной переменной
вычисляется во время компиляции, поэтому
составной литерал не является динамическим
массив.
Приведение (ptrdiff_t)
необходимо, чтобы -1
не было равномерно продвинуто до SIZE_MAX
.
Это решение по-прежнему имеет два недостатка:
-
Это не безопасный тип. Он проверяет только
для размеров типов, а не
их семантики. Если типы
отличаются, скажем, a double
размером 8 и
a uint64_t
, у вас проблемы.
-
Выражения должны позволить использовать оператор &
. таким образом
он не будет работать с переменными, объявленными с помощью register
класс хранения.
Ответ 4
Проще говоря: вы не можете сделать общий своп-макрос в 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, - это встроенная функция для каждого типа данных, который можно вообразить или достаточно сложный макрос (см. Предыдущие макрокоманды и предыдущие сообщения).
Вот что такое решение, использующее шаблоны в С++, выглядит следующим образом:
template<typename T>
inline void swap(T &x, T &y)
{
T tmp = x;
x = y; y = tmp;
}
В C вам понадобится что-то вроде:
inline void swap_int(int *x, int *y) { /* Code */ }
inline void swap_char(char *x, char *y) { /* Code */ }
// etc.
или (как уже упоминалось несколько раз) довольно сложный макрос, который может быть опасным.
Ответ 5
Это не обязательно работает нормально для int
в соответствии со стандартом. Представьте себе случай, когда x
и y
были INT_MAX
и INT_MAX-1
соответственно. Первый оператор добавления приведет к подписанному переполнению, которое является стандартом undefined.
Более надежной реализацией макроса подкачки для int
будет алгоритм замены XOR
Ответ 6
Серьезно, сколько свопов вам нужно делать в вашем коде, чтобы все головные боли возникали здесь в этом потоке с данными решениями? Я имею в виду, что это не 10-строчная сложная и подверженная ошибкам структура кода, это хорошо известная идиома с одной временной переменной и тремя простыми назначениями. Напишите его там, где он вам нужен, даже в одной строке, если вы хотите сэкономить место:
function foo ()
{
int a, b, tmp;
...
tmp = a; a = b; b = tmp;
....
}
Или используйте "локальный" макрос, где a и b более сложны.
#define SWAP(t,a,b) ( (t) = (a), (a) = (b), (b) = (t) )
function foo (pointer)
{
int tmp;
...
SWAP(tmp, pointer->structure.array[count+1], pointer->structure.array[count+2]);
...
}
#undef SWAP