Menu
Coddy logo textTech

ジェネリックスワップ

CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 51/61。

プログラミングでよく行う操作の一つに、2つの値の交換があります。通常は、型ごとに別々の swap 関数を書く必要があります。int 用、double 用、という具合です。しかし、void*memcpy を使えば、任意の2つの変数を交換する単一の関数を書けます。

重要な洞察は、入れ替えが単に bytes を移動させているだけだということです。data の size がわかっていれば、実際の type を知らなくても bytes をコピーできます。function のシグネチャは次のようになります:

void generic_swap(void* a, void* b, size_t size);

sizeパラメーターは、関数に移動するバイト数を指示します。内部では、一時バッファーと、バイトを入れ替えるための<string.h>memcpyを使用します。

void generic_swap(void* a, void* b, size_t size) {
    char temp[size];      // 一時バッファ
    memcpy(temp, a, size);  // temp = a
    memcpy(a, b, size);     // a = b
    memcpy(b, temp, size);  // b = temp
}

これで、この単一の関数はあらゆる型で動作します。

int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x は現在 10、y は現在 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p は現在 2.71、q は現在 3.14

size_tvoid* を使用するこのパターンは、C の標準ライブラリが qsortbsearch のような汎用関数を実装する方法です。

challenge icon

チャレンジ

簡単

型に関係なく、任意の2つの変数の値を交換できる汎用的な swap ユーティリティを、void* ポインターと memcpy の力を使って構築しましょう。

コードは3つのファイルに分けて構成します。

  • swap.h:2つの void* ポインターと、交換するデータのサイズを示す size_t パラメーターを受け取る generic_swap function を Declare します。include ガードも忘れないでください。
  • swap.cgeneric_swap function を Implement します。temporary buffer と memcpy を使って、2つのメモリ位置の間で bytes を移動します。Remember the three-step swap pattern:first の value を temp に copy し、second を first に copy し、temp を second に copy します。
  • main.c:異なるデータ型で generic swap が動作することを示します。type indicator(整数の場合は i、double の場合は d)に続けて2つの値を読み取ります。適切な type の変数を2つ作成し、generic_swap function を使ってそれらを swap し、その後、両方の値を表示します。

プログラムは3つの入力を受け取ります。

  1. type indicator:integer の場合は i、double の場合は d
  2. first value
  3. second value

swap 後、両方の値を別々の行に表示します。double の場合は小数点以下2桁を使用してください。

入力が i510 の場合の出力例:

10
5

入力が d3.142.71 の場合の出力例:

2.71
3.14

入力が i-742 の場合の出力例:

42
-7

入力が d99.990.01 の場合の出力例:

0.01
99.99

このアプローチの美しい点は、generic_swap function が、どの type を swap しているのかをまったく認識する必要がないことです。単に bytes を移動するだけです。同じ function が integer、double、struct、その他あらゆるデータ型で動作します。memcpy のために <string.h> を、size_t のために <stddef.h> または <stdlib.h> を include する必要があります。

自分で試してみよう

#include <stdio.h>
#include "swap.h"

int main() {
    char type;
    scanf("%c", &type);
    
    if (type == 'i') {
        int a, b;
        scanf("%d", &a);
        scanf("%d", &b);
        
        // TODO: generic_swap を呼び出して a と b をスワップする
        // ヒント: &a、&b、および sizeof(int) を使用する
        
        printf("%d\n", a);
        printf("%d\n", b);
    } else if (type == 'd') {
        double a, b;
        scanf("%lf", &a);
        scanf("%lf", &b);
        
        // TODO: generic_swap を呼び出して a と b をスワップする
        // ヒント: &a、&b、および sizeof(double) を使用する
        
        printf("%.2f\n", a);
        printf("%.2f\n", b);
    }
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Cオンラインコンパイラ