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

Есть ли встроенный способ замены двух переменных в C

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

Вопрос:

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

4b9b3361

Ответ 1

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

Ответ 2

Да, вам нужно определить его самостоятельно.

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

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

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

  • И писать своп на месте тоже не так сложно - это всего лишь три простых строки кода!

Ответ 3

Вы можете сделать что-то подобное с макросом, если не возражаете использовать расширение 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;
}

Ответ 4

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

#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 * несовместимы, компиляция завершится неудачно... при условии, что это стандартный компилятор C. К сожалению, многие компиляторы предполагают тип 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, потому что он зависит от указателей, но кроме этого, он общий - вы даже можете использовать его для массивов переменной длины. Он может даже обрабатывать замену идентичных переменных - не то, что вы хотели бы сделать это.

Ответ 5

В 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++]) не дает проблемных побочных эффектов.
  • имя переменной temp также SWAP, чтобы не вызывать ошибки, если другое имя сталкивается с выбранным жестко запрограммированным именем.
  • Он не вызывает memcpy (который фактически завершал выполнение реальных вызовов функций в моих тестах, хотя компилятор может их оптимизировать).
  • Его тип проверен (сравнение с указателями заставляет компилятор предупреждать, если они не совпадают).

Ответ 6

Еще один макрос, не упомянутый здесь: вам не нужно указывать тип, если вместо этого вы укажете временную переменную. Кроме того, оператор запятой полезен здесь, чтобы избежать трюка 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

Ответ 7

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

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

Ответ 8

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

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

  • адрес: void * p
  • size: чтение двух байтов в void*p, мы получим короткое целое число.
  • использование: например. напечатайте короткое целое число с %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
}

Ответ 9

Вы можете сделать что-то подобное с макросом без использования временной переменной.

#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;
}