【C】雑多な処理集

 

ビット操作

10年以上前に作ったコードなので正しく動くのか不確かである。
従って事前の動作確認が必要である。

/* ビット操作のおぼえがき */
#include <stdio.h>

/* このマクロがあるとビット反転が断然楽 */
#define  BIT31  0x80000000
#define  BIT30  0x40000000
#define  BIT29  0x20000000
#define  BIT28  0x10000000
#define  BIT27  0x08000000
#define  BIT26  0x04000000
#define  BIT25  0x02000000
#define  BIT24  0x01000000
#define  BIT23  0x00800000
#define  BIT22  0x00400000
#define  BIT21  0x00200000
#define  BIT20  0x00100000
#define  BIT19  0x00080000
#define  BIT18  0x00040000
#define  BIT17  0x00020000
#define  BIT16  0x00010000
#define  BIT15  0x00008000
#define  BIT14  0x00004000
#define  BIT13  0x00002000
#define  BIT12  0x00001000
#define  BIT11  0x00000800
#define  BIT10  0x00000400
#define  BIT9   0x00000200
#define  BIT8   0x00000100
#define  BIT7   0x00000080
#define  BIT6   0x00000040
#define  BIT5   0x00000020
#define  BIT4   0x00000010
#define  BIT3   0x00000008
#define  BIT2   0x00000004
#define  BIT1   0x00000002
#define  BIT0   0x00000001

/*-----------------------------------------------*/
/* 上位ビットから走査し,0以外のビット番号を返す */
/*-----------------------------------------------*/
static int
bsr(unsigned int bit)
{
    int           i    = -1;
    unsigned int  mask = 0x1;
#ifdef __i386__
    (void)mask;
    asm("bsr  %0, %%eax"::"g"(bit));
    asm("movl %%eax, %0":"=g"(i)); /* eax => bit */
#else  /* __i386__ */
#if 0
    (void)mask;
    i =  (31 - __builtin_clz(bit)); /* -O2 以上にすると,コンパイルの時点で展開される */
#else /* -O2 以上だとエラーになるので注意 */
    for(i = 31; i >= 0; i--)
    {
        if((bit & (mask << i)) != 0){ break; }
    }
#endif
#endif /* __i386__ */
    return i;
}

/*-----------------------*/
/* 特定のビットを1にする */
/*-----------------------*/
static unsigned int
bit_n_en(unsigned int x, unsigned int bit)
{
    printf("ビット%dを1にする: 0x%08x\n", bsr(bit), (x |= bit));
    return (x |= bit);
}

/*----------------------*/
/* 特定のビットをクリア */
/*----------------------*/
static unsigned int
bit_n_dis(unsigned int x, unsigned int bit)
{
    printf("ビット%dをクリア: 0x%08x\n", bsr(bit), (x &= (~bit))); /* ~ ...補数 */
    return (x &= (~bit));
}

/*------------------------------*/
/* 全ビットが反転したかチェック */
/*------------------------------*/
static int
is_bit_inv(unsigned int cur, unsigned int prev) 
{
    printf
    (
        "反転したビットがあるか: %s\n",
        (cur ^ prev) ? "ある" : "なし"    /* ^ ... XOR(排他論理和) */
    );

    return (cur ^ prev) ? 1 : 0;
}

/*--------------------------------*/
/* 特定ビットが反転したかチェック */
/*--------------------------------*/
static int
is_bit_n_inv(unsigned int cur, unsigned int prev, unsigned bit) 
{
    printf
    (
        "ビット%dのみ反転したか: %s\n",
        bsr(bit),
        ((cur ^ prev) & bit) ? "反転した" : "反転せず"
    );
    return ((cur ^ prev) & bit) ? 1 : 0;
}

/*--------------*/
/* 全ビット反転 */
/*--------------*/
static unsigned int
bit_inv(unsigned int x)
{
    printf("全ビット反転: 0x%08x\n", ~x);
    return ~x;
}

/*----------------------------*/
/* 特定ビットのみ反転(楽な方) */
/*----------------------------*/
static unsigned int
bit_n_inv(unsigned int x, unsigned bit)
{
    printf("ビット%dのみ反転: 0x%08x\n", bsr(bit), x ^ bit);
    return (x ^ bit);
}

/*----------------------------------*/
/* 特定ビットのみ反転(ややこしい版) */
/*----------------------------------*/
static unsigned int
bit_n_inv2(unsigned int x, int bitn)
{
    unsigned mask = (0x1 << bitn);
    printf("ビット%dのみ反転(2): 0x%08x\n",
           bitn, ((x & ~mask) | ((x & mask)^mask)));
    /*********************************************************
     * 上記のbit処理のメモ                                   *
     * (1) (x & ~mask)      ... x & ~mask で bitn のみクリア *
     * (2) (x &  mask)      ... bitn のみ ON にする          *
     * (3) (x &  mask)^mask ... bitn のみ反転                *
     * (4) (x & ~mask)|((x & mask)^mask) ... OR演算          *
     *********************************************************/
    return ((x & ~mask) | ((x & mask)^mask));
}

int
main()
{
    unsigned int x = 0x0000ffff; /* 元の値 */
    unsigned int y = 0x0000ffff; /* 変更後の値のつもり */

    printf("元の値: 0x%08x\n", x);

    bit_inv(x);               /* 全ビット反転           */
    bit_n_inv(x, BIT8);       /* Bit8のみ反転(楽)       */
    bit_n_inv2(x, 8);         /* Bit8のみ反転(苦)       */
    is_bit_inv(x, y);         /* 反転したビットがあるか */
    is_bit_n_inv(x, y, BIT8); /* 反転したビットがあるか */
    bit_n_dis(x, BIT8);       /* Bit8のみクリア         */
    bit_n_en(x, BIT31);       /* Bit31のみ1にする       */

    return 0;
}

 

排他的論理和を使ってスワップ処理をする

  • 多分正しく動作するのだと思うが、保証なし。
#include <stdio.h>
/*
 * a,b,2つの変数の値を入れ替える場合の処理(1-3)
 * (1) a と b の排他的論理和を a に格納する
 * (2) (1)のa と 初期値b の排他的論理和を b に格納する
 * (3) (3)のb と (1)のa の排他的論理和を a に格納する
 */

int
main()
{
    int a = 3;
    int b = 9;

    printf("[before] (a, b) = (%d, %d)\n", a, b);    
    /* (1) a と b の排他的論理和を a に格納する */
    /* (2) (1)のa と 初期値b の排他的論理和を b に格納する */
    /* (3) (3)のb と (1)のa の排他的論理和を a に格納する */
#if 0
    a = ~(a + b);
    b = ~(a + b);
    a = ~(a + b);
#else
    a = (~a + ~b); 
    b = (~a + ~b);
    a = (~a + ~b);
#endif
    printf("[after] (a, b) = (%d, %d)\n", a, b);    

    return 0;
}

 

バイトオーダを確認するプログラム

  • 次のように振る舞う
    • リトルエンディアンの場合, 78 56 34 12
    • ビッグエンディアンの場合, 12 34 56 78
#include <stdio.h>

void
check_byte_order(int i)
{
    unsigned    char    *p = (unsigned char *)&i;
    printf("%x %x %x %x\n", p[0], p[1], p[2], p[3]);
} 

int
main()
{
    int    i = 0x12345678;
    check_byte_order(i);
    return    0;
}

 

バイトオーダ変換プログラム

10年以上前に作ったコードなので、結果が正しいのか不明である。
使用する前に確認が必要である。

#include <stdio.h>

/* 4バイトデータをビッグエンディアンに変換する */
#define  ULONG_LITTLE2BIG(a)                                       \
         (unsigned long)                                           \
         (                                                         \
             ((unsigned long)(*((unsigned char *)a    ))      ) |  \
             ((unsigned long)(*((unsigned char *)a + 1)) <<  8) |  \
             ((unsigned long)(*((unsigned char *)a + 2)) << 16) |  \
             ((unsigned long)(*((unsigned char *)a + 3)) << 24)    \
         )

/* 4バイトデータをリトルエンディアンに変換する */
#define  ULONG_BIG2LITTLE(a)                                       \
         (unsigned long)                                           \
         (                                                         \
             ((unsigned long)(*((unsigned char *)a    )) << 24) |  \
             ((unsigned long)(*((unsigned char *)a + 1)) << 16) |  \
             ((unsigned long)(*((unsigned char *)a + 2)) <<  8) |  \
             ((unsigned long)(*((unsigned char *)a + 3))      )    \
         )
/* 2バイトデータをビッグエンディアンに変換する */
#define  USHORT_LITTLE2BIG(a)                                      \
         (unsigned long)                                           \
         (                                                         \
             ((unsigned long)(*((unsigned char *)a    ))      ) |  \
             ((unsigned long)(*((unsigned char *)a + 1)) <<  8)    \
         )

/* 2バイトデータをリトルエンディアンに変換する */
#define  USHORT_BIG2LITTLE(a)                                      \
         (unsigned long)                                           \
         (                                                         \
             ((unsigned long)(*((unsigned char *)a + 2)) <<  8) |  \
             ((unsigned long)(*((unsigned char *)a + 3))      )    \
         )

void
check_byte_order(void)
{
    int                  i  = 0x12345678;
    short       int      si = 0x1234;

    unsigned    char    *pi = (unsigned char *)&i;
    /* リトルエンディアンの場合, 78 56 34 12  *
     * ビッグエンディアンの場合, 12 34 56 78  */
    printf("%x %x %x %x\n", pi[0], pi[1], pi[2], pi[3]);

    /* 4バイトデータをビッグエンディアンに変換する */
    printf("4byte: 0x%08x\n", (unsigned)ULONG_LITTLE2BIG(&i));

    /* 2バイトデータをビッグエンディアンに変換する */
    printf("2byte: 0x%04x\n", (unsigned)USHORT_LITTLE2BIG(&si));
} 

int
main()
{
    check_byte_order();
    return    0;
}

浮動小数を16進数に変換する

  • %fをサポートしていない環境でも小数値を表示出来る
#include <stdio.h>

void
float_2_hexel(void)
{
    float           f1  = 0.750;
    float           f2  = 0.250;
    float           f3  = 0.0;
    unsigned    int ui1 = 0;
    unsigned    int ui2 = 0;
    unsigned    int ui3 = 0;

    f3 = f1 - f2;
    printf("f1: %f\n", f1);
    printf("f2: %f\n", f2);
    printf("f3: %f\n", f3);

    ui1 = (unsigned int)*((unsigned int *)&f1);
    ui2 = (unsigned int)*((unsigned int *)&f2);
    ui3 = (unsigned int)*((unsigned int *)&f3); 
    /* ↑ このとき, ui1 - ui2 をすると,0x00c0_0000 となり,
     * 0.5 = 0x3f00_0000 と異なってしまい不可であるので注意。
     * つまり,f3 は f1 - f2 でしか求められないということ。
     */

    printf("ui1: %f   (%08x)\n", *(float *)&ui1, ui1);
    printf("ui2: %f   (%08x)\n", *(float *)&ui2, ui2);
    printf("ui3: %f   (%08x)\n", *(float *)&ui3, ui3);
}

int
main()
{
    float_2_hexel2();

    return 0;
}

 

文字コードを表示するプログラム

1文字ずつ読み取って 16進数( %x) で表示するプログラム。
引数にはファイルを指定するが、引数がなければ標準入力を待つ。

/* bd ... byte dump */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>

static int invoke_bd(int argc, char * argv[]);
static int bd(FILE * fp);

int
main(int argc, char * argv[])
{
  invoke_bd(argc, argv);
  return 0;
}

static int
invoke_bd(int argc, char * argv[])
{
  FILE    * fp = (argc == 1) ? stdin : NULL;
  int       err = 0;
  int       ret = 0;

  if (argc == 1)
  {
    err = bd(stdin);
    ret = (!err) ? 0 : err;
  }

  for (++argv; *argv != NULL; argv++)
  {
    assert(fp = fopen(*argv, "r"));

    err = bd(fp);
    if (err)
    {
      fprintf(stderr, "ERROR: [%s]\n", *argv);
    }

    err = fclose(fp);
    assert(!err);
  }
  return 0;
}

static int
bd(FILE * fp)
{
  int     c;

  while (fread(&c, 1, sizeof(char), fp) == sizeof(char))
  {
#if 0
    if (fwrite(&c, 1, sizeof(char), stdout) != sizeof(char))
    {
      perror("fwrite");
      return 1;
    }
#else
    printf("%02x", c & 0x000000ff);
#endif
  }

  return 0;
}

 

構造体メンバのオフセットを得る

/* http://www5d.biglobe.ne.jp/~noocyte/Programming/CMacros.html */
/* 構造体メンバのオフセットを知る */
#include <stdio.h>

#define  offsetof(type, member)    ((size_t) &( ((type *)0)->member ))

typedef struct _hoge_t
{
  int    i;
  char   buf[256];
  int    j;
  char   str[8];
  char   flg[2];
}
  hoge_t;

void show_offset(void);

int
main()
{
  show_offset();
  return 0;
}

void
show_offset(void)
{
  printf("i  : %d\n", offsetof(hoge_t, i));
  printf("buf: %d\n", offsetof(hoge_t, buf));
  printf("j  : %d\n", offsetof(hoge_t, j));
  printf("str: %d\n", offsetof(hoge_t, str));
  printf("flg: %d\n", offsetof(hoge_t, flg));
}

 

文字列表記の16進数を数値に変換する

  • 変換した 16進数値を使って引き算を行う
/* 
 * 16進数の引き算
 * % ./a.out 080496d8 080495dc
 * 0x000000fc
 */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int
main(int argc, char * argv[])
{
    char          * p    = NULL;
    unsigned int    hex1 = 0x0;
    unsigned int    hex2 = 0x0;
    
    assert(argc == 3);

    hex1 = strtol(argv[1], &p, 16);
    hex2 = strtol(argv[2], &p, 16);
    
    printf("0x%08x - 0x%08x = 0x%08x\n", hex1, hex2, hex1-hex2);

    return 0;
}

 

malloc したサイズを動的に得る

  • 環境依存のコードなので正常動作するかはそれぞれかと思われる
/* http://d.hatena.ne.jp/kzk/searchdiary?word=malloc */

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

static
void malloc_size_chk(void * p)
{
    printf("[%p] %d byte\n", p, *(((size_t *)p)-1) & ~0xf);
}

int
main()
{
    void * p = NULL;

    p = malloc(1024);
    malloc_size_chk(p);
    free(p);

    p = malloc(999);
    malloc_size_chk(p);
    free(p);

    p = malloc(1023);
    malloc_size_chk(p);
    free(p);

    return 0;
}

関数型の使用例

#include <stdio.h>

#define PICKUP_ARG 2

/* Fn_err_arg is function(int) returning void */
typedef void Fn_err_arg(int);
/* Fn_print_arg is function(char **) returning pointer to char */
typedef char *Fn_get_argn(char **, int);

/* err_arg is Fn_err_arg => err_arg is function(int) returning void */
Fn_err_arg    err_arg;
Fn_get_argn   get_argn;

int
main(int argc, char *argv[])
{
    char *ret = NULL;

    if(argc < PICKUP_ARG)
    {
        err_arg(argc);
    }
     
    ret = get_argn(argv, PICKUP_ARG);
    printf("arguments of %d is %s\n", PICKUP_ARG, (ret) ? ret : "(null)");
    return  0;
}

void
err_arg(int i)
{
    printf("ERROR: argc = %d... argument is too few\n", i);
}

char *
get_argn(char **arg, int n)
{
    return *(arg+n-1);
}