noocyte のプログラミング研究室 〜プログラムは楽しげに走らねばならない♪〜

C/C++ 関数・マクロ集 ((ほぼ?) 処理系・OS 非依存)

公開:2006/09/02(土)
最終更新:2016/10/22(土)

「C/C++ 関数・マクロ集」というタイトルですが, そのうちのいくつかはC専用だったりします.(苦笑)

2007/06/24(日) 追記

高木さんより, Cの規格上移植性に問題がある点をご指摘いただいたので, 現在修正中です.
(たくさんあります….orz)

とはいってもその多くは, めったにお目にかかれないような珍しい処理系とか, 「そんなの実在するの?」という処理系に移植する場合の話なので, 実用上ほとんどの場合は問題ないと思います.
(一部そうとはいえないものもありますが.)

Cの規格に照らして完全に「処理系・OS 非依存」 にするのは困難な場合もあり, 完璧な移植性にこだわるあまりプログラムが書けなくなっては本末転倒なので, タイトルに「ほぼ?」を入れました.orz

2007/06/21(木) 追記

このページを含め,私が C/C++ 関連記事を書くに当たりたびたび参考に&リンクさせていただいている 「株式会社きじねこ」の高木さんが, このページのマクロをパロディ C++ テンプレート化したものを公開なさっています. C++ ユーザの方はぜひご覧ください.

「C/C++ 関数・マクロ集」と書いておきながら,実は C++ はたまにしか使っていないので,C++ 専用のコンテンツはなかなか増えそうにありません (看板に偽りあり). そういうわけで,このページの C++ 化は高木さんにお願いしたいと思います (他力本願).

このページの主な更新は Blog でお知らせします.


0.目次

  1. 整数型に関する関数・マクロ
    1. 指定された整数型が符号付か否かを判定する. (IntegerIsSigned())
    2. 指定された整数型が2の補数表現か否かを判定する. (IntegerIsTwosComplement())
    3. 指定された整数型が1の補数表現か否かを判定する. (IntegerIsOnesComplement())
    4. 指定された整数型が「符号ビット+絶対値」表現か否かを判定する. (IntegerIsSignAndAbs())
    5. 指定された整数型が表現可能な最小値および最大値を返す. (IntegerMin(),IntegerMax())
    6. 整数演算で端数を偶数丸め (最近接偶数への丸め,JIS丸め,ISO丸め)
    7. 整数演算で端数四捨五入
    8. 整数除算で端数切り上げ.(商のみ,ICEIL())
    9. 整数除算で端数切り上げ. (商+剰余,iceil(),lceil(),llceil())
    10. 整数除算で端数切り下げ. (商+剰余,ifloor(),lfloor(),llfloor())
    11. 整数除算 (商+剰余,C標準ライブラリ関数 div() の64ビット版)
    12. 最大公約数を求める.
    13. 無符号整数の最下位の '1' のビットだけを抽出する.(LowestOneBit())
    14. 2の冪乗か否かを判定する.(IsPowerOf2())
    15. ビット数から8/10/16進数の桁数を求める.({OCT,DEC,HEX}DIGITS())
    16. 無符号整数型の MSB 抽出用マスク (MSBMASK())
  2. 数値型に関するマクロ
    1. 複数の数値またはポインタが昇順になっているか? (ValuesInOrder())
    2. 数値またはポインタが範囲内か? (ValueInRange{In,Ex}clusive(),ValueInRangeOp())
  3. 配列に関するマクロ
    1. 配列の要素数を返す.(ArraySizeOf())
    2. 配列を動的に確保する.(ARRAY*ALLOC())
    3. 配列の終端アドレスを取得する.(ArrayEnd())
    4. 配列の最後の要素のアドレスを取得する.(ArrayLast())
  4. 構造体に関するマクロ
    1. 構造体メンバのオフセットを返す.(offsetof())
    2. 構造体メンバのサイズを返す.(MemberSizeOf())
    3. 構造体の配列メンバの要素数を返す.(MemberArraySizeOf())
    4. 構造体メンバのアドレスから構造体のベースアドレスを逆算する. (StructBase(),StructBaseFromOffset())
    5. 動的構造体のメンバのアドレスを返す.(StructMember())
  5. 一般のデータ型に関するマクロ
    1. 型のビット数を返す.(BitSizeOf())
    2. 型のアラインメントを返す.(AlignmentOf())
    3. アラインメント調整のため,オフセットを切り上げる. (AlignOffset(),AlignType())
  6. メモリ上のデータ操作関数・マクロ
    1. データ (バイト列) をバイト逆順にする.(ByteReverse())
    2. アドレス (値) のアラインメントを返す.(AddressAlignmentOf())
  7. 文字・文字列・文字コードに関する関数・マクロ
    1. Unicode 関数・マクロ集
      1. UTF-16 符号単位がサロゲートか否かを判定する.
      2. サロゲート・ペア ⇔ Unicode スカラ値変換
      3. UTF-16 文字列関数
    2. シフト JIS 2バイト文字の判定
    3. シフト JIS の2バイト文字 ⇔ 区点番号/JIS/EUC-JP 変換
    4. ASCII 図形文字に対応する ASCII 制御文字コードを返す.(CTRL())
    5. 文字列終端 NUL のアドレスを取得する.(StringEnd())
    6. マクロ展開結果を文字列化する.(MacroExpandedString())
  8. 入出力に関する関数・マクロ
    1. バイナリファイルに変数 (ポインタ以外) を書く.(WriteVar())
    2. バイナリファイルから変数 (ポインタ以外) を読む.(ReadVar())
    3. 改行コード (CR,CRLF,LF) が混在するテキストファイルを読む.
  9. エンディアンに関する関数・マクロ
    1. 実行時にエンディアンを判定する関数 (あらゆる4バイト・エンディアンに対応)
    2. エンディアンを変換 (big ⇔ little) する関数およびマクロ (CHAR_BIT 対応)
    3. エンディアン名を取得する関数 (あらゆる4バイト・エンディアンおよび CHAR_BIT に対応)
  10. 日付・グレゴリオ暦/ユリウス暦計算関数
    1. 閏年の高速判定 (グレゴリオ暦/ユリウス暦)
    2. 日付が有効か否かの判定 (グレゴリオ暦/ユリウス暦)
    3. ○月のN回目のW曜日は何日?
    4. ○月D日 (W曜) は,その月の何回目のW曜日?
    5. 同じ月の別の日の曜日を求める.
    6. ライブラリ関数を使わずに指定日時 (年月日曜時) が夏時間か判定する.
    7. グレゴリオ暦/ユリウス暦 ⇒ 通算日数変換
    8. 通算日数 ⇒ グレゴリオ暦/ユリウス暦変換
  11. 双方向線型リスト処理マクロ集
  12. 時代遅れの(?)マクロ
    1. 関数のプロトタイプ宣言
  13. 謎の検索ワード集
  14. 象の卵
    1. 構造体のメンバ数の取得方法?
    2. 構造体/共用体のエンディアン?
    3. new[ ] で確保した配列の要素数を取得する方法?
    4. 検索ワード「関数のサイズ C言語 取得(計算)」
  15. 参考図書
  16. サイト内関連ページ
  17. 外部へのリンク
  18. 更新履歴

1.整数型に関する関数・マクロ

1.1 指定された整数型が符号付か否かを判定する.

次のマクロは,整数型 intType が符号付のときそのときに限り真を返す.

// 2007/06/24(日) 移植性の問題を修正.
#define IntegerIsSigned(intType)    ((intType)(-1) < 0)
// 旧版:移植性に問題あり.
// 1の補数を使用する処理系では,全ビットが1の場合,それが
// 負の0かトラップ表現かは処理系定義.また,~ の演算結果が
// トラップ表現を生成する場合の動作は未定義.
#define IntegerIsSigned(intType)    ((intType)~(intType)0 < 0)

使用例:

printf("size_t は符号%s\n", IntegerIsSigned(size_t) ? "付" : "無");
printf("ptrdiff_t は符号%s\n", IntegerIsSigned(ptrdiff_t) ? "付" : "無");
2006/09/03(日) 考案
2007/06/24(日) 移植性の問題を修正

1.2 指定された整数型が2の補数表現か否かを判定する.

次のマクロは,整数型 intType が符号付で, 負数を2の補数で表現するときそのときに限り真を返す.

#define IntegerIsTwosComplement(intType) \
  (IntegerIsSigned(intType) && \
   ((intType)((((intType)1 << (BitSizeOf(intType) - 2)) - 1) << 2) \
    == (intType)(-4)))

上記の == の左辺は,下位2ビットのみが0となるビットパターン.

&& 以後の部分を次のようにすると,移植性に問題あり.

●((intType)~(intType)0 == (intType)(-1))
左辺が IntegerIsSigned の旧版と同じ問題を引き起こす可能性がある.

●((intType)(~((intType)1 << (BitSizeOf(intType) - 1)) << 1) == (intType)(-2))
この途中結果 (intType)1 << (BitSizeOf(intType) - 1) は MSB のみが1と
なるビットパターン.2の補数の場合,これが有効な値かトラップ表現かは
処理系定義.
■注意
2007/06/25(月) 考案

1.3 指定された整数型が1の補数表現か否かを判定する.

次のマクロは,整数型 intType が符号付で, 負数を1の補数で表現するときそのときに限り真を返す.

#define IntegerIsOnesComplement(intType) \
  (IntegerIsSigned(intType) && \
   ((intType)((((intType)1 << (BitSizeOf(intType) - 2)) - 1) << 2) \
    == (intType)(-3)))

IntegerIsTwosComplement() の -4 を -3 に変えただけ.

■注意

IntegerIsTwosComplement() の注意書きと同文.

2007/06/25(月) 考案

■参考 (2016/06/04(土) 追記)

1の補数表現を使用するコンピュータとしては次のものがあったらしい.
(出典:Signed number representations (Wikipedia 英語版))

1.4 指定された整数型が「符号ビット+絶対値」か否かを判定する.

次のマクロは,整数型 intType が符号付で, 負数を「符号ビット+絶対値」で表現するときそのときに限り真を返す.

#define IntegerIsSignAndAbs(intType) \
  (IntegerIsSigned(intType) && \
   ((intType)((((intType)1 << (BitSizeOf(intType) - 2)) + 1) << 1) \
    == (intType)(-2)))

== の左辺は,ビットパターン 10……010.

■注意

IntegerIsTwosComplement() の注意書きと同文.

2007/10/11(木) 追記

初めて "8ビット=1バイト" の概念を導入したマシンである IBM 7030 Stretch も「符号+絶対値」形式だったらしい.(ただし整数じゃなくて固定小数点と書いてある.)

2007/06/25(月) 考案

1.5 指定された整数型が表現可能な最小値および最大値を返す.

特殊な処理系への移植性に問題あり.(修正方法検討中)

次の2つのマクロはそれぞれ,指定された整数型 intType が表現可能な最小値および最大値を返す.

#define IntegerMin(intType) \
  (IntegerIsSigned(intType) \
   ? (intType)((intType)1 << (BitSizeOf(intType) - 1)) : (intType)0)
   
#define IntegerMax(intType) \
  (IntegerIsSigned(intType) \
   ? (intType)~((intType)1 << (BitSizeOf(intType) - 1)) : (intType)~(intType)0)

これらのマクロは, 負の整数を2の補数で表現する CPU 専用です. (そうでない CPU ってあるの?)

なお BitSizeOf() は,型のサイズをビット数で取得するマクロです (後述).

注意:

これらのマクロは,intType の実際の有効ビット数が sizeof(intType) * CHAR_BIT と異なる場合には,正しい結果を返しません. (BitSizeOf() の注意書きを参照.)

使用例:
printf("long 型の値の範囲は %ld 〜 %ld.\n", IntegerMin(long), IntegerMax(long));
printf("unsigned long 型の値の範囲は %lu 〜 %lu.\n",
       IntegerMin(unsigned long), IntegerMax(unsigned long));
2006/09/03(日) 考案

1.6 整数演算で端数を偶数丸め (最近接偶数への丸め,JIS丸め,ISO丸め)

/*─────────────────────────────────────
機能  :整数除算で端数を偶数丸めする.つまり,
        ・有理数 dividend/divisor を最も近い整数に丸める.
        ・ただし dividend/divisor の小数部がちょうど 0.5 の場合は,
          最も近い偶数に丸める.

        ●別名
        ・最近接偶数への丸め (round to the nearest even)
        ・最近接丸め
        ・JIS丸め (JIS Z 8401 規則A)
        ・ISO丸め (ISO 80000-1:2009 Annex B)
        ・銀行家の丸め (banker's rounding)
        ・五捨五入
        ・偶捨奇入

入力  :(1) dividend (≧0):被除数.
        (2) divisor:除数.オーバーフローを避けるため,
              0 < divisor ≦ (UINT_MAX - 1) / 2 + 1
            でなければならない.

戻り値:有理数 dividend/divisor の小数部を偶数丸めした値.

規格  :(1) ISO 80000-1:2009 Quantities and units - Part 1: General
            Annex B Rounding of numbers
        (2) JIS Z 8401 規則A

2015/01/01(木) 考案・作成
─────────────────────────────────────*/
#include <limits.h>
#include <assert.h>

unsigned RoundHalfToEven(unsigned dividend, unsigned divisor)
{
  unsigned quot, rem, rem2;

  // 引数チェック
  assert(divisor > 0);
  assert(divisor <= (UINT_MAX - 1) / 2 + 1); // オーバーフロー防止

#if 賢い処理系
  // 次の商と剰余を1回の除算命令 (またはサブルーチン呼び出し) で求める
  // コードを生成する賢い処理系ならば,こちらを使用する方が速い.
  // (VC2008/2012/2013 /O2 では最適化された.)
  quot = dividend / divisor;
  rem  = dividend % divisor;

#else // あまり賢くない処理系
  // 上の商と剰余を求めるために (遅い) 除算を2回行うコードを生成する
  // あまり賢くない処理系ならば,こちらを使用する方が速いはず.
  // (乗算も遅いが,除算よりは速いので.)
  quot = dividend / divisor;
  rem  = dividend - quot * divisor;
#endif

  rem2 = rem * 2;
  assert(rem2 >= rem); // オーバーフロー検出

  if(rem2 > divisor) {
    // 端数>0.5 の場合:切り上げる.
    quot++;
  } else if(rem2 == divisor) {
    // 端数=0.5 の場合:quot が奇数ならば偶数に切り上げる.

#if 1 // 高速版 (整数加算命令 + ビット演算命令)
    quot++;
    quot &= ~1U; // quot を偶数に切り下げる.
#else // やや低速版
    // 上のコードと結果は同じだが,条件分岐命令を使う
    // コードにコンパイルされてしまうとちょっと遅い.
    if(quot & 1U) quot++;
#endif

  } else {
    // 端数<0.5 の場合:切り捨てる (ここでは何もする必要なし).
  }

  return quot;
}

■参考

1.7 整数演算で端数四捨五入

/*─────────────────────────────────────
機能  :整数除算で端数を四捨五入する.(JIS Z 8401 規則B)

入力  :(1) dividend (≧0):被除数 (整数型).
        (2) divisor (>0):除数 (整数型).

戻り値:有理数 dividend/divisor の小数部を四捨五入した値.数学的には,
          RoundHalfUp(dividend, divisor) ≡ floor(dividend/divisor + 1/2).

2010/07/18(日) 考案・作成
2011/06/29(水) 改良版 (オーバーフローしにくいようにした.)
2015/01/03(土) 公開
─────────────────────────────────────*/
#define RoundHalfUp(dividend, divisor) \
  (((dividend) + (divisor) / 2) / (divisor))

■同様のマクロ (有名どころ)

■参考

1.8 整数除算で端数切り上げ.(商のみ,マクロ)

/*─────────────────────────────────────
機能  :整数除算で端数切り上げ.

        Cの標準ライブラリには,浮動小数の小数部分を切り上げる関数 ceil()
        があるが,このマクロは ceil(dividend/divisor) の整数版.
        整数同士の除算 dividend / divisor (dividend≧0,divisor>0) において,
        小数部を切り上げた商を返す.

入力  :(1) dividend (≧0,整数型):被除数.
        (2) divisor (>0,整数型):除数.

戻り値:商 dividend/divisor の小数部分を切り上げた整数値.

注意  :オーバーフローを防ぐため,ICEIL(dividend, divisor) を計算する前に
        次の条件が成立していなければならない.(xxx_MAX は dividend と
        divisor の型が int ならば INT_MAX,unsigned ならば UINT_MAX など.)

            #include <assert.h>
            #include <limits.h>

            assert(divisor - 1 <= xxx_MAX - dividend);

        (数学的には,(ICEIL の計算式の分子)≦xxx_MAX であることを確認している
         のだが,この式を次のようにそのままCで書いてしまうと左辺がオーバー
         フローする可能性があるのでまずい.)

            assert(dividend + divisor - 1 <= xxx_MAX); // NG

        次の判定方法は,dividend と divisor が符号整数型の場合は不可実際にオーバーフローしたか否かを判定しようと意図しているが,
        符号付整数型ではオーバーフロー時の動作は未定義.
        (参考:Cのオーバーフローについて)

            assert(dividend + divisor - 1 >= dividend); // 符号付整数の場合はNG

証明  :dividend を divisor で割った商を q,余りを r (0≦r<divisor) とすると,
          dividend == divisor * q + r
        なので,
           ICEIL(dividend, divisor) == (dividend + divisor - 1) / divisor
             == (divisor * q + (divisor + r - 1)) / divisor
             == q + (divisor + r - 1) / divisor
        この第2項 (divisor + r - 1) / divisor に着目すると,

        (1) dividend/divisor が割り切れる場合は r==0 なので,
              第2項 == (divisor - 1) / divisor
            この整数除算 (端数切り捨て) は分母より分子が小さいので0となる.
            したがって ICEIL(dividend, divisor) == q.

        (2) dividend/divisor が割り切れない場合は 1≦r≦divisor-1 なので,
              第2項 == (divisor + r - 1) / divisor
            において divisor ≦ 分子 ≦ (2 * divisor - 2) < 2 * divisor
            であり,これを divisor で整数除算 (端数切り捨て) すると1になる.
            したがって ICEIL(dividend, divisor) == q + 1.
            つまり dividend/divisor の端数が切り上げられたことになる.

1990/03/11(日)以前? 考案
2015/02/21(土)       証明とオーバーフローに関する注意を追記.
─────────────────────────────────────*/
#define ICEIL(dividend, divisor) \
  (((dividend) + ((divisor) - 1)) / (divisor))

■同様のマクロ (有名どころ)

1.9 整数除算で端数切り上げ.(商+剰余,関数)

「切り上げ C言語」,「切り上げ C++」 などで検索してくる人が不思議に多いので追加.

/*─────────────────────────────────────
機能  :整数除算で端数切り上げ (商を+∞方向に丸める).

書式  :div_t iceil(int dividend, int divisor);
        ldiv_t lceil(long dividend, long divisor);
        lldiv_t llceil(long long dividend, long long divisor);

入力  :(1) dividend:被除数 (任意の整数).
        (2) divisor:除数 (>0).

戻り値:商および剰余.
        (1) 商 (xdiv_t::quot):有理数 dividend/divisor の小数部を+∞方向に
            丸めた整数値.つまり dividend/divisor 以上で最小の整数.
        (2) 剰余 (xdiv_t::rem)
            ・rem == dividend - quot * divisor
            ・-divisor < rem ≦ 0

参考  :端数処理 (Wikipedia)

1990/03/11(日) 作成 (初版)
2007/07/14(土) 作成 (Web 公開版)
─────────────────────────────────────*/
#include <assert.h>
#include <stdlib.h>

// 切り上げ関数を定義するマクロ.
#define IMPLEMENT_xceil(xceil, intType, xdiv) \
  xdiv##_t xceil(intType dividend, intType divisor) \
  { \
    xdiv##_t qr; \
    \
    assert(divisor > 0); \
    qr = xdiv(dividend, divisor); \
    if(qr.rem > 0) { \
      qr.rem -= divisor; \
      qr.quot++; \
    } \
    return qr; \
  }

// iceil() を定義する.
IMPLEMENT_xceil(iceil, int, div)

// lceil() を定義する.
IMPLEMENT_xceil(lceil, long, ldiv)

#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
// C99 以後
// llceil() を定義する.
IMPLEMENT_xceil(llceil, long long, lldiv)
#endif /* defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) */

1.10 整数除算で端数切り下げ.(商+剰余,関数)

剰余付の切り下げは,「日付 ⇔ 通算日数変換」でも使用する予定なので追加.

/*─────────────────────────────────────
機能  :整数除算で端数切り下げ (商を−∞方向に丸める).

書式  :div_t ifloor(int dividend, int divisor);
        ldiv_t lfloor(long dividend, long divisor);
        lldiv_t llfloor(long long dividend, long long divisor);

入力  :(1) dividend:被除数 (任意の整数).
        (2) divisor:除数 (>0).

戻り値:商および剰余.
        (1) 商 (xdiv_t::quot):有理数 dividend/divisor の小数部を−∞方向に
            丸めた整数値.つまり dividend/divisor 以下で最大の整数.
        (2) 剰余 (xdiv_t::rem)
            ・rem == dividend - quot * divisor
            ・0 ≦ rem < divisor

参考  :端数処理 (Wikipedia)

1990/03/11(日) 作成 (初版)
2007/07/14(土) 作成 (Web 公開版)
─────────────────────────────────────*/
#include <assert.h>
#include <stdlib.h>

// 切り下げ関数を定義するマクロ.
#define IMPLEMENT_xfloor(xfloor, intType, xdiv) \
  xdiv##_t xfloor(intType dividend, intType divisor) \
  { \
    xdiv##_t qr; \
    \
    assert(divisor > 0); \
    qr = xdiv(dividend, divisor); \
    if(qr.rem < 0) { \
      qr.rem += divisor; \
      qr.quot--; \
    } \
    return qr; \
  }

// ifloor() を定義する.
IMPLEMENT_xfloor(ifloor, int, div)

// lfloor() を定義する.
IMPLEMENT_xfloor(lfloor, long, ldiv)

#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
// C99 以後
// llfloor() を定義する.
IMPLEMENT_xfloor(llfloor, long long, lldiv)
#endif /* defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) */

1.11 整数除算 (商+剰余,C標準ライブラリ関数 div() の64ビット版)

/*─────────────────────────────────────
機能  :C標準ライブラリ関数 div() の64ビット版.
        (C99 未対応で,div() の64ビット版がサポートされていない処理系用.
         ただし64ビット整数型はサポートされている必要がある.)
入力  :(1) dividend:被除数 (任意の整数).
        (2) divisor:除数 (≠0).
戻り値:商および剰余.
        (1) 商 (div64_t::quot):有理数 dividend/divisor の小数部を0方向に
            丸めた整数値.つまり,
            ・絶対値:|dividend/divisor| 以下で最大の整数.
            ・符号:dividend と divisor が同符号ならば+,異符号ならば−.
        (2) 剰余 (div64_t::rem)
            ・rem == dividend - quot * divisor
            ・0 ≦ |rem| < divisor
            ・rem の符号は dividend と同じ.
2007/09/17(月) 作成
─────────────────────────────────────*/
typedef struct {
  int64_t quot;
  int64_t rem;
} div64_t;

div64_t div64(int64_t dividend, int64_t divisor)
{
  div64_t qr;
  Bool negativeQuotient = FALSE;    // 最終的に qr.quot < 0
  Bool negativeRemainder = FALSE;   // 最終的に qr.rem < 0

  if(divisor < 0) {
    divisor = (-divisor);
    negativeQuotient = !negativeQuotient;
  }
  if(dividend < 0) {
    dividend = (-dividend);
    negativeQuotient = !negativeQuotient;
    negativeRemainder = TRUE;
  }
  qr.quot = dividend / divisor;
  qr.rem = dividend - divisor * qr.quot;
  if(negativeQuotient) qr.quot = (-qr.quot);
  if(negativeRemainder) qr.rem = (-qr.rem);
  return qr;
}

1.12 最大公約数を求める.

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

/*─────────────────────────────────────
機能  :2つの無符号整数 m,n の最大公約数 (GCD) を求める.
        (ユークリッドの互除法を使用.)
入力  :m,n:任意の無符号整数値.
戻り値:GCD(m, n).ただし m または n の少なくとも一方が0ならば max(m, n).
参考  :(1) ユークリッドの互除法 (Wikipedia)英語版には再帰版と非再帰版の
            擬似コードがある.
        (2) 末尾再帰 (tail recursion) の最適化をしてくれるコンパイラならば,
            再帰版でもスタックを使わず,速度も落ちない (はず).
2007/10/20(土) 作成
2008/05/03(土) 戻り値の説明文を訂正.
─────────────────────────────────────*/
// unsigned long 用,非再帰版
unsigned long GcdUL_NonRecursive(unsigned long m, unsigned long n)
{
  unsigned long r;

  while(n > 0) {
    r = m % n;
    m = n;
    n = r;
  }
  return m;
}

// unsigned long 用,再帰版
unsigned long GcdUL_Recursive(unsigned long m, unsigned long n)
{
  return (n <= 0) ? m : GcdUL_Recursive(n, m % n);
}

/*─────────────────────────────────────
機能  :GcdUL_NonRecursive(),GcdUL_Recursive() のテスト.
2007/10/20(土) 作成
─────────────────────────────────────*/
int main(void)
{
  unsigned long m, n, gcd1, gcd2;

  for(;;) {
    fprintf(stderr, "m n: ");
    if(scanf("%lu %lu", &m, &n) != 2) break;
    gcd1 = GcdUL_NonRecursive(m, n);
    gcd2 = GcdUL_Recursive(m, n);
    assert(gcd1 == gcd2);
    if(gcd1 > 0) {
      assert((m % gcd1) == 0);
      assert((n % gcd1) == 0);
    }
    printf("GCD(%lu, %lu) = %lu\n", m, n, gcd1);
  }
  return EXIT_SUCCESS;
}

/*─────────────────────────────────────
実行例 (VC2003)
─────────────────────────────────────*/
D:\C\test\Math\GCD\Debug>GCD
m n: 150 225
GCD(150, 225) = 75
m n: 1024 256
GCD(1024, 256) = 256
m n: 27 5
GCD(27, 5) = 1
m n: 1234567890 987654321
GCD(1234567890, 987654321) = 9
m n: 0 100
GCD(0, 100) = 100
m n: ^Z

1.13 無符号整数の最下位の '1' のビットだけを抽出する.

/*─────────────────────────────────────
機能  :無符号整数の最下位の '1' のビットだけを抽出する.
入力  :uintValue:無符号整数.
戻り値:uintValue の純2進表現で最下位の '1' のビットだけを抽出したビットパ
        ターンを返す.別の言い方をすれば,uintValue=((奇数) * 2n) のとき,
        2n を返す.uintValue=0 のときは0を返す.
使用例:AddressAlignmentOf()
2007/07/25(水) 考案
2007/07/26(木) 作成
2010/08/25(水) 最適化のため演算順序変更
─────────────────────────────────────*/
#define LowestOneBit(uintValue)     ((~(uintValue) + 1U) & (uintValue))

2010/08/25(水) 追記

Java にも同名の Integer.lowestOneBit() というのがあるらしい.
ただしこちらは2の補数を使っているので (-intValue & intValue).

解説:intの最も右側の1のビット以外を0にする (mwSoft)

1.14 2の冪乗か否かを判定する.(IsPowerOf2())

#include <limits.h> // for UINT_MAX
#include <stdlib.h>
#include <stdio.h>

/*─────────────────────────────────────
機能  :2の冪乗か否かを判定する.
入力  :uintValue:無符号整数.
        IsPowerOf2(uintValue) では uintValue≧0,
        IsPowerOf2_(uintValue) では uintValue>0.
戻り値:uintValue が2の冪乗のときそのときに限り真.
注意  :IsPowerOf2_(uintValue) は IsPowerOf2(uintValue) よりも少し速いが,
        uintValue=0 の場合も真を返すので uintValue>0 のときに限り使用すべき.
2009/11/22(日) 考案・作成 (IsPowerOf2())
2011/06/29(水) 改名 (IsPowerOf2() → IsPowerOf2_()),IsPowerOf2() 追加.
─────────────────────────────────────*/
// 通常版 (uintValue≧0)
#define IsPowerOf2(uintValue)   (IsPowerOf2_(uintValue) && (uintValue > 0))
// 少し高速版 (ただし uintValue>0)
#define IsPowerOf2_(uintValue)  ((((uintValue) - 1U) & (uintValue)) == 0)

/*─────────────────────────────────────
機能  :IsPowerOf2_() のテスト.
2009/11/22(日) 作成
2015/02/27(金) 10進表示を右揃えにした.
─────────────────────────────────────*/
void Test_IsPowerOf2_(unsigned startValue, unsigned endValue)
{
  unsigned value = startValue;
  // value の表示桁数を自動決定する.
  const unsigned nHexDigits = HEXDIGITS(BitSizeOf(value)); // value の16進桁数
  const unsigned nDecDigits = DECDIGITS(BitSizeOf(value)); // value の10進桁数

  printf("Start: 0x%0*X %u\n", nHexDigits, value, value);
  for(;;  value++) {
    if(IsPowerOf2_(value))
      printf("0x%0*X %*u\n", nHexDigits, value, nDecDigits, value);
    if(value >= endValue) break;
  }
  printf("End: 0x%0*X %u\n", nHexDigits, value, value);
}

int main(void)
{
  // ↓unsigned が64ビット以上の処理系では,UINT_MAX はやめた方が….(笑)
  Test_IsPowerOf2_(0, UINT_MAX);
  return EXIT_SUCCESS;
}

/*─────────────────────────────────────
実行結果 (VC2013)
─────────────────────────────────────*/
Start: 0x00000000 0
0x00000000          0 ← IsPowerOf2_(0) も真になるので注意!
0x00000001          1
0x00000002          2
0x00000004          4
0x00000008          8
0x00000010         16
0x00000020         32
0x00000040         64
0x00000080        128
0x00000100        256
0x00000200        512
0x00000400       1024
0x00000800       2048
0x00001000       4096
0x00002000       8192
0x00004000      16384
0x00008000      32768
0x00010000      65536
0x00020000     131072
0x00040000     262144
0x00080000     524288
0x00100000    1048576
0x00200000    2097152
0x00400000    4194304
0x00800000    8388608
0x01000000   16777216
0x02000000   33554432
0x04000000   67108864
0x08000000  134217728
0x10000000  268435456
0x20000000  536870912
0x40000000 1073741824
0x80000000 2147483648
End: 0xFFFFFFFF 4294967295

1.15 ビット数から8/10/16進数の桁数を求める.

/*─────────────────────────────────────
機能  :ビット数から16進桁数を求める.
入力  :nBits:ビット数.
戻り値:nBits ビットの無符号整数を16進表示するのに必要な最大桁数.
2010/03/27(土) 作成
─────────────────────────────────────*/
// どっちでもよい.
#define HEXDIGITS(nBits)    ICEIL(nBits, 4U)
#define HEXDIGITS(nBits)    (((nBits) + 3U) >> 2)

/*─────────────────────────────────────
機能  :ビット数から8進桁数を求める.
入力  :nBits:ビット数.
戻り値:nBits ビットの無符号整数を8進表示するのに必要な最大桁数.
2010/03/27(土) 考案・作成
─────────────────────────────────────*/
#define OCTDIGITS(nBits)    ICEIL(nBits, 3U)

/*─────────────────────────────────────
機能  :ビット数から10進桁数を求める.
        近似式なのでビット数が大きいと誤差が出る可能性があるが,とりあえず
        Excel で試算してみると256ビットまでは大丈夫なことは確認した.

入力  :nBits:ビット数.とりあえず nBits=1〜256 としておく.ただし16ビット
        処理系 (nBitsが16ビット整数型) の場合はオーバーフローしてしまうので,
        16ビット無符号整数の場合は nBits=1〜53 (≦ UINT_MAX / 1233),
        16ビット符号付整数の場合は nBits=1〜26 (≦ INT_MAX / 1233).

戻り値:nBits ビットの無符号整数を10進表示するのに必要な最大桁数.

参考  :(1) 1233/4096 は log10(2)=0.3010299… の近似値.
        (2) 数学的に厳密な計算式および近似式は,
            DECDIGITS(n) = floor(log10(2n−1)) + 1
                         ≒ floor(n * log10(2)) + 1
                         ≒ floor(n * 1233 / 4096) + 1.

2010/03/27(土) 考案・作成
2011/01/18(火) 高速化のため log10(2) の近似値を変更.
               (301/1000 → 1233/4096,分母を2の冪乗にした.)
2011/05/22(日) 数学的に厳密な計算式を追記.
2012/08/04(土) 説明に近似式を追加,それに合わせて定義変更 (結果に影響なし).
2012/08/09(木) 16ビット処理系の場合の注意を追記.
─────────────────────────────────────*/
#define DECDIGITS(nBits)   ((((nBits) * 1233U) >> 12) + 1U)

/*─────────────────────────────────────
機能  :{OCT,DEC,HEX}DIGITS() のテスト.
2010/03/27(土) 作成
─────────────────────────────────────*/
#if defined(_MSC_VER) && (_MSC_VER < 1400)
// VC2003 では unsigned long long が使えるのに %ll が使えない.
#define LLFORMAT  "I64"
#else /* defined(_MSC_VER) && (_MSC_VER < 1400) */
#define LLFORMAT  "ll"
#endif /* defined(_MSC_VER) && (_MSC_VER < 1400) */

#define TEST_DIGITS(xxxDigits, valueFormat) \
  nDigits = sprintf(buf, "%" LLFORMAT valueFormat, maxValue); \
  assert(nDigits == xxxDigits(nBits)); \
  printf("nBits:%u %s:%u maxValue:%s\n", nBits, #xxxDigits, nDigits, buf);

void DigitsTest(void)
{
  uint64_t maxValue = 0; // nBits ビット無符号整数で表現可能な最大値
  uint64_t prevValue;
  unsigned nBits = 0;
  unsigned nDigits;
  char buf[64];

  for(;;) {
    nBits++;
    prevValue = maxValue;
    maxValue = (maxValue << 1) | 1U;
    // 万一 uint64_t に無効ビットがあっても正しい位置で終了する.
    if(maxValue == prevValue) break;

    TEST_DIGITS(OCTDIGITS, "o") // OCTDIGITS() の結果が正しいか確認する.
    TEST_DIGITS(DECDIGITS, "u") // DECDIGITS() の結果が正しいか確認する.
    TEST_DIGITS(HEXDIGITS, "X") // HEXDIGITS() の結果が正しいか確認する.
  }
}

1.16 無符号整数型の MSB 抽出用マスク

/*─────────────────────────────────────
機能  :無符号整数型の MSB だけを抽出するためのビットマスクを返す.
        (無効ビットがある型でもたぶん OK.符号付整数型は不可.)
入力  :uintType:無符号整数型.
戻り値:MSB だけが1となる uintType 型の値.
        (例) MSBMASK(uint8_t)  → 0x80U
             MSBMASK(uint16_t) → 0x8000U
             MSBMASK(uint32_t) → 0x80000000U
             MSBMASK(uint64_t) → 0x8000000000000000U
注意  :(1) 次の方法では,uintType に無効ビットがある場合に対応できない.
              #define MSBMASK(uintType) \
                ((uintType)1 << (BitSizeOf(uintType) - 1))
        (2) 符号付整数型では,右シフトの処理系依存性のためうまくいかない.
            また処理系によっては (途中結果または最終結果が) トラップ表現に
            引っかかる可能性がある.そもそもトラップ表現のある符号付整数型で
            MSB だけのビットパターンを抽出しようという考え自体が誤り.
2010/03/26(金) 考案・作成
─────────────────────────────────────*/
#define MSBMASK(uintType) \
  ((uintType)((uintType)~(uintType)0 - ((uintType)~(uintType)0 >> 1)))

2.数値型に関するマクロ

2.1 複数の数値またはポインタが昇順になっているか? (ValuesInOrder())

/*─────────────────────────────────────
機能  :複数の数値またはポインタが昇順になっているか否かを判定する.
入力  :value1〜:数値型,またはオブジェクトへのポインタ型.
        (不完全型や関数へのポインタは不可)
戻り値:value1 ≦ value2 ≦ value3 … のときそのときに限り真.
2007/10/02(火) 作成 (ValueInRange)
2007/10/03(水) 作成 (ValuesInOrder4)
2007/10/06(土) 改名 (ValueInRange → ValuesInOrder3)
─────────────────────────────────────*/
#define ValuesInOrder3(value1, value2, value3) \
  (((value1) <= (value2)) && ((value2) <= (value3)))

#define ValuesInOrder4(value1, value2, value3, value4) \
  (((value1) <= (value2)) && ((value2) <= (value3)) && ((value3) <= (value4)))

2.2 数値またはポインタが範囲内か? (ValueInRange*())

/*─────────────────────────────────────
機能  :数値またはポインタが区間 (範囲) 内にあるか否かを判定する.
        ・ValueInRangeInclusive(),ValueInRange():閉区間専用.
        ・ValueInRangeExclusive():開区間専用.
        ・ValueInRangeOp():(半)開区間にも使用可能.
入力  :数値型,またはオブジェクトへのポインタ型.
        (不完全型や関数へのポインタは不可)
        (1) minValue:最小値.
        (2) value:値.
        (3) maxValue:最大値.
        (4) infValue:下限値 (infinimum).
            (浮動小数型で infValuevalue の場合,infValue は最小値ではなく下限値.)
        (5) supValue:上限値 (supremum).
            (浮動小数型で valuesupValue の場合,supValue は最大値ではなく上限値.)
        (6) op1,op2:不等号 (< または <=).
戻り値:(1) ValueInRange:
            minValue ≦ value ≦ maxValue のときそのときに限り真.
        (2) ValueInRangeOp:
            infValue op1 value op2 supValue のときそのときに限り真.
2007/10/02(火) 作成 (ValueInRange)
2007/10/06(土) 改名 (ValueInRange → ValuesInOrder3)
2010/06/27(日) 作成 (ValueInRangeOp)
2015/08/09(日) 作成 (ValueInRangeExclusive)
               改名 (ValueInRange → ValueInRangeInclusive)
─────────────────────────────────────*/
// minValue と maxValue を範囲に含む (inclusive).
#define ValueInRangeInclusive(minValue, value, maxValue) \
  ValuesInOrder3(minValue, value, maxValue)

#define ValueInRange ValueInRangeInclusive // 旧名

// infValue と supValue を範囲に含まない (exclusive).
#define ValueInRangeExclusive(infValue, value, supValue) \
  (((infValue) < (value)) && ((value) < (supValue)))

#define ValueInRangeOp(infValue, op1, value, op2, supValue) \
  (((infValue) op1 (value)) && ((value) op2 (supValue)))

// 例
if(ValueInRangeOp(10.0, <=, x, <, 20.0)) { … } // 左閉右開区間 [10.0, 20.0)

3.配列に関するマクロ

3.1 配列の要素数を返す.

次のマクロは,任意の配列 array[] の要素数を返す (もちろん不完全型は不可).

#define ArraySizeOf(array)     (sizeof(array) / sizeof(array[0]))

このマクロを使用すると,配列に要素を追加/削除した場合でも, 別途要素数を書き替える必要がなくなり,ソースの保守性が向上する.

他人様の書いたソースを見ていると, 特定の配列の要素数を取得するマクロを定義しているのを時々見かける. 例えば int 型要素の配列 IntArray[] の場合は次のような具合.

#define N_ELEMENTS  (sizeof(IntArray) / sizeof(int))

せっかくここまでやるのなら, どうしてもう一歩進めて任意の配列に使用できるようにしないのか不思議.


■使用例

const char * const StringArray[] = {
  "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten",
};

void PrintStrings(void)
{
  unsigned i;
  for(i = 0;  i < ArraySizeOf(StringArray);  i++)
    printf("StringArray[%u] = \"%s\"\n", i, StringArray[i]);
}

■同様のマクロ (有名どころ)

考案日不明 (1987年以前)

■図解 (2011/09/25(日) 追記)

配列の物理的なデータ構造 (メモリ上でどういうビットパターンになっているか) がわかっていれば,このマクロは割り算 (包含除) を習ったばかりの小学3年生でも思いつくはず.

「C 配列 要素数」などで検索して来る人が毎日何人もいるということは, 配列の物理的な構造を理解していない (配列を C/C++ の文法としてしか理解していない (教わらない?)) 人が多いということだろう.(ひょっとして教える方もわかっていない?)

#define N   32

int array[N] = { 0x00010203, 0x04050607, … , 0x7C7D7E7F };


●array[] の (C/C++ から見た) 物理的なデータ構造 (ビットパターン)
  (CHAR_BIT=8ビット,int=4バイト・リトルエンディアンの CPU の場合)


                       1バイト
                   (CHAR_BITビット)
    array,       ←────────→
    &array[0] →┏━━━━━━━━━┓┬─────────┬
               ┃ 00000011 ┃↑                  ↑
               ┠                  ┨│                  │
               ┃ 00000010 ┃│                  │
       array[0] ┠                  ┨│sizeof(array[0])  │
               ┃ 00000001 ┃│ = 4             │
               ┠                  ┨│                  │
               ┃ 00000000 ┃↓                  │
    &array[1] →┠─────────┨┴                  │
                ┃ 00000111 ┃                    │
                ┠                  ┨                    │
                ┃ 00000110 ┃                    │
       array[1] ┠                  ┨                    │
                ┃ 00000101 ┃                    │
                ┠                  ┨                    │
                ┃ 00000100 ┃                    │
    &array[2] →┠─────────┨      sizeof(array) │
                :                  :       = 4 * N     │
                :                  :                    │
                :                  :                    │
                :                  :                    │
                :                  :                    │
                :                  :                    │
                :                  :                    │
  &array[N-1] →┠─────────┨                    │
                ┃ 01111111 ┃                    │
                ┠                  ┨                    │
                ┃ 01111110 ┃                    │
     array[N-1] ┠                  ┨                    │
                ┃ 01111101 ┃                    │
                ┠                  ┨                    │
                ┃ 01111100 ┃                    ↓
    &array[N] →┗━━━━━━━━━┛──────────┴


array:配列の先頭要素 array[0] の先頭アドレス,または配列全体を表す.
(参考:新・闘わないプログラマ No.327 配列の先頭アドレス)

&array[i]:array[i] の先頭アドレス

&array[N]:array の終端アドレス

■注意 (2006/10/10(火) 追記,2011/09/25(日) 図解に合わせて変更)

ArraySizeOf(array) の array は配列型変数でなければならず, 配列へのポインタでは正しい値を返しません.
例えば次のような場合です.

/* 上記図解のコードの続き */
int *pArray = array;

/* 2007/06/24(日) 移植性の問題を修正 */
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
  /* C99 以後 */
  printf("%zu\n", ArraySizeOf(pArray));   /* 誤 */
  printf("%zu\n", ArraySizeOf(array));    /* 正 */
#else /* defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) */
  /* C99 より前 */
  printf("%lu\n", (unsigned long)ArraySizeOf(pArray));    /* 誤 */
  printf("%lu\n", (unsigned long)ArraySizeOf(array));     /* 正 */
#endif /* defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) */

うまくいかない理由は,sizeof(pArray) が配列のサイズ (バイト数) ではなく,配列へのポインタのサイズだからです.
(建物の大きさと,その住所の文字数 (アドレスのサイズ) には何の関係もないでしょ?)

・ポインタは4バイトとする.
・array の先頭アドレスを仮に 0x8090A0B0 とする.

       pArray(略図)
      ┏━━━━━┓                           array
      ┃    ・──╂──────────→┏━━━━━━┓┬
      ┗━━━━━┛            0x8090A0B0┃  array[0]  ┃↑
            ||                            ┠──────┨│
            ∨                            ┃  array[1]  ┃│
       pArray(詳細)                       ┠──────┨│
      ┏━━━━━┓┬                    ┃  array[2]  ┃│
      ┃ 11010000 ┃↑                    ┠──────┨│sizeof(array)
      ┠          ┨│                    :            :│
      ┃ 10100000 ┃│sizeof(pArray)      :            :│
      ┠          ┨│  = 4              :            :│
      ┃ 10010000 ┃│  ≠ sizeof(array)  :            :│
      ┠          ┨│                    ┠──────┨│
      ┃ 10000000 ┃↓                    ┃ array[N-1] ┃↓
      ┗━━━━━┛┴                    ┗━━━━━━┛┴

また,ArraySizeOf() を関数にすることはできません.第1の理由は, 関数に渡されるのが配列そのものではなく,配列の先頭要素へのポインタだからであり, 上記の場合と同じ問題に直面するからです.第2の理由は, 任意の配列に使えるようにしようとすると,引数の型が定まらなくなるからです.

なぜわざわざこんな注意書きを追記したかというと,ついさっき ArraySizeOf() の一歩手前まで行きながら, 最後の一歩を間違えたり,行き過ぎたりしている(いた)人を見つけたので.


■多次元配列の要素数を取得する方法 (2007/04/01(日) 追記)

C/C++ の多次元配列は「配列の配列」なので,例えば多次元配列 array[N1][N2][N3] の要素数 N1,N2,N3 は次のようにすれば得られます.

ArraySizeOf(array) → N1
ArraySizeOf(array[0]) → N2
ArraySizeOf(array[0][0]) → N3

■2009/01/11(日) 追記

C++ の new[ ] で確保した配列の要素数を取得する標準的な方法は存在しない.

3.2 配列を動的に確保する.

次の各マクロは,要素の型と個数を指定して配列を確保します. alloca(),malloc(),calloc(),realloc() を直接使うよりも便利です. (ただし alloca() は非標準なので処理系依存.)

#define ALLOCA(type, n)     ((type*)alloca(sizeof(type) * (n)))
#define MALLOC(type, n)     ((type*)malloc(sizeof(type) * (n)))
#define CALLOC(type, n)     ((type*)calloc((n), sizeof(type)))
#define REALLOC(type, n, p) ((type*)realloc((p), sizeof(type) * (n)))

次の各マクロは,配列の先頭を指すポインタ arrayPtr と要素数 n を引数として配列を確保します. また配列要素の型を変更する場合は,arrayPtr の型だけを変更すればよく, サイズの計算式を変更する必要がありません.

#define ARRAYALLOCA(arrayPtr, n)  ((arrayPtr) = alloca(sizeof((arrayPtr)[0]) * (n)))
#define ARRAYMALLOC(arrayPtr, n)  ((arrayPtr) = malloc(sizeof((arrayPtr)[0]) * (n)))
#define ARRAYCALLOC(arrayPtr, n)  ((arrayPtr) = calloc((n), sizeof((arrayPtr)[0])))
#define ARRAYREALLOC(newArrayPtr, oldArrayPtr, n) \
  ((newArrayPtr) = realloc((oldArrayPtr), sizeof((newArrayPtr)[0]) * (n)))

使用例:

#define OK      0       /* 正常終了 */
#define FAIL    (-1)    /* エラー   */

double *SineTable;      /* 配列の先頭を指すポインタ */

int MakeSineTable(unsigned n, double initialAngle, double stepAngle)
{
  double angle;
  unsigned i;

  /* 配列 SineTable[n] を確保する.*/
  if(ARRAYMALLOC(SineTable, n) == NULL)
    return FAIL; /* メモリ不足 */

  /* 配列の内容 (正弦関数表) を記入する.*/
  for(angle = initialAngle, i = 0;  i < n;  i++, angle += stepAngle)
    SineTable[i] = sin(angle);

  return OK; /* 成功 */
}
1991/05/11(土) 考案 (*ALLOC())
1995/12/16(土) 考案 (ARRAY*ALLOC())

3.3 配列の終端アドレスを取得する.

/*─────────────────────────────────────
引数  :array:配列型変数.
戻り値:配列の直後のアドレス.
2008/06/24(火) 作成
─────────────────────────────────────*/
#define ArrayEnd(array)         ((array) + ArraySizeOf(array))

使用例:

int IntArray[N];

long ArraySumTest(void)
{
  const int *element = IntArray;
  const int * const arrayEnd = ArrayEnd(IntArray);
  long sum = 0;

  while(element < arrayEnd) sum += *element++;
  return sum;
}

3.4 配列の最後の要素のアドレスを取得する.

/*─────────────────────────────────────
引数  :array:配列型変数.
戻り値:配列の最後の要素のアドレス.
2009/09/07(月) 作成
2009/09/21(月) 改定
─────────────────────────────────────*/
#define ArrayLast(array)     ((array) + (ArraySizeOf(array) - (size_t)1))

4.構造体に関するマクロ

構造体のメンバ数の取得方法を知りたい方はこちら

4.1 構造体メンバのオフセットを返す.(offsetof())

C 標準ライブラリのヘッダファイル stddef.h で定義されているマクロ offsetof() は,type 型構造体のメンバ member の,先頭からのバイト・オフセットを返します.()
次にその定義の一例を示します.

// C専用 (stddef.h で定義).C++用はこちら.
#define offsetof(type, member)      ((size_t)&((type*)0)->member)

このマクロは,昔々 (1987年頃) UNIX 4.1BSD のヘッダファイル struct.h の中で見つけ,便利そうだと思って自分のヘッダファイルに取り込んでずっと使ってきたのですが, 今では C 標準ライブラリのマクロに昇格したんですね (感慨).

■同様のマクロ (有名どころ)

■余談

小耳に挟んだ話によると,ごく一部の CPU (Cray Y-MP など) には, なぜか構造体の先頭オフセットが0でないものがあるらしい. しかしそういうのはワードマシンのごく一部だけで,現在大多数を占めているバイトマシンではありえないだろう.


解説 (2012/09/01(土) 追記)

type 型の構造体変数が定義されていれば, そのメンバ member のオフセット offset は次のようにして求められる.

type t;
#define offset	((size_t)((char*)&t.member - (char*)&t))

しかしこの方法では次の問題がある.

もし t がアドレス0に配置される (つまり &t == 0) ならば, offset の計算式は次のようになり,引き算をしなくてすむ.

offset = (size_t)((char*)&t.member - (char*)&t)      // &t == 0
       = (size_t)(ptrdiff_t)&t.member
       = (size_t)&t.member

ここで変数 t は,そのアドレス &t を計算に使っているだけであり,t の実体にアクセスする必要はない. だから t を *(type*)0 に置き換えてもよく,そうすれば t が不要になる.

offset = (size_t)&t.member
       = (size_t)&((*(type*)0).member)
       = (size_t)&(((type*)0)->member)

これは定数式なので,配列宣言で使用することもできる.

(例) char buffer[offsetof(type, member) + sizeof(…)]; // OK

注意:上記の (type*)0 の部分を (type*)NULL と書くのは誤り. (2012/09/01(土) 追記)

ネット上ではそのような書き方をしている人を時々見かける.「NULL は 0 として定義されているんだから同じじゃないか」と言いたい人もいるだろう.確かにコンパイルしてしまえば全く同じだから,どっちの書き方をしても動作上は問題ない.しかし考え方としては完全に間違い. (下記の「内包と外延」を参照)

NULL は「何も指さないポインタ」という意味なので,((type*)NULL)->member という式は無意味 (概念上矛盾している).
(世間では「矛盾塊」というらしい.)

他方, offsetof での (type*)0 は上記の説明のとおり「type 型のデータがアドレス0に配置されている (と見なす)」という意味であり,さらにこの0は「0を引き算しても結果が変わらない (加法単位元)」という意味での0である.

NULL を0として定義するのは単に C/C++ の「便宜上のお約束」であって, 実際にはアドレス0に有効なデータが存在する (つまり (type*)0 が有効なポインタである) 場合もある.逆に,昔のコンピュータには NULL≠0 である機種も存在したらしい (下記の comp.lang.c FAQ 5.17 参照).

メモリ保護機構のある OS では,アプリケーションプログラムが (論理) アドレス0にアクセスしようとしてもアクセス違反になるが, 組込み系や OS カーネルでは物理アドレス0にアクセスできる.そこにはメモリが実在しているだけでなく,CPU にとって特別な意味を持つデータやプログラム (ハードウェアレベルで仕様が決まっている) が配置されているのが普通だからである.

■参考

4.2 構造体メンバのサイズを返す.

次のマクロは,type 型構造体のメンバ member のサイズを返します.

#define MemberSizeOf(type, member)       sizeof(((type*)0)->member)

このマクロも,UNIX 4.1BSD のヘッダファイル struct.h の中で見つけたものを元にしています.


■同様のマクロ (有名どころ)

1989/09/18(月) 作成

4.3 構造体の配列メンバの要素数を返す.

次のマクロは,type 型構造体のメンバ memberArray が配列のとき,その要素数を返します.

#define MemberArraySizeOf(type, memberArray)   ArraySizeOf(((type*)0)->memberArray)
2006/07/10(月) 作成
2007/05/16(水) 改定

4.4 構造体メンバのアドレスから構造体のベースアドレスを逆算する.

次のマクロは,type 型構造体のメンバ member のアドレス pMember が与えられた時, 構造体のベースアドレスを返します.

#define StructBaseFromOffset(type, offset, pMember) \
  ((type*)((char*)(pMember) - (offset)))

#define StructBase(type, member, pMember) \
  StructBaseFromOffset((type), offsetof(type, member), (pMember))

このマクロも,UNIX 4.1BSD のヘッダファイル struct.h の中で見つけたものを元にしています.


■同様のマクロ (有名どころ)


2007/06/24(日) 追記

仕様上はポインタ演算に問題がありますが, 実用上は多分問題ないと思うのでそのままにします.
(StructMember() も同様)

2009/01/12(月) 改定

4.5 動的構造体のメンバのアドレスを返す.

C/C++ の文脈で「動的構造体」というと, 世間では malloc や new で動的に確保した (静的) 構造体のことを意味するようですが, ここでは別の意味に使います. C/C++ に限らずコンパイラ言語では, 構造体の定義はコンパイル時に (静的に) 決定されます. これに対しここでいう「動的構造体」とは, コンパイル時にはその構造が確定しておらず, プログラムの実行時に構造が決定される構造体を意味します. 例えば可変長配列をメンバとして含んだり, 特定のメンバを含むか否かが実行時に決定されるような場合です. 無理やりC言語風に書けば,

struct MyDynamicStruct(size_t n, int coordType, type_t valueType, size_t maxStrLen) {
  int number[n]; // 可変長配列
  // 実行時に選択されるメンバ
  switch(coordType) {
  case INTEGER: // 3次元座標は符号付整数
                int x, y, z;
                break;
  case FLOAT  : // 3次元座標は単精度浮動小数
                float x, y, z;
                break;
  case DOUBLE : // 3次元座標は倍精度浮動小数
                double x, y, z;
                break;
  default: ;    // 座標なし
  }
  valueType value;              // 実行時に型が指定されるメンバ
  char string[maxStrLen + 1];   // 可変長配列
};

つまり静的構造体はプログラマがその構造を決定するのに対し, 動的構造体はプログラムが実行時に構造を決定します.動的構造体は,C言語の struct 構文で宣言される (静的) 構造体とは無関係です.

次のマクロは,先頭アドレスを base とする動的構造体において, その先頭から offset バイトの位置にある type 型メンバのアドレスを返します. つまり,静的構造体での &base->member に相当する値です.

#define StructMember(type, base, offset) ((type*)((char*)(base) + (offset)))
2007/06/24(日) 追記

仕様上はポインタ演算に問題がありますが, 実用上は多分問題ないと思うのでそのままにします.
(StructBase() も同様)

1993/02/17(水) 作成

5.一般のデータ型に関するマクロ

5.1 型のビット数を返す.

次のマクロは,データ型 type のサイズをビット数で取得します. CHAR_BIT は limits.h の中で定義されています.

#include <limits.h>
#define BitSizeOf(type)     (sizeof(type) * CHAR_BIT)
■注意 (2007/06/24(日) 誤記訂正)

私はいまだに使ったことはありませんが, 有効ビット数が中途半端な整数型も存在するそうです. そのようなデータ型では,実際の有効ビット数と BitSizeOf(type) の値が一致しない場合があります.


2006/12/30(土) 追記

PIC マイコン用Cコンパイラ (MPLAB© C18C コンパイラ) にも,2の冪乗でないサイズ (24bit) の整数型があります (リンク先の11ページ参照). しかもその型名がなんと,short long 型!! (どっちやねん!)
この型名は独自拡張であり,Cの規格上は違反だそうです. ちなみにアラインメントは書かれてないけど,何バイトなんだろう?

5.2 型のアラインメントを返す.

次のマクロは,データ型 type のアラインメントを取得します. 「アラインメントって何?」という方はこちらへどうぞ.

#define AlignmentOf(type)    offsetof(struct { char a; type b; }, b)

このマクロは,構造体の先頭に1バイト (char 型) のダミーメンバ a を, その次に type 型のメンバ b を配置し, b のオフセットを調べることで type の実際のアラインメントを取得します.

なお,このマクロは C 専用です. C++ では構文エラーになり,使用できません.
(C++ 用は下記の追記参照.)

Microsoft C では,同様の機能を持つ演算子 __alignof() が使用できます. ただし AlignmentOf() と異なる値を返す場合があります (バグというわけではなく,そういう仕様のようです).

GCC でも同様の機能を持つ演算子 __alignof__ が使えるようですが,実験はまだ行っていません.

1993/08/09(月) 考案

5.3 アラインメント調整のため,オフセットを切り上げる.

次の2つのマクロは,オフセット値 offset がアラインメントの倍数になるように切り上げた値を返します.

#define AlignOffset(offset, alignment) \
  ((size_t)((offset) + ((alignment) - 1)) & ~(size_t)((alignment) - 1))

#define AlignType(offset, type)    AlignOffset(offset, AlignmentOf(type))

AlignOffset() には,アラインメント alignment を直接指定します. alignment は2の冪乗 (1,2,4,8,…) でなければなりません. AlignType() には,アラインメントを直接指定する代わりに, 型名 type を指定します.

■同様のマクロ (有名どころ)

■参考

1995/06/16(金) 考案

6.メモリ上のデータ操作関数・マクロ

6.1 データ (バイト列) をバイト逆順にする.

/*─────────────────────────────────────
機能  :データ (バイト列) をバイト逆順にする.
入力  :(1) data:データの先頭アドレス.
        (2) nBytes:バイト列のサイズ (バイト数).
入出力:((unsigned char*)data)[0 〜 nBytes-1]:データ.
1990/05/06(日) 初版
2007/04/16(月) 改定
2009/05/23(土) トラップ表現のある処理系で問題になるかもしれないので,
               char* → unsigned char* に変更.
               (0x80 が格納されているバイトアドレスを (signed) char* で参照
                した途端,何かが起きる! … かもしれない.)
─────────────────────────────────────*/
void ByteReverse(void *data, size_t nBytes)
{
  unsigned char *low = (unsigned char*)data;
  unsigned char *high = low + nBytes;
  unsigned char temp;

  while(--high > low) {
    // *low と *high を交換する.
    temp = *low;
    *low++ = *high;
    *high = temp;
  }
}

6.2 アドレス (値) のアラインメントを返す.

/*─────────────────────────────────────
機能  :アドレス (値) のアラインメントを取得する.つまり,そのアドレスが最大
        何バイトのアラインメントに適合するかを調べる.
入力  :address:アドレス.
戻り値:address のアラインメント.address=0 (NULL) ならば0.
使用例:アラインメントを考慮した memcpy() の高速版 (気が向いたら公開予定).
2007/07/25(水) 考案
2007/07/26(木) 作成
─────────────────────────────────────*/
#define AddressAlignmentOf(address)     LowestOneBit((size_t)(address))

7.文字・文字列・文字コードに関する関数・マクロ

7.1 Unicode 関数・マクロ集

別ページに飛びます.

7.2 シフト JIS 2バイト文字の判定

別ページ (ネットで第1バイトの巧妙な判定方法を見つけたので追加.)

7.3 シフト JIS の2バイト文字 ⇔ 区点番号/JIS/EUC-JP 変換

別ページ

7.4 ASCII 図形文字に対応する ASCII 制御文字コードを返す.

次のマクロは,ASCII 図形文字に対応する ASCII 制御文字コードを返します.
(ソース文字コードが ASCII (または亜種) の処理系専用.)

#define CTRL(c)     ((c) ^ 0x40)

CTRL('@') 〜 CTRL('_') は 0x00(NUL) 〜 0x1F(US) に, CTRL('?') は 0x7F(DEL) になります.
(ASCII コード表 (縦32×横4 のもの), JIS X 0211 制御コード表 と見比べてみてください.)

例えば,CTRL+C の文字コード (0x03) は CTRL('C') と書けます (注意:CTRL('c') は不可).

1989/05/12(金) 考案

7.5 文字列終端 NUL のアドレスを取得する.

次の関数は,文字列終端の '\0' (ワイド文字版は L'\0') のアドレスを返す.
(Win32 API の命名規則に倣って,関数名の最後の文字を 'A' (char 版) または 'W' (wchar_t 版) にしている.)

/*─────────────────────────────────────
書式  :char *StringEndA(const char *string);
        wchar_t *StringEndW(const wchar_t *string);
戻り値:文字列 string の終端 NUL のアドレス.
用途  :string の後ろに別の文字列を連結する場合や,string 直後の空きメモリを
        使いたい場合などに使用する.
1991/09/06(金) 作成 (StringEnd)
2005/05/23(月) 作成 (StringEndW),改名 (StringEnd → StringEndA)
2009/05/23(土) IMPLEMENT_StringEnd() で定義を共通化.
─────────────────────────────────────*/
#define IMPLEMENT_StringEnd(funcName, charType) \
  charType *funcName(const charType *string) \
  { \
    while(*string != (charType)0) string++; \
    return (charType*)string; \
  }

// StringEndA() を定義する.
IMPLEMENT_StringEnd(StringEndA, char)

// StringEndW() を定義する.
IMPLEMENT_StringEnd(StringEndW, wchar_t)

「こんな関数,一体何の役に立つの?」という幻聴 (?) がするので, 使用例を挙げておく.

■使用例1:複数回の sprintf() の出力を連結する. (バッファオーバーランに注意.)

char buffer[BUFSIZE];
char *dest = buffer;

sprintf(dest, "…", …);
sprintf(dest = StringEndA(dest), "…", …);
       :
sprintf(dest = StringEndA(dest), "…", …);

printf("%s\n", buffer /* dest じゃないよ */); // 連結した文字列を出力する.

■使用例2:あるディレクトリ内の全エントリ (ファイル,サブディレクトリなど) 名をフルパス名で出力する.

// 細かい仕様 (パス名の最大長および区切り文字) は一応 Windows 用.
const char * const directory = (ディレクトリのフルパス名);
const char *entry; // ディレクトリエントリ名
char *dest;
char pathName[MAX_PATH]; // パス名用バッファ

strcpy(pathName, directory);
dest = StringEndA(pathName);

// 必要ならば,directory の最後に区切り文字 '\\' を追加する.
if(必要) *dest++ ='\\';

while((entry = (directory から次のエントリ名を取得)) != NULL) {
  strcpy(dest, entry);      // pathName の必要部分だけを書き替える.
  printf("%s\n", pathName); // entry をフルパスで出力する.
}

この例だけだとあまり有難みがわからないが, ディレクトリツリーをスキャンしながらすべてのフルパス名を列挙する場合に, パス名の必要部分のみを書き替えるようにすると効率が良い (エントリごとにフルパス名全体を作り直すのは無駄が多い). StringEnd() は,その書き替えを行う先頭位置を探すのに使用できる.
(実際に StringEnd() が返すのはその1文字前 ('\\') の位置 (つまりサブディレクトリ名の終端 NUL).)

7.6 マクロ展開結果を文字列化する.

/*─────────────────────────────────────
概要  :マクロ展開結果を文字列化する.
引数  :macroArg:マクロ呼び出しを含む式など.
        (マクロの引数にできるものなら何でもよい.)
戻り値:macroArg をマクロ展開した結果を文字列リテラルにしたもの.
─────────────────────────────────────*/
#define MacroExpandedStringAux(x)          #x
#define MacroExpandedString(macroArg)  MacroExpandedStringAux(macroArg)

// マクロ展開結果を out に出力する.
#define PrintMacroExpansion(macroArg, out) \
  fprintf((out), "MacroExpandedString(%s) -> \"%s\"\n", \
          #macroArg, MacroExpandedString(macroArg))

■使用例1

PrintMacroExpansion(MAX_PATH + 1, stdout);
PrintMacroExpansion(fgetc(stdin) == EOF, stdout);
PrintMacroExpansion(assert(x), stdout);
PrintMacroExpansion(MacroExpandedString(MAX_PATH + 1), stdout);
PrintMacroExpansion(x + y, stdout); // マクロ呼び出しを含まない場合

// 実行結果 (VisualC++ 2012)
MacroExpandedString(MAX_PATH + 1) -> "260 + 1"
MacroExpandedString(fgetc(stdin) == EOF) -> "fgetc((&__iob_func()[0])) == (-1)"
MacroExpandedString(assert(x)) -> "(void)( (!!(x)) || (_wassert(L"x", L"test.cpp", 85), 0) )"
MacroExpandedString(MacroExpandedString(MAX_PATH + 1)) -> ""260 + 1""
MacroExpandedString(x + y) -> "x + y" // マクロ呼び出しを含まない場合 (そのまま)

■使用例2

コンパイル中にマクロの展開結果を表示する.(VC2012)

#pragma message("_MSC_VER=" MacroExpandedString(_MSC_VER))
#pragma message("_MSC_FULL_VER=" MacroExpandedString(_MSC_FULL_VER))
#pragma message("NTDDI_VERSION=" MacroExpandedString(NTDDI_VERSION))
#pragma message("WINVER=" MacroExpandedString(WINVER))
#pragma message("_WIN32_WINNT=" MacroExpandedString(_WIN32_WINNT))
#pragma message("_WIN32_IE=" MacroExpandedString(_WIN32_IE))
#pragma message("__FILE__=" MacroExpandedString(__FILE__))
#pragma message("__LINE__=" MacroExpandedString(__LINE__))

// コンパイル中の表示
1>------ ビルド開始: プロジェクト: CompileTest, 構成: Debug Win32 ------
1>  CompileTest.cpp
1>  _MSC_VER=1700
1>  _MSC_FULL_VER=170060610
1>  NTDDI_VERSION=0x06010000
1>  WINVER=0x0601
1>  _WIN32_WINNT=0x0601
1>  _WIN32_IE=0x0800
1>  __FILE__="..\\CompileTest.cpp"
1>  __LINE__=15
1>  CompileTest.vcxproj -> D:\C\CompileTest\VC2012\Win32\Debug\CompileTest.exe
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========

このマクロは,マクロ定数の値を文字列化するトリッキーな方法として知られている. 「macro stringize」で検索すると STR(x) とか STRINGIZE(x) などという名前が付けられていることが多いが,こんなあいまいな名前だと x がどういう条件ならば使えるのかよくわからない.それに,検索したページで示されている実行例は x が単純なマクロ定数の場合ばかりだが,実際にはマクロ定数や関数マクロを含む式なら何でもよい.

というわけでこのマクロは MacroExpandedString() という名前にした.
C89 以降なら動作が保証されているらしい.

実際には macroArg は式に限らず,マクロの引数にできるものならば何でもよい.
また macroArg にはマクロを含まなくてもよいが,その場合は macroArg がそのまま文字列になるだけなので何もうれしくない.(上の実行例の "x + y" の場合)

2012/11/25(日) 作成
2013/10/27(日) 使用例2を追記

■同様のマクロ (2016/10/22(土) 追記)

OpenCV の opencv2/core/version.hpp では,バージョン番号を文字列化するのに CVAUX_STR( ) というマクロが使われている.
CVAUX_STRW( ) はワイド文字列バージョン.

参考


8.入出力に関する関数・マクロ

8.1 バイナリファイルに変数 (ポインタ以外) を書く.

次のマクロは,任意の型の変数 var をバイナリファイルへの出力ストリーム out に書き出し, 成功したときそのときに限り真を返します.

#define WriteVar(var, out)   (fwrite(&(var), 1, sizeof(var), (out)) == sizeof(var))

var がポインタ型やポインタを含む型であっても書き出すことはできますが, そうする意味がありません.

このマクロを使うに当たり, データ型の選択やエンディアンの変換などを行わなければ, 作成されたバイナリファイルは CPU 依存,処理系依存となります.

考案日不明 (1989年頃?)

8.2 バイナリファイルから変数 (ポインタ以外) を読む.

次のマクロは,任意の型の変数 var をバイナリファイルの入力ストリーム in から読み込み, 成功したときそのときに限り真を返します.

#define ReadVar(var, in)    (fread(&(var), 1, sizeof(var), (in)) == sizeof(var))
考案日不明 (1989年頃?)

8.3 改行コード (CR,CRLF,LF) が混在するテキストファイルを読む.

改行コードが混在するテキストファイルを読む方法の参考用. 改行コードを統一して出力する関数の例です.

(2007/07/29(日) 追記)
「改行コード 判定」などで検索してくる人が時々いるので, 改行コードの種類別出現回数を数えるようにしました.
(でも判定しようとしても,混在してたら判定不能というオチがつくだけなんで全然うれしくない. そんなことするより,どんな改行コードでも読めるようにしておく方がいい.)

●テキストファイルの文字コードに関する前提 (2007/07/05(木) 追記)

#include <errno.h>
#include <stdio.h>
#include <string.h>

#if defined(TEXTFILE_IS_ASCII)
// テキストファイルの文字コードが ASCII (またはその亜種や拡張) の場合
#define CR 0x0D
#define LF 0x0A

#elif defined(TEXTFILE_IS_EBCDIC)
// テキストファイルの文字コードが EBCDIC 系の場合
#define CR 0x0D
#define LF 0x25

#endif /* defined(TEXTFILE_IS_*) */

#define OK   0
#define FAIL (-1)

/*─────────────────────────────────────
機能  :改行コード (CR,CRLF,LF) が混在するテキストファイルを読み,統一した
        改行コード eol に変換して別のファイルに出力する.テキストファイル内
        の文字を次のように変換する.
          CR LF → eol
          CR (LF が後続しない) → eol
          LF (CR が先行しない) → eol
入力  :(1) fileName:入力ファイル名.
        (2) eol:出力する改行コード文字列 (end of line).
        (3) out:出力ファイルへのストリーム.テキストモードでもバイナリモー
            ドでもよいが,eol はそれに合わせること.
戻り値:成功ならば OK,エラーならば FAIL.
注意  :(1) ファイルの最終行が改行なしで終わっている場合,この関数は eol を
            付加しない.
        (2) DOS や CP/M テキストファイルの EOF 文字 (CTRL+Z=0x1A) は,EOF
            とは見なさずそのまま出力する.(今でも使われてるの?)
            ・参考:EOF (通信用語の基礎知識)
2007/02/18(土) 作成 (原型)
2007/07/05(木) 文字コードの違いによる移植性の問題を修正.
2007/07/29(日) 改行コードの種類別出現回数を数えるようにした.
2007/10/20(土) ファイルの最終行が改行なしの場合の注意書きを追加.
2007/10/31(水) ・エラーメッセージを表示するようにした.
               ・DOS や CP/M テキストファイルの EOF 文字に関する注意を追記.
─────────────────────────────────────*/
int ConvertEol(const char *fileName, const char *eol, FILE *out)
{
  // 改行コードの種類別出現回数
  unsigned long cr_count   = 0; // CR の出現回数
  unsigned long crlf_count = 0; // CRLF の出現回数
  unsigned long lf_count   = 0; // LF の出現回数
  FILE *in;
  int c;

  // OS 依存性をなくすため,入力ファイルをバイナリモードでオープンする.
  // (UNIX では事実上バイナリモードのみ.)
  if((in = fopen(fileName, "rb")) == NULL) {
    fprintf(stderr, "%s: Can't open \"%s\" (%s)\n",
            __FUNCTION__, fileName, strerror(errno));
    goto Error;
  }

  while((c = fgetc(in)) != EOF) {
    // 注意:以下の CR,LF を '\r','\n' と書いてしまうと,
    // 例えばソース文字コードが ASCII 系でテキストファイルの
    // 文字コードが EBCDIC 系 (あるいはその逆) の場合に問題を生じる.
    switch(c) {
    case CR: if((c = fgetc(in)) == LF) {
               // CRLF が現れた場合
               crlf_count++;
             } else {
               // 単独の (つまり LF が後続しない) CR が現れた場合:
               // c を in に戻す.c=EOF の場合もあるが,ungetc()
               // が無視するので問題ない.
               ungetc(c, in);
               cr_count++;
             }
             goto EndOfLine;
    case LF: // 単独の (つまり CR が先行しない) LF が現れた場合
             lf_count++;
             // FALLTHROUGH
EndOfLine:   // 行末に達した場合:改行コードを出力する.
             if(fputs(eol, out) == EOF) goto WriteError;
             break;
    default: // 改行コード以外:そのまま出力する.
             if(fputc(c, out) == EOF) goto WriteError;
    }
  }
  fclose(in);

  // 改行コードの種類別に出現回数を出力する.
  printf("改行コードの出現回数:CR=%lu CRLF=%lu LF=%lu\n",
         cr_count, crlf_count, lf_count);
  return OK;

WriteError: fprintf(stderr, "%s: File write error (%s)\n",
                    __FUNCTION__, strerror(errno));
Error     : if(in != NULL) fclose(in);
            return FAIL;
}

9.エンディアンに関する関数・マクロ

イマドキの CPU では, ビッグエンディアンとリトルエンディアン以外はありえないと思いますが, ここでは シャレ 完全を期すため, 理論的に可能なすべての4バイト・エンディアンを扱っています.(笑)

9.1 実行時にエンディアンを判定する関数 (あらゆる4バイト・エンディアンに対応)

CHAR_BIT≧10 の場合にバグあり.気が向いたら修正予定.

#include <assert.h>
#include <limits.h> /* for CHAR_BIT */
#ifdef __unix__
#include <sys/types.h>
#else /* __unix__ */
typedef unsigned uint32_t;  /* 4バイト無符号整数 (CHAR_BIT≠8 でも同じ型名?) */
#endif /* __unix__ */

typedef int Bool;

/*─────────────────────────────────────
4バイト・エンディアンを表す定数.(ここではエンディアン・コードと呼ぶ.)
「"NUXI" と "IXUN" って何?」という方はこちらへ.
2006/12/28(木) 作成
2007/03/04(日) CHAR_BIT≠8 の場合にも対応 (笑)
─────────────────────────────────────*/
#define ENDIAN_CODE(byte0, byte1, byte2, byte3) \
  ((uint32_t)(((((((byte0) << CHAR_BIT) | (byte1)) \
                 << CHAR_BIT) | (byte2)) << CHAR_BIT) | (byte3)))

#define BIG_ENDIAN    ENDIAN_CODE(1, 2, 3, 4)
#define LITTLE_ENDIAN ENDIAN_CODE(4, 3, 2, 1)
#define NUXI_ENDIAN   ENDIAN_CODE(2, 1, 4, 3)
#define IXUN_ENDIAN   ENDIAN_CODE(3, 4, 1, 2)

/*─────────────────────────────────────
上記以外で,理論的に存在しうる4バイト・エンディアン (これで全部).
しかし,実在するという話は聞いたことがない.(笑)
2007/04/01(日) 作成
─────────────────────────────────────*/
#define UNXI_ENDIAN   ENDIAN_CODE(1, 2, 4, 3)
#define UINX_ENDIAN   ENDIAN_CODE(1, 3, 2, 4)
#define UIXN_ENDIAN   ENDIAN_CODE(1, 3, 4, 2)
#define UXNI_ENDIAN   ENDIAN_CODE(1, 4, 2, 3)
#define UXIN_ENDIAN   ENDIAN_CODE(1, 4, 3, 2)
#define NUIX_ENDIAN   ENDIAN_CODE(2, 1, 3, 4)
#define NIUX_ENDIAN   ENDIAN_CODE(2, 3, 1, 4)
#define NIXU_ENDIAN   ENDIAN_CODE(2, 3, 4, 1) // 実在しました!! (゚д゚ )
#define NXUI_ENDIAN   ENDIAN_CODE(2, 4, 1, 3)
#define NXIU_ENDIAN   ENDIAN_CODE(2, 4, 3, 1)
#define IUNX_ENDIAN   ENDIAN_CODE(3, 1, 2, 4)
#define IUXN_ENDIAN   ENDIAN_CODE(3, 1, 4, 2)
#define INUX_ENDIAN   ENDIAN_CODE(3, 2, 1, 4)
#define INXU_ENDIAN   ENDIAN_CODE(3, 2, 4, 1)
#define IXNU_ENDIAN   ENDIAN_CODE(3, 4, 2, 1)
#define XUNI_ENDIAN   ENDIAN_CODE(4, 1, 2, 3)
#define XUIN_ENDIAN   ENDIAN_CODE(4, 1, 3, 2)
#define XNUI_ENDIAN   ENDIAN_CODE(4, 2, 1, 3)
#define XNIU_ENDIAN   ENDIAN_CODE(4, 2, 3, 1)
#define XIUN_ENDIAN   ENDIAN_CODE(4, 3, 1, 2)

/*─────────────────────────────────────
機能  :現在実行中の CPU の4バイト・エンディアンを取得する.
        バイエンディアンの CPU は使ったことないけど,現在設定されているエン
        ディアンを返すはず.
戻り値:4バイト・エンディアンを表すエンディアン・コード (xxxx_ENDIAN).
        これはバイト列 (下位アドレスから) 0x01,0x02,0x03,0x04 に対応する
        値である.
2006/12/28(木) 作成
─────────────────────────────────────*/
uint32_t GetEndian4(void)
{
  uint32_t quadByte;
  unsigned char * const byte = (unsigned char*)&quadByte;
  unsigned i;

  for(i = 0;  i < sizeof(quadByte);  i++)
    byte[i] = (unsigned char)(i + 1);

  return quadByte;
}

/*─────────────────────────────────────
戻り値:Big Endian のときそのときに限り真.
2006/12/28(木) 作成 (関数)
2007/04/01(日) マクロに変更.
─────────────────────────────────────*/
#define IsBigEndian()     (GetEndian4() == BIG_ENDIAN)

/*─────────────────────────────────────
戻り値:Little Endian のときそのときに限り真.
2006/12/28(木) 作成 (関数)
2007/04/01(日) マクロに変更.
─────────────────────────────────────*/
#define IsLittleEndian()  (GetEndian4() == LITTLE_ENDIAN)

/*─────────────────────────────────────
使用例
─────────────────────────────────────*/
int main(void)
{
  uint32_t endian = GetEndian4();

  // 念のため uint32_t が4バイト整数であることを確認する.
  assert(sizeof(uint32_t) == 4);

#if 1
  // ほとんどの場合はこちらで十分のはず.
  printf("%s Endian (0x%08lX)\n",
         (endian == BIG_ENDIAN) ? "Big" :
         (endian == LITTLE_ENDIAN) ? "Little" : "Unknown",
         endian);
#else /* 0/1 */
  // 上のコードで "Unknown" が表示されるという,貴重な経験をした方はこちらをどうぞ.
  printf("%s Endian (0x%08lX)\n", EndianName4(endian), endian);
#endif /* 0/1 */

  return EXIT_SUCCESS;
}
1990/01/26(金) 考案 (原型)

9.2 エンディアンを変換 (big ⇔ little) する関数およびマクロ (CHAR_BIT 対応)

「エンディアン 変換」で検索してくる人がたくさんいるのでオマケ.
(big ⇔ little の変換ならばバイト逆順にするだけなんで簡単なことなんですが….
おっと,それを言っちゃあ,このページには簡単なことしか書いてない….(苦笑))

引っ越しました.
2007/03/03(土) 作成

9.3 エンディアン名を取得する関数 (あらゆる4バイト・エンディアンおよび CHAR_BIT に対応)

CHAR_BIT≧10 の場合にバグあり.気が向いたら修正予定.

/*─────────────────────────────────────
機能  :endianCode に対応する4バイト・エンディアン名を取得する.
        理論上可能なあらゆる4バイト・エンディアンに対応している.(笑)
        CHAR_BIT≠8 の場合にも対応している.(笑)
入力  :endianCode:エンディアン・コード.
戻り値:エンディアン名文字列.この文字列は,次回の EndianName4() の呼び出し
        の際に書き替えられる場合がある.endianCode が不正な場合には NULL を
        返す. 
注意  :(1) この関数はスレッドセーフではない.(笑)
        (2) この関数は (UNIX 環境以外でも) 正しく動作するはずであるが,ほと
            んどシャレで作った関数なので,実用的価値については疑問がある.(笑)
2007/03/04(日) 作成
─────────────────────────────────────*/
#ifdef unix
#undef unix
#endif /* unix */

const char *EndianName4(uint32_t endianCode)
{
  static const char unix[4] = { 'U', 'N', 'I', 'X' };
  static char name[sizeof(uint32_t) + 1];
  char usedFlag[sizeof(uint32_t)];
  unsigned byte, i;

  switch(endianCode) {
  case BIG_ENDIAN   : return "Big";
  case LITTLE_ENDIAN: return "Little";
  default:
    memset(usedFlag, FALSE, sizeof(usedFlag));
    name[sizeof(uint32_t)] = '\0';
    i = sizeof(uint32_t);
    do {
      byte = (unsigned)(endianCode & BYTEMASK(0, uint32_t));
      endianCode >>= CHAR_BIT;
      if((byte < 1) || (sizeof(uint32_t) < byte) || usedFlag[--byte])
        return NULL; // endianCode が不正.
      usedFlag[byte] = TRUE;
      name[--i] = unix[byte];
    } while(i > 0);
    return name;
  }
}
2007/03/04(日) 作成

10.日付・グレゴリオ暦/ユリウス暦計算関数

そのうち別ページに独立させる予定ですが,とりあえずここに入れておきます.

2007/11/19(月) 追記
2008/12/24(水) 修正

ここに挙げている関数は,グレゴリオ暦でもユリウス暦でも使用可能. 両者で処理が異なる場合はどちらの暦か julian フラグで指定するようにしている. これはユリウス暦用の処理がグレゴリオ暦用の一部として書けることが多いので, コードの重複を省いて保守性の向上と実行コードサイズの削減を図るため (同じようなコードを2回以上書きたくない).

ところでグレゴリオ暦の閏年の100年, 400年ルールを知らない人が少なからずいることは知っていたが, 逆に400年より長いルールがあると思っている人もいることを知ってちょっとビックリ. 確かにグレゴリオ暦に3,200年と80,000年の閏年ルールを入れると, 計算上は1年が平均 365.2422 日となって平均太陽年にかなり近くなるが, 現在のところそういうルールはないし, 将来入るという話も聞いたことがない.

参考

10.1 閏年の高速判定 (グレゴリオ暦/ユリウス暦)

#include <stdlib.h>

/*─────────────────────────────────────
機能  :閏年か否かを判定する (グレゴリオ暦/ユリウス暦).
入力  :(1) year:年.year≦0 でも可.
        (2) julian:ユリウス暦ならば真,グレゴリオ暦ならば偽.
戻り値:year 年がグレゴリオ暦/ユリウス暦の閏年のときそのときに限り真.
参考  :除算回数を極力減らして高速化を図っている.
        ・ユリウス暦の場合:除算を全く使用しない.
        ・グレゴリオ暦の場合:4の倍数年のときに限り除算を1回だけ行う.
          したがって平均除算回数は 1/4 回.
2007/08/19(日) 作成
2008/12/24(水) div() の使用をやめ,unsigned で除算を行うようにした.
─────────────────────────────────────*/
Bool IsLeapYear(int year, Bool julian)
{
  unsigned quot, rem;

  // y ← abs(year)
  const unsigned y = (unsigned)((year >= 0) ? year : -year);
  // 絶対値を取る理由:
  // (1) 負数の除算結果は処理系依存となるので,これを避けるため.
  //     (また処理系によっては,符号付より無符号の整数除算の方が
  //      わずかに速くなる可能性がある.)
  // (2) 4の倍数判定を除算ではなくビット演算で高速に行うため,
  //     万一 year が1の補数形式の負数だとまずい.

  if((y & 3U) != 0) return FALSE; // year が4の倍数ではない.
  // year が4の倍数の場合
  if(julian) return TRUE;

  // 除算は1回ですませる.次のうちの一つを選択.
  // (参考:割り算は遅い (HPC SYSTEMS Inc.)
#if 1
  // コンパイラが賢ければ,商と剰余を1回の除算命令
  // (または除算サブルーチンの呼び出し) で計算してくれるだろう.
  quot = y / 100U;
  rem  = y % 100U;
#elif 1
  // 上の方法がうまく最適化されない (除算を2回行ってしまう) 場合
  // (除算1回,乗算1回)
  quot = y / 100U;
  rem  = y - quot * 100U;
#else
  // コンパイラがアホやから自力で最適化する場合
  // ・x86 の場合:DIV 命令を使えば1回の除算で商と剰余が同時に求められる.
  asm {
    :
    :
  };
#endif

  if(rem != 0) return TRUE;      // year が100の倍数ではない.
  // year が100の倍数の場合
  return (quot & 3U) == 0;        // year が400の倍数 ⇔ 真
}

10.2 日付が有効か否かの判定 (グレゴリオ暦/ユリウス暦)

/*─────────────────────────────────────
機能  :日付が有効か否かを判定する (グレゴリオ暦/ユリウス暦).
入力  :(1) (year, month, day):日付.year≦0 でも可.
        (2) julian:ユリウス暦ならば真,グレゴリオ暦ならば偽.
戻り値:有効な日付のときそのときに限り真.
2007/09/17(月) 作成
2007/10/10(水) 改定 (ValueInRangeInclusive() を採用.)
─────────────────────────────────────*/
Bool IsValidDate(int year, unsigned month, unsigned day, Bool julian)
{
  // 1〜12月の日数 (平年).
  static const unsigned char daysInMonth[12] = {
    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  };

  return ValueInRangeInclusive(1, month, 12) && (1 <= day) &&
         ((day <= daysInMonth[month - 1]) ||
          // 計算コストの低い順,偽になる確率が高い順に条件を並べる方がよい.
          ((day == 29) && (month == 2) && IsLeapYear(year, julian)));
}

10.3 ○月のN回目のW曜日は何日?

/*─────────────────────────────────────
機能  :ある月のn回目のdow曜日の日付を求める.

入力  :(1) n:1〜5.
        (2) dow:曜日番号.(0:日曜,…,6:土曜).
        (3) dow1:その月の1日の曜日番号.1日ではなくday日の曜日がわかって
            いる場合には FirstDayOfWeek() を併用すればよい.

戻り値:その月のn回目の dow 曜日の日付.

参考  :曜日番号は一応,日〜土を0〜6としたが,この関数に限り1〜7でもかま
        わないし,何曜日から始めてもよい.(7日間にわたって1ずつ増える数列で
        あれば何でもよい.)

例    :2007年3月:1日は木曜(4)で,第3金曜(5)は16日.
            NthDayOfWeekToDay(3, 5, 4) == 16.

1990/05/04(金) 作成 (自作 Calendar ライブラリ版)
2007/03/21(水) 作成 (Web 公開版)
2007/09/17(月) すべての引数の型を int から unsigned に変更.
2008/12/24(水) 乗算の遅い処理系で乗算を使わないように変更.
─────────────────────────────────────*/
unsigned NthDayOfWeekToDay(unsigned n, unsigned dow, unsigned dow1)
{
  unsigned day;

  // day ← (最初の dow 曜日の日付)−1
  if(dow < dow1) dow += 7;
  day = dow - dow1;

  // day ← n回目の dow 曜日の日付 (day + 7 * (n - 1) + 1)
#if 乗算の速い処理系
  day += 7 * n - 6;
#else // 乗算の遅い処理系
  day += (n << 3) - n - 6;
#endif
  return day;
}

10.4 ○月D日 (W曜) は,その月の何回目のW曜日?

/*─────────────────────────────────────
機能  :○月day日 (w曜日) が,その月の何回目のw曜日か (週番号) を返す.
戻り値:○月day日の週番号 (1〜5).
2007/03/21(水) 作成
2007/09/17(月) 引数および戻り値の型を int から unsigned に変更.
2008/10/07(火) 改名 (DayToNthDayOfWeek() → DayToWeekNumber())
─────────────────────────────────────*/
unsigned DayToWeekNumber(unsigned day)
{
  return (day - 1) / 7 + 1;
}

10.5 同じ月の別の日の曜日を求める.

/*─────────────────────────────────────
機能  :ある月の day0 日が dow0 曜日のとき,同月の別の日 day の曜日を求める.

入力  :(1) day,day0:同じ月の日付.
        (2) dow0:day0 の曜日番号 (0〜6,何曜日から始めてもよい).

戻り値:day 日の曜日番号.

解説  :戻り値を dow とすると数学的には
          dow = (dow0 + (day - day0)) % 7.
        しかしC言語では,被除数が負になるとまずいので,それを防ぐために7の
        倍数を加算して被除数が負にならないようにする.そのような倍数の最小値
        である35を採用する.

2007/08/19(日) 作成 (day=1 専用関数 FirstDayOfWeek())
2007/09/17(月) 任意の日の曜日を計算できるように一般化,AnotherDayOfWeek に
               改名.FirstDayOfWeek() はマクロ化.(どちらも舌足らずでイマイチ
               な名前だが,あまり長くなるのも困る….)
2007/10/28(日) 説明の改訂 (変更漏れ修正を含む).
─────────────────────────────────────*/
unsigned AnotherDayOfWeek(unsigned day, unsigned day0, unsigned dow0)
{
  return (dow0 + 35 + day - day0) % 7;
}

// 同じ月の1日の曜日番号を返す.
#define FirstDayOfWeek(day0, dow0)    AnotherDayOfWeek(1, (day0), (dow0))

10.6 ライブラリ関数を使わずに指定日時 (年月日曜時) が夏時間か判定する.

はてな人力検索の質問に対する回答 #5. 日時 (年月日,曜,時) とその年の夏時間の規則が指定されたとき, OS やライブラリの時刻関数を全く使わずにその日時が夏時間か否かを判定する方法.

参考:夏時間/サマータイム (Wikipedia)

10.7 グレゴリオ暦/ユリウス暦 ⇒ 通算日数変換

アルゴリズムの説明

10.8 通算日数 ⇒ グレゴリオ暦/ユリウス暦変換

アルゴリズムの説明

11.双方向線型リスト処理マクロ集

ダウンロード (マクロ集 ListMacro.h + サンプルプログラム (ソース,Windows 用実行ファイル,実行結果))

●データ構造
┏━━━┯━┓
┃last  │・╂─────────────────────┐
┠───┼─┨      ┏━━━━┯━┓    ┏━━━━┯━┓│  ┏━━━━┯━┓
┃first │・╂──→┨forward │・╂─→┨forward │・╂┴→┨forward │/┃
┗━━━┷━┛      ┠─┬──┴─┨    ┠─┬──┴─┨    ┠─┬──┴─┨
 リストヘッダ       ┃/│backward┠←─╂・│backward┠←─╂・│backward┃
                    ┗━┷━━━━┛    ┗━┷━━━━┛    ┗━┷━━━━┛
                         要素 #1             要素 #2             要素 #N
●特長・使い方 ●機能

12.時代遅れの(?)マクロ

1980年代後半には,市販の MS-DOS 用のCコンパイラは ANSI 準拠になっていたと思います.しかしそれから10年以上経っても, UNIX ワークステーションを買うともれなく付いてくるCコンパイラは K&R 準拠のままでした.(今はどうなんだろう?)

以下のマクロは,MS-DOS 用 (ANSI 準拠) と UNIX 用 (K&R 準拠) 両方のCコンパイラで同じソースをコンパイルできるようにするために使っていたものです.

12.1 関数のプロトタイプ宣言

ANSI C では,関数や関数ポインタの型を宣言するには, 次のようにプロトタイプ宣言を使うのはご承知のとおり.

/* ANSI */
void MyFunc(char *string, int number);
int (*MyFuncPointer)(double x, double y);

しかし K&R ではプロトタイプ宣言を使うことはできず, 引数リストの部分は次のように空でないといけない.

/* K&R */
void MyFunc();
int (*MyFuncPointer)();

/* 注意:本来の K&R には void 型はないが,当時の処理系では使えた.*/

これらを ANSI,K&R 両方でコンパイルできるようにするため, 次のように書くのは二度手間だしブサイク.

#ifdef __STDC__
/* ANSI */
void MyFunc(char *string, int number);
int (*MyFuncPointer)(double x, double y);
#else /* __STDC__ */
/* K&R */
void MyFunc();
int (*MyFuncPointer)();
#endif /* __STDC__ */

そこで次のように書いていた.

#ifdef __STDC__
/* ANSI */
#define PROTOTYPE(rettype_fname, args)  rettype_fname args
#else /* __STDC__ */
/* K&R */
#define PROTOTYPE(rettype_fname, args)  rettype_fname()
#endif /* __STDC__ */

PROTOTYPE(void MyFunc, (char *string, int number));
PROTOTYPE(int (*MyFuncPointer), (double x, double y));

/* 関数定義本体の引数は K&R スタイルで書く.*/
void MyFunc(string, number)
char *string;
int number;
{
  :
  :
}

/* 引数に関数ポインタがある場合はこんな具合 */

PROTOTYPE(void MyFunc2, (int (*funcPtr)(double x)));

void MyFunc2(funcPtr)
PROTOTYPE(int (*funcPtr), (double x));
{
  :
  :
}

__STDC__ は ANSI 準拠の処理系なら自動的に定義される … かと思いきや, 処理系固有の拡張機能を OFF にしないと定義されなかった. それは困るので,実際には __STDC__ じゃなくて NOPROTOTYPE のようなシンボルを 自分で定義して使っていた.

PROTOTYPE マクロの原型 (prototype?) は,確か1980年代後半に SunOS の /usr/include の中で見つけたものだったと思う. K&R の処理系がまだあるのかどうか知らないが, 古いオープンソースなどには同様のマクロが残っている可能性がある. 2000年頃,入社数年目の若手が何やらソースを眺めて首をひねっていた. 肩越しに覗いてみると,PROTOTYPE と同様のマクロだったので解説したことを覚えている.


13.謎の検索ワード集

「C言語 CRLF」,「C++ CRLF」

時々検索して来る人がいるけど,一体何を知りたいのかよくわからない.

猫でも知ってるとおり,UNIX 系 OS の改行コードは LF (NL), DOS/Windows の改行コードは CR LF.


■参考


「C マクロ …」

C/C++ のマクロがどういうものか全くわかっていないとしか思えない検索ワードが多すぎるので追加.

C/C++ のマクロとは,「コンパイルするに, ソースコードの一部を置き換えるための規則」だということは理解してますか?
たとえば,

#include <stdio.h>
#include <stdlib.h>

#define ONE         1
#define ADD_ONE(x)  ((x) + ONE)

int add_one(int x)
{
  return x + 1;
}

int main(void)
{
  printf("ADD_ONE(100) = %d\n", ADD_ONE(100));
  printf("add_one(100) = %d\n", add_one(100));
  return EXIT_SUCCESS; // stdlib.h で定義されているマクロ
}

というソースコードは,プリプロセッサが次のように置き替えたでコンパイルされる.この置き換えを マクロ展開 (macro expansion) という.

int main(void)
{
  printf("ADD_ONE(100) = %d\n", ((100) + 1));
  printf("add_one(100) = %d\n", add_one(100));
  return 0;
}

つまりプリプロセッサは,ONE や ADD_ONE( ) などのマクロが呼び出された場所に, それらの定義の中身をぶちまけるのである.プリプロセスが終わると ONE や ADD_ONE() の定義は不要になるので, コンパイルが始まった時点でマクロというものはもはや存在していない.

なおプリプロセス後のファイル (*.i) は,次のようにすれば見ることができる.

「C言語 マクロ 戻り値」

この検索ワードも以前から多い.たぶん「関数では値を返すために return を使うのに,なぜ関数マクロでは使用しないのか?」 という疑問なんだろうと思っていたら,今日「C return で値を返すマクロ」で検索して来た人がいたので, 「ああ,やっぱりそうか」と思った. マクロが関数と同じようなものだと誤解している初心者が多いんだろう.
(マクロをちゃんと解説していない入門書や授業が多いのか?)

上のマクロ展開の説明を読めばわかると思うのでこれ以上の説明は不要だろうけど, そのサンプルコードで,マクロ ADD_ONE(x) の定義を関数 add_one( ) と同じように (下記) 変えたらどうなると思う?

#define ADD_ONE(x)      return ((X) + ONE)

理解できない人は,マクロ展開後に main() がどうなったか,*.i ファイルを見て確認すること.

「C言語 動的 マクロ」,「C言語 動的マクロ」

上に書いたとおり C/C++ のマクロはコンパイルソースコードの一部を置き替えるための規則だけど, 「動的」というのはプログラムの実行時 (もはやソースコードが変更されても影響を受けない) に何かをするということだから, 「C/C++ の動的マクロ」というのは名辞矛盾 (概念上ありえない).
(余談:インタプリタ言語である Lisp のマクロは動的.コンパイラのある Lisp 処理系ならば静的にも使える.)

「C マクロ for」,「C マクロ if」,「C++ マクロ new」,「C マクロ #if」

もしかしたら for 文や if 文などに展開されるマクロを知りたいのかもしれないけど, 一応念のために言っとく.
for,if,new などはマクロじゃなくて予約語 (コンパイラにとって特別な意味を持つ単語) ね.

#if,#ifdef,#include など,行頭の '#' から始まるのはプリプロセッサ命令 (または指令,指示,ディレクティブ).
マクロを定義する #define もプリプロセッサ命令の一つ.

「C マクロ #」,「C マクロ ##」

# も ## もマクロの中で使うものだから検索ワードとしてはおかしくないけど, これも一応念のために言っとく.
# はマクロじゃなくて文字列化演算子 (stringizing operator),## はトークン連結演算子 (token concatenation operator) ね.


■余談 (昔話)

# と ## は ANSI C で導入されたが,それ以前の多くの処理系では次のようなトリックで実現していた.

// トークンの文字列化
#ifdef __STDC__
// ANSI C
#define STRINGIZE(token)    #token
#else /* __STDC__ */
// ANSI 以前:文字列内でもマクロ置換される処理系が多かった.
#define STRINGIZE(token)    "token"
#endif /* __STDC__ */

// トークン連結
#ifdef __STDC__
// ANSI C
#define CONCATENATE(token1, token2)     token1##token2
#else /* __STDC__ */
// ANSI 以前:コメントを単に削除する (空文字列に置き換える) 処理系が多かった.
// ANSI C はコメントを単一のスペースに置き換えるのでこの方法は使えない.
// 例:CONCATENATE(xyz, 123) → xyz/**/123 → xyz123
#define CONCATENATE(token1, token2)     token1/**/token2
#endif /* __STDC__ */

「C言語 マクロ定義 L LL U UL ULL L""」

これは間違いなく勘違いしている!
L,U,UL などはマクロじゃなくて整数リテラルのサフィックスね.
L だけはワイド文字(列)リテラル L"…" にも使う (この場合は先頭に付く).

参考

「C マクロ ? :」

? : はマクロじゃなくて三項演算子.Cの入門書って読んだことある?
なんか噂によると,三項演算子を知らない「Cプログラマ (自称)」が結構多いみたいだけど….

そもそも '?'':' もマクロ名に使える文字じゃないでしょ!
どうしてこれをマクロだと思ったの? (謎)
他にも構文や演算子のことをマクロだと勘違いしているらしい検索ワード多いし….
(上に書いた for,if,new もそうかもしれない.)

「構造体配列 要素数」

なぜ構造体の配列だけ特別だと思うの?

ArraySizeOf()


14.象の卵

なんか以前から,象の卵 (あるはずのないもの) を探しに来る人が結構いる.
象の卵をいつまでも探し続ける人達(´・ω・)カワイソス….
というわけで追記.

13.1 構造体のメンバ数の取得方法?

時々「C言語 構造体 要素数(メンバ数)」等で検索して来る人がいるけど, Cでは構造体のメンバ数を取得することはできない. 仮に構造体のメンバ数だけを取得できたとしても, メンバの番号を指定してその型, サイズ,オフセットなどを取得できなければ何の役にも立たないのに, 一体何に使うつもりですか? (激謎)

Cのプログラムで構造体定義データを使いたければ, (二度手間だけどCの構造体宣言とは別に) 自分で用意するしかない. 「それはしたくないけど,どうしてもプログラム中で構造体定義データを使いたい」 という人は, リフレクション機能のあるプログラム言語を使用してください.

2007/06/01(金) 初版
2008/08/13(水) 改定

2011/11/27(日) 追記

これを読んでもまだ構造体のメンバ数の取得方法を探しまわっている人が何人もいるようだ. 「Cの構造体メンバ数の取得方法があるはずだ」と信じている人は, Cプログラムをコンパイルしてできた実行ファイルにも構造体というものが実在していると思い込んでいるんだろう. 実際には,構造体に含まれていたメンバ (基本データ型) が,相対的位置関係を保って順番にメモリ上に並んでいるだけだ (まあ,Cではそれを「構造体」と呼ぶわけだが).

CPU が理解 (直接処理) できるのは,それがサポートしている基本データ型 (整数型および浮動小数型) だけであって,構造体を扱う命令というのは (Java チップなど特殊なプロセッサ以外には) 存在しない.

したがって,メモリ上のどこからどこまでが一つの構造体だとか, メンバ数やメンバ名などという情報は,コンパイルが終わったら全部捨てられてしまう. 実行ファイルに構造体というものが存在しないのだから, 当然メンバ数だのメンバ名だのを取得する方法も存在しない. そうしたければ自分で明示的にそのデータを別途用意するしかない.

そもそもCは「面倒見のよい便利な言語」ではなく, 「高級アセンブラ」である. ハードウェアをプログラマが完全に制御できるようにするための言語がアセンブラだが, さすがにすべてのプログラムをアセンブラで書くのは大変な労力だし, 移植性もないのでCを使うのである. だからCは,メモリ上のデータを1バイト, 1ビット単位でプログラマの思い通りに操作できなければならない. Cのプログラマが構造体を使うのはメモリ上にメンバが順番に並んでほしいからであって, 頼んでもいないメンバ数やメンバ名などをコンパイラが勝手に追加すると困ることがある. (特にメモリがわずかしかない組み込み機器など)

13.2 構造体/共用体のエンディアン?

「構造体 エンディアン」とか「共用体 エンディアン」で検索してくる人が時々いるけど, そんなものはありません.
「エンディアン」がどういうものか理解してますか?
(「共用体を使ってエンディアン変換したい」ということならアリだけど.)

「エンディアン」を一般的に定義すると,

複数の要素 (普通はバイトまたはビット) からなるデータを表現するのに, 要素を並べる順序に自由度がある場合, 順序の選択肢の一つ一つがエンディアンである.
といえると思う.余談だが,日付の表記方法を次のように呼ぶプログラマもいるらしい.

Cの構造体のメンバの順序は (非標準の処理系依存機能を使わない限り) ソースに記述された順序と一致するので,エンディアンの違い (つまり要素順序の自由度) などどいうものはない. また共用体では,同時に存在するメンバは高々一つだけなので, そもそもメンバの (空間的) 順序というものがない.

普通「構造体のエンディアン変換」という場合, 「構造体のエンディアン」(そんなものないってば!) を変換するという意味ではなく,構造体に含まれる基本データ型 ((複数バイト長の) 整数型,浮動小数型) のうち必要なものをそれぞれエンディアン変換するという意味. それに変換元や変換先の全メンバのエンディアンが同じとは限らない. 共用体の場合も同様だが,変換する時点でどのメンバが存在しているのかを判定し, そのメンバだけを変換する必要がある.

2008/08/13(水)

13.3 new[ ] で確保した配列の要素数を取得する方法?

「C++ 動的配列 要素数」などで検索して来る人も時々いる. new[ ] で確保した配列の要素数を取得したいということだろう. 結論を先に書くと,C++ の規格ではそういう方法は存在しないだけでなく, 処理系依存の方法や裏技さえ存在しない処理系もある
(C++ はあまり使ってないし,規格書も持っていないので間違っているかもしれない.)

最初は「どうせ配列の直前の数バイト (処理系依存) に要素数が入っているだろうから, それを読み出すだけで OK」と簡単に考えていた.

class A {
  :
  :
};

A *array = new A[NELEMENTS];

// 多くの32ビット処理系では,K はたぶん -1 か -2.
// (デバッグや最適化オプションの指定によっても変わる可能性がある.)
const size_t nElements = ((const size_t*)array)[K];

↓このページによると,g++ (バージョン不明) では配列の直前の4バイト (=sizeof(size_t)) に要素数が入っているから,上のコードで K=-1 とすれば取得できるはず.

ただしデストラクタを呼ぶ必要がない場合, その4バイトは存在しない (と上記のページに書いてある). なるほど,無駄なメモリを使わないための最適化ってことね. つまり処理系が (実行時の) 配列要素数を管理していないので, 上のコードは使えないし,当然別の取得方法も存在しない.

g++ 以外の処理系でも上記のような最適化を行っている可能性がある. 最適化やデバッグオプションの指定や配列要素のアラインメントによっても, 「配列直前の数バイト」のサイズと構造が変わる可能性がある.

逆に言うと,配列の要素数を取得する手段を提供する処理系があるとすれば, 配列のメモリサイズが最適化されていない (不要な場合でもヘッダに要素数を持っている) ということになる. (特に汎用) メモリ管理ライブラリの開発者が, どれだけ高速化と省メモリを両立させるために知恵を絞っているか, 想像すらしたことないでしょ?(笑)

2009/01/11(日)

13.4 検索ワード「関数のサイズ C言語 取得(計算)」

できません.どうしても知りたければ,(コンパイラではなく) リンカのオプションでマップファイル (*.map) を作成するように指定し,それを見て確認してください.

ただし,

このように,最終的に関数のサイズ (どころか存続) を決定するのはリンカであってコンパイラではない.だからコンパイラ (コンパイル時) に関数の (最終的な) サイズがわかるわけがない.

2013/03/15(金)

15.参考図書

珠玉のプログラミング

楽天で買う

価格:3,570円(税込、送料別)

珠玉のプログラミング―本質を見抜いたアルゴリズムとデータ構造
ジョン ベントリー
ピアソンエデュケーション
売り上げランキング: 7857
おすすめ度の平均: 4.5
5 アルゴリズムについて勉強したい人には必読の本です
5 楽しく読めるプログラミングの本
5 「プログラミング」と言う作業を見つめなおすのに最適。「設計する」と言う概念がよく分からない初級プログラマにも
5 納得!アルゴリズムは重要
5 プログラマなら読むべき本



プログラミング作法

楽天で買う

価格:2,940円(税込、送料別)

プログラミング作法
プログラミング作法
posted with amazlet at 10.06.20
ブライアン カーニハン ロブ パイク
アスキー
売り上げランキング: 20088
おすすめ度の平均: 4.5
5 良著です。
5 入門書の次の次の次くらいに!
3 繰り返し読む必要あり
5 絶対にお勧めの本です
5 良いプログラマになりたいあなたに



【送料無料】アルゴリズムクイックリファレンス

楽天で買う

価格:3,360円(税込、送料別)

アルゴリズムクイックリファレンス
George T. Heineman Gary Pollice Stanley Selkow
オライリージャパン
売り上げランキング: 28776



アルゴリズム辞典

楽天で買う

価格:26,250円(税込、送料別)

アルゴリズム辞典
アルゴリズム辞典
posted with amazlet at 09.07.29

共立出版
売り上げランキング: 319557
おすすめ度の平均: 5.0
5 基本的なアルゴリズムを収集



ハッカーのたのしみ―本物のプログラマはいかにして問題を解くか
ジュニア,ヘンリー・S. ウォーレン
エスアイビーアクセス
売り上げランキング: 27265
おすすめ度の平均: 5.0
5 ビットの楽しみ
5 たのしみ? たしなみ?
5 ちゃんと読むと得した気分になれます
5 最後の頑張りに効きます
5 Hackっていうのは、こういうコトさ

著者ページ:Hacker's Delight
第2版 (英語版) 第2章が無料で読める.

主に2進整数やビットパターンのさまざまな演算技法について解説している. 基本的には (特定のプログラミング言語に依存しない) 数学的な解説が中心だが,C言語によるサンプルコードも示している.

アラインメントやオフセットの計算に使える「2の冪乗の倍数への切り上げ/切り下げ」や, メモリブロックの管理に使える「次の2の冪乗への切り上げ/切り下げ」, (ビット/バイト) エンディアン変換や FFT (高速フーリエ変換) で使われるビットリバース (ビット逆順) などを含む「ビットやバイト単位の並べ替え」など.

■Cのオーバーフローについて (2015/02/21(土) 追記)

Cは整数やポインタの演算オーバーフローを教えてくれないので, 必要ならば自力で判定しなければならないが,色々落とし穴が存在するらしい. (下記「Cのオーバーフローに関するリンク集」を参照. ちなみに整数・ポインタ演算オーバーフローチェックは,むしろアセンブラの方がずっと簡単. CPU の演算命令がオーバーフローフラグをセットしたり,例外を発生させたりする.)

この中の「C コンパイラの最適化の問題」の最後の方で,「ハッカーのたのしみ」 に四則演算のオーバーフローのチェック方法が記載されていると書かれている. 自分の本は現在行方不明なので内容を確認できないが,Amazon の「なか見!検索」の目次には確かに項目がある (第2章 2-12,第2版の目次では 2-13).

さらに追記:第2版 (英語版) 第2章 (pp.28-36) が無料で読める. (今見つけたばかりなのでまだ読んでない.)

●参考:Cのオーバーフローに関するリンク集




省メモリプログラミング

楽天で買う

価格:4,410円(税込、送料別)

省メモリプログラミング―メモリ制限のあるシステムのためのソフトウェアパターン集 (Software patterns series)
ジェイムズ ノーブル チャールズ ウィアー
ピアソンエデュケーション
売り上げランキング: 80302
おすすめ度の平均: 5.0
5 メモリ制限のあるシステム
5 分類が上手い
5 組み込み向けのデザインパターンとしてはまともです。
5 すべての設計者・プログラマに必須

「省メモリ」とあるが,メモリ管理の高速化についても参考になる技法が解説されている. 時々「malloc 高速(化)」などで検索して来る人がいるが, malloc の速度をこれ以上大きく改善する余地はあまりないと思う (あるとしても非常に難しいだろう).その理由は,

それでもなお malloc の高速化それ自体を目指したい人には「(悲愴な顔で) 頑張ってください」としか言えないが, 「アプリケーションのメモリ管理を高速化したいから高速な malloc が欲しい」というのならあまりにも芸がなさすぎる. そういう人はこの本の「第5章 Memory Allocation:メモリ割当て」を読んで反省してください.(笑)

アプリを高速化したいなら,できるだけ malloc/free を呼び出す頻度を減らすこと. そのためには1回の malloc で確保した大きな領域 (メモリプール) に多数のオブジェクトを詰め込む必要がある (これは省メモリにもなる) が, どのオブジェクトを同じ領域に入れるべきかはオブジェクトの寿命 (extent),サイズ, アラインメントなどを考慮して決める必要がある. 特に,寿命を知っているのはアプリケーションだけだ.

ところで,省メモリが高速化につながる場合も多い.昔からメモリと速度のトレードオフ (高速でメモリを大量に使用するアルゴリズム (例えばテーブル参照) を使うか, それとも低速でメモリを少ししか使用しないアルゴリズムを使うか) がよく問題になるので,省メモリと高速化は両立しないと思い込んでいる人もいるだろう. しかし最近の CPU は命令実行速度に比べてメモリアクセス速度がはるかに遅いので, 無駄なメモリを削減したり,メモリ上のデータ配置を変える (同時期に頻繁に使用するデータをなるべく少数のキャッシュラインに集中させる) と高速化されることも多い. (1970年代以前の CPU は命令実行とメモリアクセスが同期していたので同程度の速度だった.)

さて,ここで問題.次のコードで大きな2次元配列 (例えば画像データ) をコピーする場合,(1) と (2) のどちらが速いか.またその理由を述べよ. (理由を書かなければ0点)

int src[M][N], dest[M][N];
unsigned i, j;

// (1)
for(i = 0;  i < M;  i++)
  for(j = 0;  j < N;  j++)
    dest[i][j] = src[i][j];

// (2)
for(j = 0;  j < N;  j++)
  for(i = 0;  i < M;  i++)
    dest[i][j] = src[i][j];



BINARY HACKS

楽天で買う

価格:3,360円(税込、送料別)

Binary Hacks ―ハッカー秘伝のテクニック100選
高林 哲 鵜飼 文敏 佐藤 祐介 浜地 慎一郎 首藤 一幸
オライリー・ジャパン
売り上げランキング: 35295
おすすめ度の平均: 5.0
5 組み込み系の開発者は必携です
5 ハードコア?なソフトウエア
5 大工さんにおける電動工具の紹介本
5 当然教科書ではない。でも、とても参考になります。
5 バイナリアンの基本



ガベージコレクションのアルゴリズムと実装

楽天で買う

価格:3,360円(税込、送料別)

ガベージコレクションのアルゴリズムと実装
中村 成洋 相川 光
秀和システム
売り上げランキング: 4312
おすすめ度の平均: 3.5
2 擬似コードのバグは見て見ぬふり
5 GCの入門書として今のところ最強!



【送料無料】リンカ・ローダ実践開発テクニック

楽天で買う

価格:2,940円(税込、送料別)

著者サポートページ




Linkers & loaders

楽天で買う

価格:3,360円(税込、送料別)

Linkers & Loaders
Linkers & Loaders
posted with amazlet at 10.06.17
John R. Levine
オーム社
売り上げランキング: 139529
おすすめ度の平均: 3.0
4 プログラムが実行される仕組みが良く分かる
1 概要が書かれた本
1 ひどい訳
5 dllのしくみがわかる!
5 パッケージソフト開発者の必読書



実践デバッグ技法

楽天で買う

価格:2,940円(税込、送料別)

実践 デバッグ技法 ―GDB、DDD、Eclipseによるデバッギング
Norman Matloff Peter Salzman
オライリージャパン
売り上げランキング: 79214



デバッガの理論と実装 (ASCII SOFTWARE SCIENCE Language)
ジョナサン・B. ローゼンバーグ
アスキー
売り上げランキング: 295384
おすすめ度の平均: 5.0
5 デバッグできないとき
5 普通に読んでいくだけでも面白い
5 デバッカの理論には必読



【送料無料】ARM組み込みソフトウェア入門

楽天で買う
価格:4,620円(税込、送料別)




【送料無料】ARM Cortex-M3システム開発ガイド

楽天で買う
価格:3,360円(税込、送料別)

CQ出版社の書籍案内 (内容見本PDFあり,立ち読み可)




【送料無料】世界の定番ARMマイコン超入門キットSTM32ディスカバリ〔完全版〕

楽天で買う
価格:4,200円(税込、送料別)




【送料無料】ARM9/11/XScaleハンドブック

楽天で買う
価格:2,520円(税込、送料別)

ARM9/11/XScaleハンドブック (TECH I Processor)

CQ出版
売り上げランキング: 324832



【送料無料】ARMでOS超入門

楽天で買う
価格:2,730円(税込、送料別)

ARMでOS超入門 (ARMマイコン)
桑野 雅彦 岡田 好一 共著
CQ出版
売り上げランキング: 108107



アセンブリ言語の教科書

楽天で買う

価格:3,990円(税込、送料別)

アセンブリ言語の教科書
愛甲 健二
データハウス
売り上げランキング: 72168
おすすめ度の平均: 3.5
3 アセンブリ言語を広範囲に解説した本
1 冗長感は否めない、そして索引も必要
1 ゆとり教育の教科書
5 アセンブリ言語の良書
5 著者の熱意が伝わります

著者サイト



【送料無料】いかにして問題をとくか第11版

楽天で買う
価格:1,575円(税込、送料別)

いかにして問題をとくか
G. ポリア
丸善
売り上げランキング: 41

16.サイト内関連ページ


17.外部へのリンク


18.更新履歴

このページの主な更新は Blog でお知らせします.

  1. 2006/09/02(土) 公開
  2. 2006/10/10(火) ArraySizeOf() の注意書きを追加.
  3. 2006/10/15(日) BitSizeOf() の注意書きを追加.
  4. 2006/10/31(火) ALLOCA(),MALLOC(),CALLOC(),REALLOC()CTRL() を追加.
  5. 2006/11/04(土) WriteVar()ReadVar() を追加.
  6. 2006/11/25(土) BitSizeOf() の定義と注意書きを修正.
  7. 2006/12/13(水) サイト内関連ページを追加.
  8. 2006/12/27(水) 実行時にエンディアンを判定する関数を追加.
  9. 2006/12/28(木) 実行時にエンディアンを判定する関数を改定.
  10. 2006/12/30(土)
  11. 2007/01/14(日) ページ新設に伴い,リンク先を変更.
  12. 2007/02/03(土) 改行コード (CR,CRLF,LF) が混在するテキストファイルを読むを追加.
  13. 2007/03/03(土) エンディアンを変換 (big ⇔ little) するマクロ (CHAR_BIT 対応) を追加.
  14. 2007/03/04(日) エンディアン名を取得する関数を追加.
  15. 2007/03/21(水) 日付・グレゴリオ暦計算関数を追加.
  16. 2007/04/01(日)
  17. 2007/04/16(月) REVERSE_ENDIAN()ByteReverse() を追加.
  18. 2007/04/25(水) コンパイル時に assert() をチェックするマクロ STATIC_ASSERT へのリンクを追加.
  19. 2007/05/13(日) シフト JIS の2バイト文字 ⇔ 区点番号/JIS/EUC-JP 変換を追加.
  20. 2007/05/16(水) MemberArraySizeOf() の定義を改定.
  21. 2007/06/01(金) 「C言語 構造体 要素数(メンバ数)」等で検索して来る人へのメッセージを追記.
  22. 2007/06/21(木) 時代遅れの(?)マクロを追記.
  23. 2007/06/24(日)〜
  24. 2007/06/25(月) IntegerIsTwosComplement()IntegerIsOnesComplement()IntegerIsSignAndAbs() を追加.
  25. 2007/07/05(木) ConvertEol() の移植性の問題を修正,前提条件を追記.
  26. 2007/07/14(土) StringEnd(), {i,l,ll}{ceil,floor}() を追加.
  27. 2007/07/28(土) LowestOneBit()AddressAlignmentOf() を追加.
  28. 2007/07/29(日) ConvertEol() に, 改行コードの種類別出現回数を数える機能を追加.
  29. 2007/07/30(月) 動的構造体の説明に,無理やりC言語風に書いた例を追記.
  30. 2007/08/12(日) Unicode 関数・マクロ集 を追加.
    • UTF-16 符号単位がサロゲートか否かを判定する.
    • サロゲート・ペア ⇔ Unicode スカラ値変換
  31. 2007/08/18(土) Unicode 関数・マクロ集 に「UTF-16 文字列関数」を追加.
  32. 2007/08/20(月) 下記を追加.
  33. 2007/08/25(土) シフト JIS 2バイト文字の判定を追加. (ネットで第1バイトの巧妙な判定方法を見つけたので.)
  34. 2007/09/17(月) 下記を追加.
  35. 2007/09/18(火) BYTEMASK の定義を改定.
  36. 2007/10/08(月) ValuesInOrder()ValueInRange() を追加.
  37. 2007/10/14(日) 「エンディアンに関する関数・マクロ」を, 「別ページ」に引越し開始.
  38. 2007/10/20(土)
  39. 2007/10/28(日) AnotherDayOfWeek() の説明を修正.
  40. 2007/10/31(水) ConvertEol() に,エラーメッセージ表示を追加, DOS や CP/M テキストファイルの EOF 文字に関する注意を追記.
  41. 2007/11/08(木) 上記「EOF 文字」のリンク先を変更.
  42. 2007/11/19(月) 日付・グレゴリオ暦/ユリウス暦計算関数の冒頭にちょっと追記.
  43. 2008/05/03(土) GCD() の戻り値の説明文を訂正.
  44. 2008/06/24(火) 「配列の終端アドレスを取得する.」を追加.
  45. 2008/08/13(水) 象の卵構造体/共用体のエンディアン?を追加, 構造体のメンバ数の取得方法?をそこに移動.
  46. 2008/12/24(水) IsLeapYear()NthDayOfWeekToDay() を改定. (ちょっと高速化?)
  47. 2009/01/11(日) new[ ] で確保した配列の要素数を取得する方法?を追加.
  48. 2009/01/12(月) StructBaseFromOffset() を追加,StructBase() を改定.
  49. 2009/05/23(土) トラップ表現のある処理系で問題になるかもしれないので, ByteReverse() を修正.
  50. 2009/09/21(月) ArrayLast() を追加.
  51. 2009/11/22(日) IsPowerOf2() を追加.
  52. 2010/03/27(土) {OCT,DEC,HEX}DIGITS(), MSBMASK() を追加.
  53. 2010/06/12(土) 参考図書追加.
  54. 2010/07/18(日) ValueInRangeOp() を追加.
  55. 2010/07/24(土)「謎の検索ワード集」を追加.
  56. 2011/01/18(火) DECDIGITS() を高速化.
  57. 2011/05/10(火) 「C マクロ …」を追加.
  58. 2011/05/21(土) 「C マクロ #」,「C マクロ ##」を追加.
  59. 2011/09/25(日) ArraySizeOf() に図解を追記.
  60. 2011/11/27(日) 「構造体のメンバ数の取得方法?」に追記.
  61. 2012/07/21(土) 「構造体配列 要素数」を追加.
  62. 2012/08/04(土) DECDIGITS() の近似式を追記.
  63. 2012/08/09(木) DECDIGITS() に16ビット処理系での注意を追記.
  64. 2012/09/01(土)「構造体メンバのオフセットを返す.」に,解説と注意を追加.
  65. 2012/11/25(日)「マクロ展開結果を文字列化する」を追加.
  66. 2013/02/11(月)「C言語 マクロ 戻り値」を追加.
  67. 2013/03/15(金) 『検索ワード「関数のサイズ C言語 取得(計算)」』を追加.
  68. 2013/10/27(日)「マクロ展開結果を文字列化する」に使用例2を追加.
  69. 2015/01/03(土) 下記を追加.
  70. 2015/02/21(土)
    • ICEIL の説明に証明とオーバーフローに関する注意を追記.
    • offsetof の説明に「内包と外延」を追記.
  71. 2015/08/09(日) ValueInRangeExclusive() を追加, ValueInRange を ValueInRangeInclusive() に改名.
  72. 2016/06/04(土)
  73. 2016/10/22(土)「マクロ展開結果を文字列化する」に OpenCV の例を追記.


Copyright © 2007-2016 noocyte.
E-mail: relipmoced (a) yahoo.co.jp
  (" (a) " を半角のアットマークに書き替えてください.)
リンクはご自由に.
「noocyte のプログラミング研究室」トップページに戻る.