일반 스왑 매크로를 C에 구현
안녕하세요 여러분.
일반 스왑 매크로를 C에 작성하는 데 문제가 발생했는데 매크로는 다음과 같습니다.
#define swap(x,y) { x = x + y; y = x - y; x = x - y; }
정수와 플로트는 잘 작동하지만 문제점이 있는지 잘 모르겠습니다.일반 매크로가 포인터, 문자 등을 교환하는 것을 의미한다면 어떨까요? 모든 입력을 교환하기 위한 일반 매크로를 작성하는 데 도움을 줄 수 있는 사람이 있습니까?
감사해요.
이것은 정수에서만 잘 작동합니다.
플로트의 경우 실패합니다(예: 매우 큰 플로트와 매우 작은 플로트를 사용하여 플로트를 실행해 보십시오).
저는 다음과 같이 제안하고 싶습니다.
#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는 compilation time에 복사할 양을 알 때 상당히 최적화됩니다.또한 수동으로 형식 이름을 전달하거나 컴파일러 고유 확장자를 사용할 필요가 없습니다.
다음과 같은 작업을 수행할 수 있습니다.
#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;
}
지맨은 이 시도를 시작했는데, 이것을 조합해서 코드화하는 것입니다.inline
함수와 매크로.이 솔루션은 복합 리터럴을 사용하므로 C99를 지원하는 최신 C 컴파일러가 있다고 가정합니다.
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
스토리지 클래스.
간단히 말해서, 당신은 적어도 약간의 위험이나 두통 없이는 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++에 템플릿으로 도입되었습니다.C에서 가장 가까이 접근할 수 있는 것은 상상할 수 있는 각 데이터 유형에 대한 인라인 함수 또는 적절하게 복잡한 매크로를 사용하는 것입니다(이전 매크로 문제 및 이전 게시물 참조).
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.
또는 (몇 번 언급한 바와 같이) 위험할 가능성이 있는 상당히 복잡한 매크로입니다.
에 가 있는 것은 .int
규격에 따라서이 경우를 상상해 보세요.x
그리고.y
이었다INT_MAX
그리고.INT_MAX-1
각각 다음과 같다.첫 번째 addition 문은 표준에 의해 정의되지 않은 서명된 오버플로를 발생시킵니다.
에 대한 int
가 될 입니다.OR 스왑 알고리즘이 될 것입니다.
정말로, 코드에서 얼마나 많은 교환을 해야만 주어진 해결책을 가지고 이 스레드에서 발생하는 모든 골칫거리를 해결할 가치가 있습니까?제 말은, 이것은 복잡하고 오류가 발생하기 쉬운 코드 구조가 아니라, 한 개의 임시 변수와 세 개의 간단한 과제가 있는 잘 알려진 관용구입니다.공간을 절약하고 싶다면 한 줄이라도 필요한 곳에 쓰십시오.
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
언급URL : https://stackoverflow.com/questions/3982348/implement-generic-swap-macro-in-c
'source' 카테고리의 다른 글
WordPress Post에서 사용자 지정 표준 URL 지정 (0) | 2023.10.03 |
---|---|
워드프레스:작성자 페이지에서 사용자 정의 분류법에 따라 포스트 카운트를 표시하는 방법 (0) | 2023.10.03 |
MySQL에 decimal을 저장하는 방법은? (0) | 2023.10.03 |
UNIX 휴대용 원자 작동 (0) | 2023.09.28 |
많은 하위 디렉터리를 별도의 새로운 Git 저장소로 분리 (0) | 2023.09.28 |