Есть ли встроенный способ поменять две переменные в C - PullRequest
17 голосов
/ 14 апреля 2010

Я знаю, как поменять 2 переменные в c ++, т.е. вы используете std::swap(a,b).

вопрос:

Имеет ли стандартная библиотека C функцию, аналогичную функции C ++ std::swap(), или я должен определить ее сам.

Ответы [ 10 ]

20 голосов
/ 14 апреля 2010

Да, вы должны определить это сами.

  1. C не имеет шаблонов.
  2. Если такая функция существует, она будет выглядеть как void swap(void* a, void* b, size_t length), но, в отличие от std::swap, она не является безопасной для типов.
  3. И нет никаких намеков на то, что такая функция может быть встроена, что важно, если частая перестановка (в C99 есть ключевое слово inline).
  4. Мы также можем определить макрос как

    #define SWAP(a,b,type) {type ttttttttt=a;a=b;b=ttttttttt;}
    

    но затеняет переменную ttttttttt, и вам нужно повторить тип a. (В gcc есть typeof(a), чтобы решить это, но вы все еще не можете SWAP(ttttttttt,anything_else);.)

  5. И написание подкачки на месте не так уж сложно - это всего лишь 3 простые строки кода!

13 голосов
/ 14 апреля 2010

В C нет эквивалента - на самом деле его не может быть, так как C не имеет шаблонных функций. Вам придется написать отдельные функции для всех типов, которые вы хотите поменять местами.

9 голосов
/ 14 апреля 2010

Вы можете сделать что-то похожее с макросом, если не возражаете против использования расширения gcc для языка C, typeof:

#include <stdio.h>

#define SWAP(a, b) do { typeof(a) temp = a; a = b; b = temp; } while (0)

int main(void)
{
    int a = 4, b = 5;
    float x = 4.0f, y = 5.0f;
    char *p1 = "Hello";
    char *p2 = "World";

    SWAP(a, b); // swap two ints, a and b
    SWAP(x, y); // swap two floats, x and y
    SWAP(p1, p2); // swap two char * pointers, p1 and p2

    printf("a = %d, b = %d\n", a, b);
    printf("x = %g, y = %g\n", x, y);
    printf("p1 = %s, p2 = %s\n", p1, p2);

    return 0;
}
6 голосов
/ 04 декабря 2013

Это работает быстро в Clang и gcc (но не в icc, который не распознает эту функцию подкачки - однако, она будет компилироваться в любом стандартном компиляторе C99), при условии, что оптимизации действительно распознают подкачку (они работают на достаточно высоком уровне) уровни оптимизации).

#include <string.h>

#define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
static inline void swap_internal(void *a, void *b, size_t size) {
    char tmp[size];
    memcpy(tmp, a, size);
    memmove(a, b, size);
    memcpy(b, tmp, size);
}

Теперь объясним, как это работает. Во-первых, строка SWAP() довольно странная, но на самом деле она относительно проста. &(a) - это аргумент a, переданный как указатель. Точно так же &(b) - это аргумент b, переданный в качестве указателя.

Самый интересный фрагмент кода - sizeof *(1 ? &(a) : &(b)). На самом деле это довольно умная часть сообщения об ошибках. Если сообщение об ошибке не требуется, это может быть просто sizeof(a). Тернарный оператор требует, чтобы его операции имели совместимые типы. В этом случае я проверяю два разных аргумента на их совместимость типов путем преобразования их в указатель (в противном случае int и double будут совместимы). Поскольку int * и double * несовместимы, компиляция завершится неудачно ... при условии, что это стандартный компилятор Си. К сожалению, многие компиляторы принимают тип void * в этом случае, поэтому он терпит неудачу, но, по крайней мере, с предупреждением (которое включено по умолчанию). Чтобы обеспечить правильный размер результата, значение разыменовывается и применяется к sizeof, поэтому побочных эффектов нет.

~/c/swap $ gcc swap.c
swap.c: In function ‘main’:
swap.c:5:64: warning: pointer type mismatch in conditional expression [enabled by default]
 #define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
                                                                ^
swap.c:16:5: note: in expansion of macro ‘SWAP’
     SWAP(cat, dog);
     ^
~/c/swap $ clang swap.c
swap.c:16:5: warning: pointer type mismatch ('int *' and 'double *') [-Wpointer-type-mismatch]
    SWAP(cat, dog);
    ^~~~~~~~~~~~~~
swap.c:5:57: note: expanded from macro 'SWAP'
#define SWAP(a, b) swap_internal(&(a), &(b), sizeof *(1 ? &(a) : &(b)))
                                                        ^ ~~~~   ~~~~
1 warning generated.
~/c/swap $ icc swap.c
swap.c(16): warning #42: operand types are incompatible ("int *" and "double *")
      SWAP(cat, dog);
      ^

Этот макрос оценивает все ровно один раз (sizeof особенный, поскольку не оценивает свои аргументы). Это обеспечивает безопасность против таких аргументов, как array[something()]. Единственное ограничение, о котором я могу подумать, это то, что он не работает с register переменными, потому что он зависит от указателей, но, кроме этого, он является общим - вы даже можете использовать его для массивов переменной длины. Он может даже обрабатывать обмен одинаковыми переменными - не то, что вы хотели бы сделать это.

3 голосов
/ 22 марта 2015

В C это часто делается с помощью макроса,
есть очень упрощенные примеры, например:
#define SWAP(type,a,b) {type _tmp=a;a=b;b=_tmp;}
... но я бы не рекомендовал использовать их, потому что они имеют некоторые очевидные недостатки.

Это макрос, написанный для предотвращения случайных ошибок.

#define SWAP(type, a_, b_) \
do { \
    struct { type *a; type *b; type t; } SWAP; \
    SWAP.a  = &(a_); \
    SWAP.b  = &(b_); \
    SWAP.t  = *SWAP.a; \
    *SWAP.a = *SWAP.b; \
    *SWAP.b =  SWAP.t; \
} while (0)
  • Каждый аргумент создается только один раз,
    , поэтому SWAP(a[i++], b[j++]) не дает проблемных побочных эффектов.
  • имя временной переменной также SWAP, чтобы не вызывать ошибок, если другое имя сталкивается с жестко заданным именем.
  • Он не вызывает memcpy (что фактически привело к выполнению реальных вызовов функций в моих тестах, даже если компилятор может их оптимизировать).
  • Проверено типом
    (сравнение с указателями заставляет компилятор предупреждать, если они не совпадают).
0 голосов
/ 15 февраля 2018

В случае числовых значений (как минимум):

Я знаю, что это не фактический или полный ответ, но до сих пор все использовали временные переменные, поэтому я подумал, что Блог Криса Тейлорса может уместно упомянуть, это, безусловно, устраняет необходимость в typeof () и т. д.

a = a ^ b;
b = a ^ b;
a = a ^ b;

или

a = a + b;
b = a - b;
a = a - b;

Теоретически, я полагаю, что эти методы могут быть применены и к строкам, и к другим типам ..

Еще только три операции.

0 голосов
/ 14 сентября 2017

Вы можете сделать что-то похожее с макросом, не используя временную переменную.

#include <stdio.h>

#define SWAP(a, b) {a=a+b;b=a-b;a=a-b;} //swap macro
int main(void)
{
    int a = 4, b = 5;
    float x = 4.0f, y = 5.0f;
    char *p1 = "Hello";
    char *p2 = "World";

    a = 4, b = 5,x = 4.0f, y = 5.0f,*p1 = "Hello",*p2="world";
    SWAP(a, b); // swap two ints, a and b
    SWAP(x, y); // swap two floats, x and y
    SWAP1p1, p2); // swap two char * pointers, p1 and p2
    printf("a = %d, b = %d\n", a, b);
    printf("x = %g, y = %g\n", x, y);
    printf("p1 = %s, p2 = %s\n", p1, p2);

    return 0;
}
0 голосов
/ 13 сентября 2017

по сути, функция подкачки заключается в обмене двух блоков памяти. с двумя адресами и размером блока в байтах мы можем поменять местами указатели, целые числа, числа, массивы, структуры, ...

указатель состоит из трех частей, например мы можем разбить short* p на три части

  1. адрес: недействительный * р
  2. размер: читая два байта в void*p, мы получаем короткое целое число.
  3. использование: например выведите короткое целое число с помощью %hu

Используя первые две части, мы сможем построить общую функцию подкачки:

#include<stdint.h> 
#ifdef _WIN32
#define alloca _alloca
#else
#include <alloca.h>
#endif

void gswap(void * const a, void * const b, int const sz) {
    // for most case, 8 bytes will be sufficient.
    int64_t tmp; // equivalent to char tmp[8];
    void * p;
    bool needfree = false;
    if (sz > sizeof(int64_t)) {
        // if sz exceed 8 bytes, we allocate memory in stack with little cost.
        p = alloca(sz);
        if (p == NULL) {
            // if sz is too large to fit in stack, we fall back to use heap.
            p = malloc(sz);
            //assert(p != NULL, "not enough memory");
            needfree = true;
        }
    }
    else {
        p = &tmp;
    }

    memcpy(p, b, sz);
    memcpy(b, a, sz);
    memcpy(a, p, sz);

    if (needfree) {
        free(p);
    }

}

например:.

{// swap int 
    int a = 3;
    int b = 4;
    printf("%d,%d\n", a, b);//3,4
    gswap(&a, &b, sizeof(int));
    printf("%d,%d\n", a, b);//4,3
}
{// swap int64
    int64_t a = 3;
    int64_t b = 4;
    printf("%lld,%lld\n", a, b);//3,4
    gswap(&a, &b, sizeof(int64_t));
    printf("%lld,%lld\n", a, b);//4,3
}
{// swap arrays
    int64_t a[2] = { 3,4 };
    int64_t b[2] = { 5,6 };
    printf("%lld,%lld,%lld,%lld\n", a[0], a[1], b[0], b[1]);//3,4,5,6
    gswap(&a, &b, sizeof(a));
    printf("%lld,%lld,%lld,%lld\n", a[0], a[1], b[0], b[1]);//5,6,3,4
}
{// swap arrays
    double a[2] = { 3.,4. };
    double b[2] = { 5.,6. };
    printf("%lf,%lf,%lf,%lf\n", a[0], a[1], b[0], b[1]);//3.000000, 4.000000, 5.000000, 6.000000
    arrswap(&a, &b, sizeof(a));
    printf("%lf,%lf,%lf,%lf\n", a[0], a[1], b[0], b[1]);//5.000000, 6.000000, 3.000000, 4.000000
}
0 голосов
/ 14 апреля 2010

Проверьте документацию вашего компилятора. Компилятор может иметь функцию swapb для замены байтов, и я могу предоставить другие подобные функции.

В худшем случае, потратьте день и напишите несколько общих функций подкачки. Он не будет занимать значительную часть графика вашего проекта.

0 голосов
/ 14 апреля 2010

Еще один макрос, не упомянутый здесь: вам не нужно указывать тип, если вместо этого вы указываете временную переменную.Кроме того, оператор запятой полезен здесь, чтобы избежать трюка do-while (0).Но обычно мне все равно, и я просто пишу три команды.С другой стороны, временный макрос полезен, если a и b более сложные.

#define SWAP(a,b,t) ((t)=(a), (a)=(b), (b)=(t))

void mix_the_array (....)
{
    int tmp;
    .....
    SWAP(pointer->array[counter+17], pointer->array[counter+20], tmp);
    .....
}

#undef SWAP
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...