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

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

公開:2006/09/02(土)
最終更新:2009/11/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. 各種整数型の仕様を調べるテストプログラム (気が向いたら公開予定)
    6. 指定された整数型が表現可能な最小値および最大値を返す. (IntegerMin(),IntegerMax())
    7. 整数除算で端数切り上げ. (商のみ,ICEIL())
    8. 整数除算で端数切り上げ. (商+剰余,iceil(),lceil(),llceil())
    9. 整数除算で端数切り下げ. (商+剰余,ifloor(),lfloor(),llfloor())
    10. 整数除算 (商+剰余,C標準ライブラリ関数 div() の64ビット版)
    11. 最大公約数を求める.
    12. 無符号整数の最下位の '1' のビットだけを抽出する.(LowestOneBit())
    13. 2の冪乗か否かを判定する.(IsPowerOf2())
  2. 数値型に関するマクロ
    1. 複数の数値またはポインタが昇順になっているか? (ValuesInOrder())
    2. 数値またはポインタが範囲内か? (ValueInRange())
  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())
  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. 象の卵
    1. 構造体のメンバ数の取得方法?
    2. 構造体/共用体のエンディアン?
    3. new[ ] で確保した配列の要素数を取得する方法?
  14. 参考図書
  15. サイト内関連ページ
  16. 外部へのリンク
  17. 更新履歴

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(月) 考案

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

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

#define IntegerSignAndAbs(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 各種整数型の仕様を調べるテストプログラム

気が向いたら公開予定

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

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

#define ICEIL(dividend, divisor) \
  (((dividend) + ((divisor) - 1)) / (divisor))

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

「切り上げ 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.9 整数除算で端数切り下げ.(商+剰余,関数)

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

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

書式  :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.10 整数除算 (商+剰余,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.11 最大公約数を求める.

#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(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 GcdRecursiveUL(unsigned long m, unsigned long n)
{
  return (n <= 0) ? m : GcdRecursiveUL(n, m % n);
}

/*─────────────────────────────────────
機能  :GcdUL(),GcdRecursiveUL() のテスト.
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(m, n);
    gcd2 = GcdRecursiveUL(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.12 無符号整数の最下位の '1' のビットだけを抽出する.

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

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

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

/*─────────────────────────────────────
機能  :2の冪乗か否かを判定する.
入力  :uintValue:無符号整数 (≧1).
戻り値:uintValue が2の冪乗のときそのときに限り真.
注意  :uintValue=0 の場合も真を返す.
2009/11/22(日) 作成
─────────────────────────────────────*/
#define IsPowerOf2(uintValue)   ((((uintValue) - 1U) & (uintValue)) == 0)

/*─────────────────────────────────────
機能  :IsPowerOf2() のテスト.
─────────────────────────────────────*/
void Test_IsPowerOf2(unsigned startValue, unsigned endValue)
{
  unsigned value = startValue;
  const unsigned nHexDigits = ICEIL(BitSizeOf(value), 4); // 16進表示の桁数

  printf("Start: 0x%0*X %u\n", nHexDigits, value, value);
  for(;;  value++) {
    if(IsPowerOf2(value)) printf("0x%0*X %u\n", nHexDigits, value, 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;
}

/*─────────────────────────────────────
実行結果 (VC2003)
─────────────────────────────────────*/
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

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())

/*─────────────────────────────────────
機能  :数値またはポインタが範囲内にあるか否かを判定する.
入力  :数値型,またはオブジェクトへのポインタ型.
        (不完全型や関数へのポインタは不可)
        (1) minValue:最小値.
        (2) value:値.
        (3) maxValue:最大値.
戻り値:minValue ≦ value ≦ maxValue のときそのときに限り真.
2007/10/02(火) 作成 (ValueInRange)
2007/10/06(土) 改名 (ValueInRange → ValuesInOrder3)
─────────────────────────────────────*/
#define ValueInRange(minValue, value, maxValue) \
  ValuesInOrder3(minValue, value, maxValue)

3.配列に関するマクロ

3.1 配列の要素数を返す.

次のマクロは,配列 array[] の要素数を返します.

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

使用例:

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

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

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

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

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

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


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

考案日不明 (1987年以前)

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

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

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

■注意 (2006/10/10(火) 追記)

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

/* 上記の例の続き */

const char * const *StringArrayPointer = StringArray;

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

うまくいかない理由は,sizeof(StringArrayPointer) が配列のサイズ (バイト数) ではなく,配列へのポインタのサイズだからです.

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

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


■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(sizeof((arrayPtr)[0]), (n)))
#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 構造体メンバのオフセットを返す.

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

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

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

X-Window では,Intrinsic.h の中で同様のマクロ XtOffsetOf() が定義されています.また少し異なるマクロ XtOffset() は,引数として type の代わりに type* を取ります.offsetof() が C 標準マクロとなった現在では, これらよりも offsetof() を使う方がいいと思います.

余談:小耳に挟んだ話によると,ごく一部の CPU (Cray Y-MP など) には, なぜか構造体の先頭オフセットが0でないものがあるそうです.

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言語の構文として書かれる (静的) 構造体とは無関係です.

次のマクロは,先頭アドレスを 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 コード表 (縦16×横8 または 縦32×横4 のもの), JIS X 0211 制御コード表 と見比べてみてください.)

例えば,CTRL-C の文字コード (Ox03) は 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).)


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, "Can't open \"%s\" (%s)\n", 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++;
             // no break
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, "ConvertEol: File write error (%s)\n", 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回ですませる.次のうちの一つを選択.
#if 1
  // コンパイラが賢ければ,商と剰余を1回の除算命令
  // (または除算ルーチンの呼び出し) で計算してくれるだろう.
  quot = y / 100U;
  rem  = y % 100U;
#elif 1
  // 上の方法がうまく最適化されない (除算を2回行ってしまう) 場合
  // (除算1回,乗算1回)
  quot = y / 100U;
  rem  = y - quot * 100U;
#else
  // コンパイラがアホやから自力で最適化する場合
  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(水) 改定 (ValueInRange() を採用.)
─────────────────────────────────────*/
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 ValueInRange(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)
  // day += 7 * n - 6;
  day += (n << 3) - n - 6;  // 乗算を使わないようにした.
  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 の処理系がまだあるのかどうか知らないが, 古いオープンソースなどには同様のマクロが残っている可能性がある. 数年前,入社数年目の若手が何やらソースを眺めて首をひねっていた. 肩越しに覗いてみると,PROTOTYPE と同様のマクロだったので解説したことを覚えている.


13.象の卵

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

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

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

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

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

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 = ((size_t*)array)[K];

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

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

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

逆に言うと,配列の要素数を取得する手段を提供する処理系があるとすれば, 配列のメモリサイズが最適化されていない (不要な場合でもヘッダが付いている) ということになる.

2009/01/11(日)

14.参考図書

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

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



15.サイト内関連ページ


16.外部へのリンク


17.更新履歴

このページの主な更新は 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() を追加.


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


track feed noocyte