Подтвердить что ты не робот

Внедрить общий макрос свопинга в C

Возможный дубликат:
есть эквивалент std:: swap() в c

Привет, люди,

Я пытался создать общий макрос свопа в C, и мой макрос выглядит следующим образом:

#define swap(x,y) { x = x + y; y = x - y; x = x - y; }

Он отлично работает для целых чисел и поплавков, но я не уверен, есть ли в нем какой-либо улов. Что делать, если под общим макросом они подразумевают подмены указателей, символы и т.д.? Может ли кто-нибудь помочь мне написать общий макрос для замены каждого входа?

Спасибо

4b9b3361

Ответ 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