web-dev-qa-db-ja.com

Cで汎用スワップマクロを実装する

重複の可能性:
cにstd :: swap()と同等のものがあります

皆さん、こんにちは。

Cで一般的なスワップマクロを記述しようとして問題がありましたが、私のマクロは次のようになります。

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

整数と浮動小数点に対しては問題なく動作しますが、何か問題があるかどうかはわかりません。汎用マクロによって、ポインターや文字などを入れ替えることを意味する場合はどうなりますか?すべての入力をスワップするための汎用マクロを書くのを手伝ってくれる人はいますか?

ありがとう

28
Mohit Dhuper

これは整数でのみ機能します。

フロートの場合は失敗します(非常に大きいフロートと非常に小さいフロートで実行してみてください)。

私は次のように提案します:

#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はかなり最適化されます。また、型名を手動で渡す必要も、コンパイラ固有の拡張機能を使用する必要もありません。

77
adamk

あなたはこのようなことをすることができます:

#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;
}
57
Paul R

GManはこの試みを開始し、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))

これには次のプロパティがあります。

  • abのそれぞれを1回だけ評価します。
  • 正しいサイズのコンパイル時間チェックがあります。
  • 非表示の変数に関する命名の問題はありません。
  • 一時変数のサイズはコンパイル時に計算されるため、複合リテラルは動的配列ではありません。

キャスト(ptrdiff_t)は、-1が暗黙的にSIZE_MAXに昇格されないようにするために必要です。

このソリューションには、次の2つの欠点があります。

  1. タイプセーフではありません。型のサイズのみをチェックし、セマンティクスはチェックしません。タイプが異なる場合は、サイズ8のdoubleuint64_tを使用すると、問題が発生します。

  2. 式では、&演算子を適用できる必要があります。したがって、registerストレージクラスで宣言された変数では機能しません。

11
Jens Gustedt

簡単に言えば: Cでgenericスワップマクロを作成することはできません、少なくともリスクや頭痛の種がないわけではありません。 (他の投稿を参照してください。説明は以下にあります。)

マクロはいいですが、実際のコードで遭遇する問題は、データ型の問題です(前述のとおり)。さらに、マクロはある意味で「ダム」です。例えば:

サンプルマクロ#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.
_

または(数回言及したように)危険である可能性のあるかなり複雑なマクロ。

9
Mr. Llama

これは、標準に従ってintに対して必ずしも正常に機能するとは限りません。 xyがそれぞれINT_MAXINT_MAX-1であった場合を想像してみてください。最初の加算ステートメントでは、標準では定義されていない符号付きオーバーフローが発生します。

intのスワップマクロのより信頼性の高い実装は、XORスワップアルゴリズムです。

0
JaredPar