2012年12月24日第1回勉強会:スライド                1
注意!      2
この発表はほぼ全編、黒魔術です!              3
しかも、あまり役に立たないことを  解説していきます!               4
なので、       5
C言語初心者は、  真に受けず、聞き流してください!             6
自己紹介       名前:koturn 0; 趣味:数学、プログラミング      エディタ:vim派 言語:C/C++/C#/Java/Python/Ruby/JavaScript/common LISP       /elisp/Vim ...
アウトライン(1)●   C言語の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #define...
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
アウトライン(1)●   C言語の文法の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #def...
三項演算子                     復習●    以下のような形で記述されます    <条件> ? <条件がtrueのときの値> : <条件がfalseのときの値>●    例:絶対値を求める三項演算子    abs_value...
アウトライン(1)●   C言語の文法の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #def...
カンマ演算子                      復習●   カンマ演算子とは、複数の式を1つの式として    扱う手段です●   以下のように記述したとき、式全体の値は値n    となります(各値は、左から1度ずつ評価され    ます)...
アウトライン(1)●   C言語の文法の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #def...
文字列の連結                    復習●   C言語では、ダブルクオートで囲んだ文字列    を2つ続けて書くと、1つの文字列とみなされ    ます    // コンパイルエラーにならず、”Hello World”と表示   ...
アウトライン(1)●   C言語の文法の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #def...
プリプロセスとは?実行バイナリができるプロセス●   実行バイナリが出来るまでの手順です     Cソースコード     Cソースコード                プリプロセス                プリプロセス     Cプリプロ...
プリプロセスとは?          コマンド●   具体的には、こういうコマンドを用います       foo.c        foo.c    $ gcc -E foo.c -o foo.i                 $ gcc -...
プリプロセスとは?          概要●   コンパイルする前の前処理です●   やっていることは、単なるソースコードの置    換です●    非常に貧弱な置換機能です●   でも、うまく使えば、おいしい!●   実は、C言語以外のソース...
アウトライン(1)●   C言語の文法の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #def...
#include                          概要●    以下のような形で使用します    #include <foo.h>   // インクルードパスの通ったところのヘッダ    #include “bar.h”   ...
#include              インクルードパス●   インクルードパスとは?    –   インクルードパスが通っているディレクトリな        ら、<>でヘッダファイルを読み込めます    –   標準ライブラリ(<stdi...
アウトライン(1)●   C言語の文法の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #def...
#define, #undef              概要●   ソースコードの置き換えを定義します●   一般的に、マクロと呼ばれる機構です●   大きく分けて、3種類のマグロがあります                          ...
#define, #undef                 概要●   ソースコードの置き換えを定義します●   一般的に、マクロと呼ばれる機構です●   大きく分けて、3種類のマグロマクロがありま    す    –   キーワードマクロ...
#define, #undef              キーワードマクロ●   キーワードのみを定義します●   主に、インクルードガードに用いられます    #define KEY_WORD●   キーワードマクロと同じ識別子を記述する  ...
#define, #undef                  定数マクロ●   C言語における、よく知られた定数の宣言方    法です    #define CONSTANT   200●   識別子自体の置き換えも可能ですが、その用   ...
#define, #undef                   関数マクロ●   マクロの醍醐味!●   関数のように、引数を取り、ソースコードを    置き換えられます    // 絶対値を返す関数マクロ    #define ABS(x...
#define, #undef        関数マクロ:注意事項(1)●   引数は、置換後の中では、丸括弧で囲いま    しょう    –   これをしないと、以下のようなマクロでは、期待        した結果が得られません    #de...
#define, #undef       関数マクロ:注意事項(1)●   2乗するマクロは以下のように書くべきです    #define SQ(x)    ((x) * (x))   // 2乗を行うマクロ●    使用例:    a = ...
#define, #undef        関数マクロ:注意事項(2)●   引数が複数回評価されることによる副作用は    避けられません    #define SQ(x)   ((x) * (x))   // 2乗を行うマクロ●   使用...
#define, #undef        関数マクロ:注意事項(3)●   引数の型をチェックすることができません    –   この理由や、複数回評価の副作用があるため、C+        +では、インライン関数を用いるのがよいです●  ...
#undef                         概要●   #undefは、#defineで定義したマクロを無効化    します●   キーワードマクロ、定数マクロ、関数マクロ    のいずれにも有効です●   以下のようにして用...
アウトライン(1)●   C言語の文法の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #def...
#if, #elif, #else, #endif                     概要●   条件コンパイルに用います    –   同じソースコードでも、複数のOSやコンパイラに        対応できます●   宣言されているマク...
#if, #elif, #else, #endif                     概要●   条件部には、数値比較やマクロが定義されて    いるかどうかを確かめるdefined演算子を用い    ることができます●   論理演算子も...
#if, #elif, #else, #endif             条件コンパイルの例●   複数のOSに対応する例として、sleep()関数を    取り上げましょう    –   sleep()関数は<unistd.h>で宣言されて...
アウトライン(1)●   C言語の文法の復習    –   三項演算子    –   カンマ演算子    –   文字列の連結●    プリプロセスの復習    –   プリプロセスとは?    –   #include    –   #def...
#ifdef, #ifndef, #endif                   概要●   #ifdefは、#if defined()の省略形です●   #ifndefは、#if !defined()の省略形です●    簡単に用いることが...
余談            改行、#if 0 ~ #endif●   プリプロセスの指定は、行末に『』を入れる    ことで、改行して続けて書くことが可能です    // FOO, BAR, BAZがいずれも10より大きいならば、    #if...
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
文字列化演算子                  概要:#●   関数マクロの引数を、ダブルクオートで囲    い、文字列化する機能です●   置換後の引数の左に、#を1つつけて記述しま    す    #define STR(x)   #x●...
文字列化演算子                 応用例:#●   使いどころが難しいですが、文字列の結合と    組み合わせるとよいでしょう●   例:    #define TRACE(var, fmt)   printf(#var “ = ...
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
トークン連結演算子               概要:##●    置換後のマクロのトークンとトークンと結合    させる演算子です●    主に、関数マクロの引数に用いられます●    例:    #define GENVAR(num)   ...
トークン連結演算子              応用例:##●   うまく使うと、記述量を劇的に減らすことが    できます!●   例:構造体の再帰代入演算●   Before:    typedef struct {      int x; ...
トークン連結演算子              応用例:##●   うまく使うと、記述量を劇的に減らすことが    できます!●   例:構造体の再帰代入演算●   After:    typedef struct {      int x; i...
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
可変引数マクロ                  概要●    関数マクロの引数を、可変にしたいことっ    て、ありますよね?●    実はできます!●    具体的には、以下のように記述します    #define err_printf(....
可変引数マクロ                  注意点●   可変引数マクロに与える引数を0個にする場合    には、注意が必要です●    例:    #define dbg_printf(fmt, ...)                ...
可変引数マクロ            省略可能にするには●   ##__VA_ARGS__と記述すると、省略可能にな    ります●    例:    #define dbg_printf(fmt, ...)                  ...
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
printlnマクロ                 意義●   printf()関数って、わざわざnをつけるのが面倒    ですよね?●   JavaのSystem.out.println()メソッドみたいに、    自動で改行をつけて欲しい...
printlnマクロ                 意義●   printf()関数って、わざわざnをつけるのが面倒    ですよね?●   JavaのSystem.out.println()メソッドみたいに、    自動で改行をつけて欲しい...
printlnマクロ                    マクロ定義●   改行つきのprintf()関数のマクロです    #define println(fmt, ...)    printf(fmt “n”, ##__VA_ARGS__...
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
unless文、until文               意義●   Rubyには、until文、unless文がありますよね?●   C言語でも欲しくないですか?                                    57
unless文、until文               意義●   Rubyには、until文、unless文がありますよね?●   C言語でも欲しくないですか?●   ええ、できますとも!●   そう、関数マクロならね           ...
unless文、until文                  マクロ定義●   カンマ演算子を利用されることを想定して、    可変引数を用います●   最後の値が、式の値になることを利用すれ    ば、以下のように定義できますね!    #...
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
デバッグ用マクロ                 意義●   デバッグ時のみ、有効にするコードを書くの    は面倒ですよね?●   以下のような#ifdef ~ #endifのコードを散在さ    せるのは嫌ですよね?    –   2行もプ...
デバッグ用マクロ                 意義●   デバッグ時のみ、有効にするコードを書くの    は面倒ですよね?●   以下のような#ifdef ~ #endifのコードを散在さ    せるのは嫌ですよね?    –   2行もプ...
デバッグ用マクロ                マクロ定義●    以下のようにすると、デバッグ時のみ有効になる    dbg_printf()関数を定義できます    #ifdef DEBUG    # define dbg_printf(....
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
SWAPマクロ                      意義●   2数を交換する関数は、以下のようになります    void swap(int *a, int *b) {      int tmp = *a;      *a = *b;  ...
SWAPマクロ                      意義●   2数を交換する関数は、以下のようになります    void swap(int *a, int *b) {      int tmp = *a;      *a = *b;  ...
SWAPマクロ                     基本形●   一番オーソドックスなSWAPマクロです    #define SWAP(type, a, b)             {                          ...
SWAPマクロ               基本形:問題点●   関数呼び出しのように使えません    –   関数呼び出しだと、アドレスを渡す必要がありま        した●   複文に置き換えられるので、セミコロンを記    述する必要が...
SWAPマクロ                基本形:解決策●   先程の問題点から、シームレスな(関数を呼び出す    ような)SWAPマクロの使用はできませんでした●   以下のように改良します    –   ポインタ型を引数に取るように作...
SWAPマクロ                  基本形:改善●   以下が改善したものとなります    #define SWAP(type, a, b)              do {                           ...
SWAPマクロ      よりシームレスにするには●   よりシームレスなSWAPマクロを目指したい!●   完成形としては、以下のような呼び出しが可    能であればよいですね    SWAP(&a, &b);●   引数の型指定を無くすには...
SWAPマクロ        よりシームレスにするには●   よりシームレスなSWAPマクロを目指したい!●   完成形としては、以下のような呼び出しが可    能であればよいですね    SWAP(&a, &b);●   引数の型指定を無くす...
SWAPマクロ                    xor SWAP●   xor(排他的論理和)を用いたSWAPアルゴリズムです    #define SWAP(a, b)       (                          ...
SWAPマクロ          xor SWAP:問題点●   同一の変数に対して用いた場合、変数の値が0    になります●   整数の変数に対してのみ、使用可能です    –   浮動小数点数と構造体には使用不可●   スピードは、一時変...
SWAPマクロ          xor SWAP:問題点●   同一の変数に対して用いた場合、変数の値が0    になります●   整数の変数に対してのみ、使用可能です    –   浮動小数点数と構造体には使用不可●   スピードは、一時変...
SWAPマクロ           xor SWAP:安全版(1)●    同一の変数かどうかをチェックするために、三項演    算子を組み込みます    #define SWAP(a, b)        (((a) == (b)) ? 0 ...
SWAPマクロ           xor SWAP:安全版(2)●   短絡評価を用いると以下のように、カッコよ    く書けます!    #define SWAP(a, b)       (((a) != (b)) && (*(a) ^= ...
SWAPマクロ                   加減算SWAP●   足し算と引き算を用いたSWAPアルゴリズムです    #define SWAP(a, b)           (                            ...
SWAPマクロ         加減算SWAP:問題点●   同一の変数に対して用いた場合、変数の値が0    になります●   整数と浮動小数点の変数に対してのみ、使用    可能です    –   構造体には使用不可●   浮動小数点数に用...
SWAPマクロ         加減算SWAP:問題点●   同一の変数に対して用いた場合、変数の値が0    になります●   整数と浮動小数点の変数に対してのみ、使用    可能です    –   構造体には使用不可   解決可能!●   ...
SWAPマクロ         加減算SWAP:安全版(1)●    同一の変数かどうかをチェックするために、三項演    算子を組み込みます    #define SWAP(a, b)           (((a) == (b)) ? 0 ...
SWAPマクロ        加減算SWAP:安全版(2)●   短絡評価を用いると以下のように、カッコよ    く書けます!    #define SWAP(a, b)       (((a) != (b)) && (*(a) += *(b)...
SWAPマクロ            GNU拡張文法SWAP●   gccのC言語拡張文法を使用したマクロです    –   typeof演算子    –   複文の式化●   シームレスかつ性能もいいです●   最強のSWAPマクロ?    ...
アウトライン(2)●    マクロテクニック    –   文字列化演算子    –   トークン連結演算子    –   可変引数マクロ●    黒魔術    –   printlnマクロ    –   until文、unless文    –...
ダフのデバイス               概要●   Tom Duffという人が考案しました●   ループのアンロールに用いられます    –   ループの条件判断回数を減らすことにより、高速        化を目指しています●   swit...
ダフのデバイス                 コード(1)●   ダフのデバイスによる、配列のコピーを行う    関数です    void duff_memcpy(int *to, const int *from, int size) {  ...
ダフのデバイス                 コード(2)●   次のように書くこともできます    void duff_memcpy(int *to, const int *from, int size) {      switch (si...
ダフのデバイス          特徴●   今回取り上げた例では、8回毎のアンロール処    理でしたが、もっと回数を増やすことも可能    です●   現代では、あまり大きく性能向上に寄与しま    せん(アンロール回数を256回毎程度にす...
ダフのデバイス          要望●   メモリコピー以外に応用したい!●   同じ処理を記述してくのはめんどくさい!                          89
ダフのデバイス          要望●   メモリコピー以外に応用したい!●   同じ処理を記述してくのはめんどくさい!●   ええ、解決できますとも!●   そう、関数マクロならね                          90
ダフのデバイス            関数マクロへの応用●   以下の<処理>の部分に、行いたい処理が来る    とよいですよね    void duff_memcpy(int *to, const int *from, int size) {...
ダフのデバイス            関数マクロへの応用●   ダフのデバイスをマクロ化すると、以下のよ    うになります    –   STATEMENT:行いたい処理(式または複文)    –   __VA_ARGS__:次のステップ前の...
ダフのデバイス            関数マクロへの応用●   使用例:    int a = 0;    DUFFS_LOOP(10, {      printf(“a = %dn”, a);    }, a++);●   複文が引数となるの...
参考文献●   プログラミング言語C 第2版                     94
参考文献●    トリッキーコードネット    –   http://tricky-code.net/●   苦しんで覚えるC言語    –   http://9cguide.appspot.com/●   C言語の小技:SWAPマクロ    ...
以上で、発表終了です!          96
どうでしたか?          97
実際のコーディングの  役に立たない   知識ばかり   でしたね?             98
でも、      99
ベテランの書いたC言語ソースを読むときに   役に立つかも    しれません               100
また、  面白トリビアとして楽しんでいただけたのなら     幸いです               101
そして何より、   関数マクロを書く助けになったのなら   嬉しいです!              102
Let,s EnjoyMeta-Programming!                    103
御清聴 ありがとうございました!  m(__)m           104
Upcoming SlideShare
Loading in …5
×

第1回勉強会スライド

1,539 views

Published on

大学で行った勉強会のスライドです。
C言語のマクロについて語っています。
C言語は得意ではないので、用語の使い方が変なところもあるかもしれません。

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,539
On SlideShare
0
From Embeds
0
Number of Embeds
18
Actions
Shares
0
Downloads
9
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

第1回勉強会スライド

  1. 1. 2012年12月24日第1回勉強会:スライド 1
  2. 2. 注意! 2
  3. 3. この発表はほぼ全編、黒魔術です! 3
  4. 4. しかも、あまり役に立たないことを 解説していきます! 4
  5. 5. なので、 5
  6. 6. C言語初心者は、 真に受けず、聞き流してください! 6
  7. 7. 自己紹介 名前:koturn 0; 趣味:数学、プログラミング エディタ:vim派 言語:C/C++/C#/Java/Python/Ruby/JavaScript/common LISP /elisp/Vim script 7
  8. 8. アウトライン(1)● C言語の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 8
  9. 9. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 9
  10. 10. アウトライン(1)● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 10
  11. 11. 三項演算子 復習● 以下のような形で記述されます <条件> ? <条件がtrueのときの値> : <条件がfalseのときの値>● 例:絶対値を求める三項演算子 abs_value = x > 0 ? x : -x;● 簡単な条件分岐であれば、if ~ elseより簡潔に 記述可能です● 式でありながら、条件分岐できるのが強み! – 基本的に文を使えないマクロに適用しやすい! 11
  12. 12. アウトライン(1)● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 12
  13. 13. カンマ演算子 復習● カンマ演算子とは、複数の式を1つの式として 扱う手段です● 以下のように記述したとき、式全体の値は値n となります(各値は、左から1度ずつ評価され ます) (値1, 値2, 値3, … , 値n)● 以下のように記述したとき、xに代入されるの は3となります x = (1, printf(“foo”), 2, printf(“bar”), 3);● 13
  14. 14. アウトライン(1)● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 14
  15. 15. 文字列の連結 復習● C言語では、ダブルクオートで囲んだ文字列 を2つ続けて書くと、1つの文字列とみなされ ます // コンパイルエラーにならず、”Hello World”と表示 printf(“Hello” “World!” “n”);● 1回の関数呼び出しで、大量の文字列を渡すと きに便利です – 読みやすく書くことが出来ます puts( “usage:n” “[option]n” “ -o : output filen” “ -s : sentence file” ); 15
  16. 16. アウトライン(1)● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 16
  17. 17. プリプロセスとは?実行バイナリができるプロセス● 実行バイナリが出来るまでの手順です Cソースコード Cソースコード プリプロセス プリプロセス Cプリプロセス Cプリプロセス ソースコード ソースコード コンパイル コンパイル アセンブリコード アセンブリコード アセンブル アセンブル オブジェクト オブジェクト ファイル ファイル リンク リンク 実行 実行 バイナリ バイナリ 17
  18. 18. プリプロセスとは? コマンド● 具体的には、こういうコマンドを用います foo.c foo.c $ gcc -E foo.c -o foo.i $ gcc -E foo.c -o foo.i foo.i foo.i $ gcc -S foo.i -o foo.s $ gcc -S foo.i -o foo.s foo.s foo.s $ gcc -c foo.s -o foo.o $ gcc -c foo.s -o foo.o foo.o foo.o $ gcc foo.o -o foo.out $ gcc foo.o -o foo.out foo.out foo.out 18
  19. 19. プリプロセスとは? 概要● コンパイルする前の前処理です● やっていることは、単なるソースコードの置 換です● 非常に貧弱な置換機能です● でも、うまく使えば、おいしい!● 実は、C言語以外のソースコードにも適用可 能です(Javaのソースコードなど) 19
  20. 20. アウトライン(1)● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 20
  21. 21. #include 概要● 以下のような形で使用します #include <foo.h> // インクルードパスの通ったところのヘッダ #include “bar.h” // ユーザ定義のヘッダ● #define行は、指定ファイルの内容に置き換えら れます● <>は、インクルードパスの通ったところからの インクルードです(標準ライブラリ等)● “”は、カレントディレクトリを基準にした、 ユーザ定義のヘッダファイルのインクルードで す 21
  22. 22. #include インクルードパス● インクルードパスとは? – インクルードパスが通っているディレクトリな ら、<>でヘッダファイルを読み込めます – 標準ライブラリ(<stdio.h>、<stdlib.h>など)● インクルードパスを追加するには、コンパイ ラに、オプションとして与えます● 例:gccの場合:-Iオプション $ gcc -I./hoge/fuga includetest.c -o includetest.out 22
  23. 23. アウトライン(1)● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 23
  24. 24. #define, #undef 概要● ソースコードの置き換えを定義します● 一般的に、マクロと呼ばれる機構です● 大きく分けて、3種類のマグロがあります 24
  25. 25. #define, #undef 概要● ソースコードの置き換えを定義します● 一般的に、マクロと呼ばれる機構です● 大きく分けて、3種類のマグロマクロがありま す – キーワードマクロ – 定数マクロ – 関数マクロ 25
  26. 26. #define, #undef キーワードマクロ● キーワードのみを定義します● 主に、インクルードガードに用いられます #define KEY_WORD● キーワードマクロと同じ識別子を記述する と、その識別子は無くなります● 次に述べる、定数マクロの定数部分が無いも のと考えてよいでしょう 26
  27. 27. #define, #undef 定数マクロ● C言語における、よく知られた定数の宣言方 法です #define CONSTANT 200● 識別子自体の置き換えも可能ですが、その用 途で使われることは少ないです #define koturn return // koturn 0;が可能になります 27
  28. 28. #define, #undef 関数マクロ● マクロの醍醐味!● 関数のように、引数を取り、ソースコードを 置き換えられます // 絶対値を返す関数マクロ #define ABS(x) ((x) > 0 ? (x) : -(x))● 使用例: abs_value = ABS(-3);● プリプロセス後、以下のように置換されます abs_value = ((-3) > 0 ? (-3) : -(-3)); 28
  29. 29. #define, #undef 関数マクロ:注意事項(1)● 引数は、置換後の中では、丸括弧で囲いま しょう – これをしないと、以下のようなマクロでは、期待 した結果が得られません #define SQ(x) (x * x) // 2乗を行うマクロのつもり● 使用例: a = SQ(3 + 5); // aが64になることを期待● 展開結果: a = (3 + 5 * 3 + 5); // aは23になる 29
  30. 30. #define, #undef 関数マクロ:注意事項(1)● 2乗するマクロは以下のように書くべきです #define SQ(x) ((x) * (x)) // 2乗を行うマクロ● 使用例: a = SQ(3 + 5); // aが64になることを期待● 展開結果: a = ((3 + 5) * (3 + 5)); // aは64になる 30
  31. 31. #define, #undef 関数マクロ:注意事項(2)● 引数が複数回評価されることによる副作用は 避けられません #define SQ(x) ((x) * (x)) // 2乗を行うマクロ● 使用例: int n = 5; a = SQ(++n); // nが6になり、aが36になることを期待● 展開結果: – インクリメントがどのタイミングで評価されるか は、未定義動作です a = ((++n) * (++n)); // nは7になり、aは49になる 31
  32. 32. #define, #undef 関数マクロ:注意事項(3)● 引数の型をチェックすることができません – この理由や、複数回評価の副作用があるため、C+ +では、インライン関数を用いるのがよいです● しかし、関数マクロにしかできないこともあ ります! 32
  33. 33. #undef 概要● #undefは、#defineで定義したマクロを無効化 します● キーワードマクロ、定数マクロ、関数マクロ のいずれにも有効です● 以下のようにして用います #define KEY_WORD #define CONSTANT 200 #define SQ(x) ((x) * (x)) #undef KEY_WORD // この行以降は、KEY_WORDキーワードマクロは使えない #undef CONSTANT // この行以降は、CONSTANT定数マクロは使えない #undef SQ // この行以降は、SQ関数マクロは使えない 33
  34. 34. アウトライン(1)● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 34
  35. 35. #if, #elif, #else, #endif 概要● 条件コンパイルに用います – 同じソースコードでも、複数のOSやコンパイラに 対応できます● 宣言されているマクロに対するif ~ else文と いったところです● 次のような形で書きます #if <条件1> // 何か書く #elif <条件2> // 省略可 // 何か書く #else // 省略可 // 何か書く #endif 35
  36. 36. #if, #elif, #else, #endif 概要● 条件部には、数値比較やマクロが定義されて いるかどうかを確かめるdefined演算子を用い ることができます● 論理演算子も利用可能です // __STDC_VERSION__は定義済みマクロ #if __STDC_VERSION__ >= 199901 // C99規格でコンパイルするとき # define rep(i, n) for (int i = 0; i < (n); i++) #endif // FOOが定義されていて、かつBARが定義されていない、またはBAZが100以下なら #if defined(FOO) && !defined(BAR) || BAZ < 100 # include “hoge.h” #else # include “piyo.h” #endif 36
  37. 37. #if, #elif, #else, #endif 条件コンパイルの例● 複数のOSに対応する例として、sleep()関数を 取り上げましょう – sleep()関数は<unistd.h>で宣言されています – Windowsに<unistd.h>はありません ● <Windows.h>に、似た機能のSleep()関数があります // Windowsでコンパイルするとき #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(__WIN32__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(__WIN64__) # include <Windows.h> # define sleep(sec) Sleep(sec * 1000) // Windows以外でコンパイルするとき #else # include <unistd.h> 37 #endif
  38. 38. アウトライン(1)● C言語の文法の復習 – 三項演算子 – カンマ演算子 – 文字列の連結● プリプロセスの復習 – プリプロセスとは? – #include – #define, #undef – #if, #elif, #else, #endif – #ifdef, #ifndef, #endif 38
  39. 39. #ifdef, #ifndef, #endif 概要● #ifdefは、#if defined()の省略形です● #ifndefは、#if !defined()の省略形です● 簡単に用いることができるが、論理演算子を 用いることが出来ません● 単に、マクロが定義されているかどうかを確 認するときに用います – インクルードガードなど #ifndef FOO_H #define FOO_H // ヘッダの内容を書いていく #endif 39
  40. 40. 余談 改行、#if 0 ~ #endif● プリプロセスの指定は、行末に『』を入れる ことで、改行して続けて書くことが可能です // FOO, BAR, BAZがいずれも10より大きいならば、 #if FOO > 10 && BAR > 10 && BAZ > 10 ● #if 0 ~ #endifは、必ずプリプロセス段階で消え るので、複数行のコメントアウトとして使用 可能です – 入れ子にすることも可能だったり #if 0 printf(“debug:a = %dn”, a); #if 0 printf(“debug:b = %dn”, b); #endif 40 #endif
  41. 41. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 41
  42. 42. 文字列化演算子 概要:#● 関数マクロの引数を、ダブルクオートで囲 い、文字列化する機能です● 置換後の引数の左に、#を1つつけて記述しま す #define STR(x) #x● 使用例: printf(STR(1a2b3c));● 展開結果 printf(“1a2b3c”); 42
  43. 43. 文字列化演算子 応用例:#● 使いどころが難しいですが、文字列の結合と 組み合わせるとよいでしょう● 例: #define TRACE(var, fmt) printf(#var “ = ” fmt “n”, var)● 使用例と展開結果: int a = 10; TRACE(a, “%d”); // マクロの呼び出し printf(“a” “ = “ “%d” “n”, var); // このように展開される printf(“a = %dn”, var); // コンパイル後は、これと等価になる 43
  44. 44. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 44
  45. 45. トークン連結演算子 概要:##● 置換後のマクロのトークンとトークンと結合 させる演算子です● 主に、関数マクロの引数に用いられます● 例: #define GENVAR(num) int var##num● 使用例: GENVAR(1) = 10; GENVAR(2) = 20;● 展開結果: int var1 = 10; int var2 = 20; 45
  46. 46. トークン連結演算子 応用例:##● うまく使うと、記述量を劇的に減らすことが できます!● 例:構造体の再帰代入演算● Before: typedef struct { int x; int y; int z; } point; int main(void) { point p = { 2, 3, -2}; point q = {-4, 1, 6}; p.x += q.x; p.y += q.y; p.z += q.z; return 0; } 46
  47. 47. トークン連結演算子 応用例:##● うまく使うと、記述量を劇的に減らすことが できます!● 例:構造体の再帰代入演算● After: typedef struct { int x; int y; int z; } point; #define REC_ASSIGN(op, point1, point2) ( point1.x op##= point2.x, point1.y op##= point2.y, point1.z op##= point2.z ) int main(void) { point p = { 2, 3, -2}; point q = {-4, 1, 6}; REC_ASSIGN(+, p, q); return 0; 47 }
  48. 48. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 48
  49. 49. 可変引数マクロ 概要● 関数マクロの引数を、可変にしたいことっ て、ありますよね?● 実はできます!● 具体的には、以下のように記述します #define err_printf(...) fprintf(stderr, __VA_ARGS__)● 使用例: err_printf(“a[%d] = %d”, i, a[i]);● 展開結果: fprintf(stderr, “a[%d] = %d”, i, a[i]); 49
  50. 50. 可変引数マクロ 注意点● 可変引数マクロに与える引数を0個にする場合 には、注意が必要です● 例: #define dbg_printf(fmt, ...) fprintf(stderr, “%s at line %u : ” fmt, __FILE__, __LINE__, __VA_ARGS__)● 使用例: dbg_printf(“Hello World”);● 展開結果: // 可変引数を省略すると、カンマが残る fprintf(stderr, “%s at line %u : “ “Hello World”, “foo.c”, 25, ); 50
  51. 51. 可変引数マクロ 省略可能にするには● ##__VA_ARGS__と記述すると、省略可能にな ります● 例: #define dbg_printf(fmt, ...) fprintf(stderr, “%s at line %u : ” fmt, __FILE__, __LINE__, ##__VA_ARGS__)● 使用例: dbg_printf(“Hello World”);● 展開結果: // 今度は、カンマが残っていない fprintf(stderr, “%s at line %u : “ “Hello World”, “foo.c”, 25); 51
  52. 52. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 52
  53. 53. printlnマクロ 意義● printf()関数って、わざわざnをつけるのが面倒 ですよね?● JavaのSystem.out.println()メソッドみたいに、 自動で改行をつけて欲しいですよね? 53
  54. 54. printlnマクロ 意義● printf()関数って、わざわざnをつけるのが面倒 ですよね?● JavaのSystem.out.println()メソッドみたいに、 自動で改行をつけて欲しいですよね?● はい、できます!● そう、関数マクロならね 54
  55. 55. printlnマクロ マクロ定義● 改行つきのprintf()関数のマクロです #define println(fmt, ...) printf(fmt “n”, ##__VA_ARGS__)● 使用例: println(“a = %d”, 10);● 展開結果: printf(“a = %d” “n”, 10); // a = 10と表示し、改行される 55
  56. 56. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 56
  57. 57. unless文、until文 意義● Rubyには、until文、unless文がありますよね?● C言語でも欲しくないですか? 57
  58. 58. unless文、until文 意義● Rubyには、until文、unless文がありますよね?● C言語でも欲しくないですか?● ええ、できますとも!● そう、関数マクロならね 58
  59. 59. unless文、until文 マクロ定義● カンマ演算子を利用されることを想定して、 可変引数を用います● 最後の値が、式の値になることを利用すれ ば、以下のように定義できますね! #define unless(...) if(!(__VA_ARGS__)) #define until(...) while(!(__VA_ARGS__)) 59
  60. 60. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 60
  61. 61. デバッグ用マクロ 意義● デバッグ時のみ、有効にするコードを書くの は面倒ですよね?● 以下のような#ifdef ~ #endifのコードを散在さ せるのは嫌ですよね? – 2行もプリプロセス行で増えてしまいます! #ifdef DEBUG fprintf(stderr, “This is debug coden”); #endif 61
  62. 62. デバッグ用マクロ 意義● デバッグ時のみ、有効にするコードを書くの は面倒ですよね?● 以下のような#ifdef ~ #endifのコードを散在さ せるのは嫌ですよね? – 2行もプリプロセス行で増えてしまいます! #ifdef DEBUG fprintf(stderr, “This is debug coden”); #endif● そんなときは、やっぱり関数マクロ! 62
  63. 63. デバッグ用マクロ マクロ定義● 以下のようにすると、デバッグ時のみ有効になる dbg_printf()関数を定義できます #ifdef DEBUG # define dbg_printf(...) fprintf(stderr, __VA_ARGS__) #else # define dbg_printf (1 ? (void) 0 : fprintf) #endif● DEBUGマクロが定義されていないときは、コンパ イラの最適化で、コードが消えます!● 0をvoidキャストしているのは、ワーニングを消す ためです 63
  64. 64. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 64
  65. 65. SWAPマクロ 意義● 2数を交換する関数は、以下のようになります void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp }● 使用例: int a = 10, b = 20; swap(&a, &b);● こんな単純な関数を、呼び出すのはもったい ないですね 65
  66. 66. SWAPマクロ 意義● 2数を交換する関数は、以下のようになります void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp }● 使用例: int a = 10, b = 20; swap(&a, &b);● こんな単純な関数を、呼び出すのはもったいな いですね● マクロで書きましょう! 66
  67. 67. SWAPマクロ 基本形● 一番オーソドックスなSWAPマクロです #define SWAP(type, a, b) { type __tmp_swap_var__ = (a); (a) = (b); (b) = __tmp_swap_var__; }● 使用例: int a = 10, b = 20; SWAP(int, a, b); 67
  68. 68. SWAPマクロ 基本形:問題点● 関数呼び出しのように使えません – 関数呼び出しだと、アドレスを渡す必要がありま した● 複文に置き換えられるので、セミコロンを記 述する必要がないです – でも、SWAP(int, a, b);と書いても問題ないです● 以下のような、中括弧省略のif ~ else節におい て問題があります // コンパイルエラーとなる if (a == 10) SWAP(int, a, b); else SWAP(int, a, c); 68
  69. 69. SWAPマクロ 基本形:解決策● 先程の問題点から、シームレスな(関数を呼び出す ような)SWAPマクロの使用はできませんでした● 以下のように改良します – ポインタ型を引数に取るように作る ● マクロであることがよりシームレスになります ● 最適化に貢献(明らかに同一の変数のSWAPは行わなくなる) – do ~ while(0)で囲う ● 呼び出し側に、セミコロンをつけることを強制します ● 中括弧省略型if ~ elseでもエラーが出なくなります ● コンパイラの最適化により、do ~ while(0)の判定は消えます● 69
  70. 70. SWAPマクロ 基本形:改善● 以下が改善したものとなります #define SWAP(type, a, b) do { type __tmp_swap_var__ = *(a); *(a) = *(b); *(b) = __tmp_swap_var__; } while (0)● 型を指定する必要があるので、完全にシーム レスとは言い切れませんが...。● 使用例: int a = 10, b = 20; SWAP(int, &a, &b); 70
  71. 71. SWAPマクロ よりシームレスにするには● よりシームレスなSWAPマクロを目指したい!● 完成形としては、以下のような呼び出しが可 能であればよいですね SWAP(&a, &b);● 引数の型指定を無くすには・・・。 71
  72. 72. SWAPマクロ よりシームレスにするには● よりシームレスなSWAPマクロを目指したい!● 完成形としては、以下のような呼び出しが可 能であればよいですね SWAP(&a, &b);● 引数の型指定を無くすには・・・。 – xorを用いたSWAP – 加減算を用いたSWAP – GNU拡張文法を用いたSWAP 72
  73. 73. SWAPマクロ xor SWAP● xor(排他的論理和)を用いたSWAPアルゴリズムです #define SWAP(a, b) ( *(a) ^= *(b), *(b) ^= *(a), *(a) ^= *(b) )● 変数宣言の必要がないため、カンマ演算子で連結するこ とにより、置換後全体を式化しています● よりカッコよく書くと↓ – ※コンパイラの警告レベルを上げると、警告が出ます #define SWAP(a, b) (*(a) ^= *(b) ^= *(a) ^= *(b)) 73
  74. 74. SWAPマクロ xor SWAP:問題点● 同一の変数に対して用いた場合、変数の値が0 になります● 整数の変数に対してのみ、使用可能です – 浮動小数点数と構造体には使用不可● スピードは、一時変数を用いるものより遅い です 74
  75. 75. SWAPマクロ xor SWAP:問題点● 同一の変数に対して用いた場合、変数の値が0 になります● 整数の変数に対してのみ、使用可能です – 浮動小数点数と構造体には使用不可● スピードは、一時変数を用いるものより遅い です 解決可能! 75
  76. 76. SWAPマクロ xor SWAP:安全版(1)● 同一の変数かどうかをチェックするために、三項演 算子を組み込みます #define SWAP(a, b) (((a) == (b)) ? 0 : ( *(a) ^= *(b), *(b) ^= *(a), *(a) ^= *(b), 1 ))● 条件判断が加わる分、処理が増えますが・・・ – aとbのアドレスがコンパイル段階で異なると分かるとき は、最適化により条件判断部分は消えます 76
  77. 77. SWAPマクロ xor SWAP:安全版(2)● 短絡評価を用いると以下のように、カッコよ く書けます! #define SWAP(a, b) (((a) != (b)) && (*(a) ^= *(b) ^= *(a) ^= *(b), 1)) カッコイイ! 77
  78. 78. SWAPマクロ 加減算SWAP● 足し算と引き算を用いたSWAPアルゴリズムです #define SWAP(a, b) ( *(a) += *(b), *(b) = *(a) - *(b), *(a) -= *(b) )● 変数宣言の必要がないため、カンマ演算子で連結するこ とにより、置換後全体を式化しています● よりカッコよく書くと↓ – ※コンパイラの警告レベルを上げると、警告が出ます #define SWAP(a, b) (*(a) += *(b) -= *(a) = *(b) - *(a)) 78
  79. 79. SWAPマクロ 加減算SWAP:問題点● 同一の変数に対して用いた場合、変数の値が0 になります● 整数と浮動小数点の変数に対してのみ、使用 可能です – 構造体には使用不可● 浮動小数点数に用いた場合、丸め誤差が発生 する場合があります● スピードは、一時変数を用いるものやxorを用 いるものよりも遅いです 79
  80. 80. SWAPマクロ 加減算SWAP:問題点● 同一の変数に対して用いた場合、変数の値が0 になります● 整数と浮動小数点の変数に対してのみ、使用 可能です – 構造体には使用不可 解決可能!● 浮動小数点数に用いた場合、丸め誤差が発生 する場合があります● スピードは、一時変数を用いるものやxorを用 いるものよりも遅いです 80
  81. 81. SWAPマクロ 加減算SWAP:安全版(1)● 同一の変数かどうかをチェックするために、三項演 算子を組み込みます #define SWAP(a, b) (((a) == (b)) ? 0 : ( *(a) += *(b), *(b) = *(a) - *(b), *(a) -= *(b), 1 ))● 条件判断が加わる分、処理が増えますが・・・ – aとbのアドレスがコンパイル段階で異なると分かるとき は、最適化により条件判断部分は消えます 81
  82. 82. SWAPマクロ 加減算SWAP:安全版(2)● 短絡評価を用いると以下のように、カッコよ く書けます! #define SWAP(a, b) (((a) != (b)) && (*(a) += *(b) -= *(a) = *(b) - *(a), 1)) カッコイイ!! 82
  83. 83. SWAPマクロ GNU拡張文法SWAP● gccのC言語拡張文法を使用したマクロです – typeof演算子 – 複文の式化● シームレスかつ性能もいいです● 最強のSWAPマクロ? #define SWAP(a, b) ({ typeof(*(a)) __tmp_swap_var__ = *(a); *(a) = *(b); *(b) = __tmp_swap_var__; }) 83
  84. 84. アウトライン(2)● マクロテクニック – 文字列化演算子 – トークン連結演算子 – 可変引数マクロ● 黒魔術 – printlnマクロ – until文、unless文 – デバッグ用マクロ – SWAPマクロ – ダフのデバイス 84
  85. 85. ダフのデバイス 概要● Tom Duffという人が考案しました● ループのアンロールに用いられます – ループの条件判断回数を減らすことにより、高速 化を目指しています● switch文のfall-throughを利用しています● caseラベルをまたがるdo ~ while文が特徴です – C言語のswitch文は、規定が弱いから利用可能 – JavaやC#のswitch文では、利用不可 85
  86. 86. ダフのデバイス コード(1)● ダフのデバイスによる、配列のコピーを行う 関数です void duff_memcpy(int *to, const int *from, int size) { int n = (size + 7) >> 3; switch (size & 7) { case 0: do { *to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; } while (--n); } } 86
  87. 87. ダフのデバイス コード(2)● 次のように書くこともできます void duff_memcpy(int *to, const int *from, int size) { switch (size & 7) { case 0: do { *to++ = *from++; case 7: *to++ = *from++; case 6: *to++ = *from++; case 5: *to++ = *from++; case 4: *to++ = *from++; case 3: *to++ = *from++; case 2: *to++ = *from++; case 1: *to++ = *from++; } while (size -= 8); } } 87
  88. 88. ダフのデバイス 特徴● 今回取り上げた例では、8回毎のアンロール処 理でしたが、もっと回数を増やすことも可能 です● 現代では、あまり大きく性能向上に寄与しま せん(アンロール回数を256回毎程度にする と、良くなりますが・・・)● コード量(つまり、実行バイナリのサイズ)が 少し増えます 88
  89. 89. ダフのデバイス 要望● メモリコピー以外に応用したい!● 同じ処理を記述してくのはめんどくさい! 89
  90. 90. ダフのデバイス 要望● メモリコピー以外に応用したい!● 同じ処理を記述してくのはめんどくさい!● ええ、解決できますとも!● そう、関数マクロならね 90
  91. 91. ダフのデバイス 関数マクロへの応用● 以下の<処理>の部分に、行いたい処理が来る とよいですよね void duff_memcpy(int *to, const int *from, int size) { int n = (size + 7) >> 3; switch (size & 7) { case 0: do { <処理>; case 7: <処理>; case 6: <処理>; case 5: <処理>; case 4: <処理>; case 3: <処理>; case 2: <処理>; case 1: <処理>; } while (--n); } } 91
  92. 92. ダフのデバイス 関数マクロへの応用● ダフのデバイスをマクロ化すると、以下のよ うになります – STATEMENT:行いたい処理(式または複文) – __VA_ARGS__:次のステップ前の処理(省略化) #define DUFFS_LOOP(n, STATEMENT, ...) { register int __tmp_loop_var__ = ((n) + 7) >> 3; switch ((n) & 7) { case 0: do { STATEMENT; __VA_ARGS__; case 7: STATEMENT; __VA_ARGS__; case 6: STATEMENT; __VA_ARGS__; case 5: STATEMENT; __VA_ARGS__; case 4: STATEMENT; __VA_ARGS__; case 3: STATEMENT; __VA_ARGS__; case 2: STATEMENT; __VA_ARGS__; case 1: STATEMENT; __VA_ARGS__; } while (--__tmp_loop_var__); } 92 }
  93. 93. ダフのデバイス 関数マクロへの応用● 使用例: int a = 0; DUFFS_LOOP(10, { printf(“a = %dn”, a); }, a++);● 複文が引数となるので、呼び出し側からは、 奇妙な形に見えますね 93
  94. 94. 参考文献● プログラミング言語C 第2版 94
  95. 95. 参考文献● トリッキーコードネット – http://tricky-code.net/● 苦しんで覚えるC言語 – http://9cguide.appspot.com/● C言語の小技:SWAPマクロ – http://no1-bug-creator.cocolog-nifty.com/blog/2011/08/cswap-2ccd.html● Duffs device – http://ja.wikipedia.org/wiki/Duffs_device● ダフのデバイスというのを初めて知った - 星一の日記 – http://d.hatena.ne.jp/hajimehoshi/20080428/1209314296 95
  96. 96. 以上で、発表終了です! 96
  97. 97. どうでしたか? 97
  98. 98. 実際のコーディングの 役に立たない 知識ばかり でしたね? 98
  99. 99. でも、 99
  100. 100. ベテランの書いたC言語ソースを読むときに 役に立つかも しれません 100
  101. 101. また、 面白トリビアとして楽しんでいただけたのなら 幸いです 101
  102. 102. そして何より、 関数マクロを書く助けになったのなら 嬉しいです! 102
  103. 103. Let,s EnjoyMeta-Programming! 103
  104. 104. 御清聴 ありがとうございました! m(__)m 104

×