プログラミングなどなど

プログラミングに関係しそうな内容を

C言語:ビットフィールド

SPINのstate vector(状態ベクトル?)が気になってデバッグしているときに、pan.cでビットフィールドが使われている箇所を見つけました。

C言語の本で存在は知っていたのですが、使用したこともなく、また実際のコードで見たのは初めてでした。state vectorの理解にも必要になりそうだったので、pan.cを参考にコードを作成し動作を確認してみました。

確認に使用したコードは以下のものになります。

ソース bitfield.c

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

/* ビットフールドを使用した構造体 */
struct P1
{
  unsigned int _pid : 8;
  unsigned int _t   : 3;
  unsigned int _p   : 3;
};

/* pan.cのソースコードを参考にダンプ用の関数を作成 */
void dumpstate(struct P1 *p)
{
  int i;
  char *cp = (char *)p;
  for (i = 0; i < sizeof(struct P1); i++, cp++)
  {
    printf("%d,", *cp);
  }
  putchar('\n');

  cp = (char *)p;
  for (i = 0; i < sizeof(struct P1); i++, cp++)
  {
    printf("0x%02x,", *cp);
  }
  putchar('\n');
}

int main(void)
{
  struct P1 p1;

  memset(&p1, 0x00, sizeof(struct P1));
  dumpstate(&p1);

  p1._pid = 1;
  dumpstate(&p1);

  p1._t = 1;
  dumpstate(&p1);

  p1._p = 1;
  dumpstate(&p1);

  p1._pid = 0xff;
  dumpstate(&p1);

  p1._t = 3;
  dumpstate(&p1);

  p1._p = 3;
  dumpstate(&p1);

  return 0;
}

コンパイルから実行まで

gdbデバッグも行いたいので -g3 オプションを付与してコンパイルしています。

コンパイルと実行結果
$ gcc -Wall -g3 bitfield.c 
$ ./a.out 
0,0,0,0,                   
0x00,0x00,0x00,0x00,
1,0,0,0,                   <- p1._pid = 1 の実行後
0x01,0x00,0x00,0x00,
1,1,0,0,                   <- p1._t = 1 の実行後
0x01,0x01,0x00,0x00,
1,9,0,0,                   <- p1._p = 1 の実行後
0x01,0x09,0x00,0x00,
-1,9,0,0,
0xffffffff,0x09,0x00,0x00, <- p1._pid = 0xff の実行後
-1,11,0,0,
0xffffffff,0x0b,0x00,0x00, <- p1._t = 3 の実行後
-1,27,0,0,
0xffffffff,0x1b,0x00,0x00, <- p1._p = 3 の実行後

gdbでの実行

$ gdb ./a.out 
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...done.
(gdb) break main
Breakpoint 1 at 0x7f3: file bitfield.c, line 32.
(gdb) run
Starting program: /home/makoto/blog/12/a.out 

Breakpoint 1, main () at bitfield.c:32
32	{
(gdb) n
35	  memset(&p1, 0x00, sizeof(struct P1));
(gdb) 
36	  dumpstate(&p1);
(gdb) 
0,0,0,0,
0x00,0x00,0x00,0x00,
38	  p1._pid = 1;
(gdb) p sizeof(struct P1)
$1 = 4
(gdb) x/4tb &p1
0x7fffffffdc74:	00000000	00000000	00000000	00000000
(gdb) n
39	  dumpstate(&p1);
(gdb) n
1,0,0,0,
0x01,0x00,0x00,0x00,
41	  p1._t = 1;
(gdb) x/4tb &p1
0x7fffffffdc74:	00000001	00000000	00000000	00000000
                ^^^^^^^^_pid部分
(gdb) n
42	  dumpstate(&p1);
(gdb) n
1,1,0,0,
0x01,0x01,0x00,0x00,
44	  p1._p = 1;
(gdb) x/4tb &p1
0x7fffffffdc74:	00000001	00000001	00000000	00000000
                                     ^^^_p部分
(gdb) n
45	  dumpstate(&p1);
(gdb) 
1,9,0,0,
0x01,0x09,0x00,0x00,
47	  p1._pid = 0xff;
(gdb) x/4tb &p1
0x7fffffffdc74:	00000001	00001001	00000000	00000000
                                  ^^^_t部分
(gdb) n
48	  dumpstate(&p1);
(gdb) 
-1,9,0,0,
0xffffffff,0x09,0x00,0x00,
50	  p1._t = 3;
(gdb) x/4tb &p1
0x7fffffffdc74:	11111111	00001001	00000000	00000000
                ^^^^^^^^_pid部分
(gdb) n
51	  dumpstate(&p1);
(gdb) 
-1,11,0,0,
0xffffffff,0x0b,0x00,0x00,
53	  p1._p = 3;
(gdb) x/4tb &p1
0x7fffffffdc74:	11111111	00001011	00000000	00000000
                                     ^^^_p部分
(gdb) n
54	  dumpstate(&p1);
(gdb) 
-1,27,0,0,
0xffffffff,0x1b,0x00,0x00,
56	  return 0;
(gdb) x/4tb &p1
0x7fffffffdc74:	11111111	00011011	00000000	00000000
                                  ^^^_t部分
(gdb) n
57	}
構造体とメモリ

上記のようにgdbでp1を保存しているメモリをバイナリダンプで出力した結果を見ると、私の環境では以下のようになっているということですね。

         +0バイト   +1バイト
アドレス  iiiiiiii 00tttppp
         ^^^^^^^^   |||---

  ^ : _pid 部分 8ビット
  - : _p 部分 3ビット
  | : _t 部分 3ビット

_pと_tとメモリの位置を間違えてしまいそうです。