ジェネリックスワップ
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 は現在 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p は現在 2.71、q は現在 3.14size_t と void* を使用するこのパターンは、C の標準ライブラリが qsort や bsearch のような汎用関数を実装する方法です。
チャレンジ
簡単型に関係なく、任意の2つの変数の値を交換できる汎用的な swap ユーティリティを、void* ポインターと memcpy の力を使って構築しましょう。
コードは3つのファイルに分けて構成します。
swap.h:2つのvoid*ポインターと、交換するデータのサイズを示すsize_tパラメーターを受け取るgeneric_swapfunction を Declare します。include ガードも忘れないでください。swap.c:generic_swapfunction を 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_swapfunction を使ってそれらを swap し、その後、両方の値を表示します。
プログラムは3つの入力を受け取ります。
- type indicator:integer の場合は
i、double の場合はd - first value
- second value
swap 後、両方の値を別々の行に表示します。double の場合は小数点以下2桁を使用してください。
入力が i、5、10 の場合の出力例:
10
5入力が d、3.14、2.71 の場合の出力例:
2.71
3.14入力が i、-7、42 の場合の出力例:
42
-7入力が d、99.99、0.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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ