Я знаю, как обменивать 2 переменных в С++, т.е. вы используете std::swap(a,b)
.
Вопрос:
Имеет ли стандартная библиотека C
аналогичная функция для С++ std::swap()
, или я должен сам ее определить.
Я знаю, как обменивать 2 переменных в С++, т.е. вы используете std::swap(a,b)
.
Вопрос:
Имеет ли стандартная библиотека C
аналогичная функция для С++ std::swap()
, или я должен сам ее определить.
В C нет эквивалента - на самом деле не может быть, так как C не имеет функций шаблона. Вам нужно будет написать отдельные функции для всех типов, которые вы хотите обменять.
Да, вам нужно определить его самостоятельно.
void swap(void* a, void* b, size_t length)
, но в отличие от std::swap
, она не безопасна для типов.inline
).Мы могли бы также определить макрос вроде
#define SWAP(a,b,type) {type ttttttttt=a;a=b;b=ttttttttt;}
но он изменяет переменную ttttttttt
, и вам нужно повторить тип a
. (В gcc there typeof(a)
, чтобы решить это, но вы все еще не можете SWAP(ttttttttt,anything_else);
.)
И писать своп на месте тоже не так сложно - это всего лишь три простых строки кода!
Вы можете сделать что-то подобное с макросом, если не возражаете использовать расширение 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;
}
Это быстро работает в 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
, потому что он зависит от указателей, но кроме этого, он общий - вы даже можете использовать его для массивов переменной длины. Он может даже обрабатывать замену идентичных переменных - не то, что вы хотели бы сделать это.
В 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
(который фактически завершал выполнение реальных вызовов функций в моих тестах, хотя компилятор может их оптимизировать).Еще один макрос, не упомянутый здесь: вам не нужно указывать тип, если вместо этого вы укажете временную переменную. Кроме того, оператор запятой полезен здесь, чтобы избежать трюка 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
Проверьте документацию компилятора. У компилятора может быть функция swapb
для обмена байтами, и я предоставляю другие подобные функции.
В худшем случае, растратите день и напишите некоторые общие функции свопинга. Он не будет потреблять значительную часть вашего графика проекта.
по сути, функция swap состоит в том, чтобы поменять два блока памяти. с двумя адресами и размером блока в байтах, мы можем поменять указатели, целые числа, парные, массивы, структуры,...
указатель имеет три части, например. мы можем сломать short* p
на три части
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
}
Вы можете сделать что-то подобное с макросом без использования временной переменной.
#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;
}