C言語のswap関数とswapマクロを教えて!
こういった悩みにお答えします.
本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
目次
swapとは
swapとは,2つの変数の値を交換する手法です.
swapを関数として実装する場合,関数の引数を変数にすると,C言語では値渡しになるので正しく値を交換できません.
なので,ポインタによる参照渡しをすることで,正しく値を交換できます.
ポインタを知りたいあなたは,ポインタとはを読みましょう.
swap関数
swap関数の実装を紹介していきます.
int型のswap関数
int型の整数を交換するswap関数は以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> void swap(int *pa, int *pb) { int tmp; tmp = *pa; *pa = *pb; *pb = tmp; } int main(void) { int a = 0, b = 1; printf("pre-swap: a = %d, b = %d\n", a, b); swap(&a, &b); printf("post-swap: a = %d, b = %d\n", a, b); return 0; } |
実行結果は以下になります.
1 2 3 4 |
$ gcc swap.c $ a.out pre-swap: a = 0, b = 1 post-swap: a = 1, b = 0 |
double型のswap関数
double型の整数を交換するswap関数は以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> void swap(double *pa, double *pb) { double tmp; tmp = *pa; *pa = *pb; *pb = tmp; } int main(void) { double a = 1.1, b = 2.2; printf("pre-swap: a = %lf, b = %lf\n", a, b); swap(&a, &b); printf("post-swap: a = %lf, b = %lf\n", a, b); return 0; } |
実行結果は以下になります.
1 2 3 4 |
$ gcc swap2.c $ a.out pre-swap: a = 1.100000, b = 2.200000 post-swap: a = 2.200000, b = 1.100000 |
swapマクロ
swapを関数として実装する場合,データ型毎に関数を定義する必要があるため,コードが長くなってしまいます.
そこで,swapマクロして実装することで,1つのマクロで複数のデータ型を扱うことができます.
データ型を引数で渡すswapマクロ
データ型を引数で渡すswapマクロは以下になります.
swapマクロは,第1引数に交換するデータ型を渡していることが特徴です.
また,このswapマクロは呼び出し元と同じ関数内で実行するため,値渡しで正常に動作することに注意して下さい.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #define SWAP(type, a, b) ({ \ type tmp = (a); \ (a) = (b); \ (b) = (tmp); \ }) int main(void) { int a = 0, b = 1; double c = 1.1, d = 2.2; printf("pre-swap: a = %d, b = %d\n", a, b); SWAP(int, a, b); printf("post-swap: a = %d, b = %d\n", a, b); printf("pre-swap: c = %lf, d = %lf\n", c, d); SWAP(double, c, d); printf("post-swap: c = %lf, d = %lf\n", c, d); return 0; } |
実行結果は以下になります.
1 2 3 4 5 6 |
$ gcc swap_data_types.c $ a.out pre-swap: a = 0, b = 1 post-swap: a = 1, b = 0 pre-swap: c = 1.100000, d = 2.200000 post-swap: c = 2.200000, d = 1.100000 |
GCC拡張のtypeofを利用するswapマクロ
GCC拡張のtypeofを利用するswapマクロは以下になります.
typeofは,データ型の型を取得するGCC拡張の予約語(キーワード)です.
11行目の「(void) (&_m1 == &_m2);」は交換するデータ型が異なる場合に警告を表示するためのコードです.
データ型を引数で渡すswapマクロと同様に,このswapマクロは値渡しで正常に動作することに注意して下さい.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #define SWAP(a, b) ({ \ typeof(a) _m1 = (a); \ typeof(b) _m2 = (b); \ typeof(a) _m3; \ (void) (&_m1 == &_m2); /* warning if data types are different. */ \ _m3 = (a); \ (a) = (b); \ (b) = _m3; \ }) int main(void) { int a = 0, b = 1; double c = 1.1, d = 2.2; printf("pre-swap: a = %d, b = %d\n", a, b); SWAP(a, b); printf("post-swap: a = %d, b = %d\n", a, b); printf("pre-swap: c = %lf, d = %lf\n", c, d); SWAP(c, d); printf("post-swap: c = %lf, d = %lf\n", c, d); return 0; } |
実行結果は以下になります.
1 2 3 4 5 6 |
$ gcc swap_typeof.c $ a.out pre-swap: a = 0, b = 1 post-swap: a = 1, b = 0 pre-swap: c = 1.100000, d = 2.200000 post-swap: c = 2.200000, d = 1.100000 |
C11規格の総称選択(Generic Selection)を利用するswapマクロ
C11規格の総称選択(Generic Selection)を利用する方法を紹介します.
まずは総称選択を知りたいあなたはこちらからどうぞ.
C11規格の総称選択を利用するswapマクロは以下になります.
C++言語の関数のオーバーロードのようにデータ型毎に関数を選択します.
他のswapマクロとは異なり,このswapマクロは関数呼び出しをするため,引数にアドレスを指定していることに注意して下さい.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #define SWAP(a, b) _Generic((a), \ int *: _Generic((b), \ int *: swap, \ default: swap \ ), \ double *: _Generic((b), \ double *: swap2, \ default: swap \ ), \ default: _Generic((b), /* int * */ \ default: swap \ ) \ ) (a, b) void swap(int *pa, int *pb) { int tmp; tmp = *pa; *pa = *pb; *pb = tmp; } void swap2(double *pa, double *pb) { double tmp; tmp = *pa; *pa = *pb; *pb = tmp; } int main(void) { int a = 0, b = 1; double c = 1.1, d = 2.2; printf("pre-swap: a = %d, b = %d\n", a, b); SWAP(&a, &b); printf("post-swap: a = %d, b = %d\n", a, b); printf("pre-swap: c = %lf, d = %lf\n", c, d); SWAP(&c, &d); printf("post-swap: c = %lf, d = %lf\n", c, d); return 0; } |
実行結果は以下になります.
1 2 3 4 5 6 |
$ gcc swap_generic_selection.c $ a.out pre-swap: a = 0, b = 1 post-swap: a = 1, b = 0 pre-swap: c = 1.100000, d = 2.200000 post-swap: c = 2.200000, d = 1.100000 |
まとめ
C言語のswap関数とswapマクロで値を交換する方法を紹介しました.
swap関数では値渡しと参照渡しの違いについて,swapマクロでは以下の3種類の方法で解説しました.
- データ型を引数で渡す
- GCC拡張のtypeofを利用する
- C11規格の総称選択(Generic Selection)を利用する
あなたの実行環境で適切なswap関数やswapマクロを利用しましょう!
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!