ビットフィールドの難点

マイコンのI/Oポートのアクセスにはよく共用体のビットフィールドを使いますよね。たいてい、メーカーがヘッダファイルを提供してくれてます。
たしかに便利で、可読性も良くなるんですが、当然ながらポインタには渡せないんですよねぇ。たとえば、8ビットの入力ポートがあって、8つのボタンの入力信号を受けてる場合、各ビット(各ボタン)独立に、共通した処理を関数化したいときに困ります。

struct st_io {
  union {
      unsigned char BYTE;               /*  Byte Access */
        struct {                          /*  Bit  Access */
          unsigned char B7:1;         /*    Bit 7     */
            unsigned char B6:1;           /*    Bit 6     */
            unsigned char B5:1;           /*    Bit 5     */
            unsigned char B4:1;           /*    Bit 4     */
            unsigned char B3:1;           /*    Bit 3     */
            unsigned char B2:1;           /*    Bit 2     */
            unsigned char B1:1;           /*    Bit 1     */
            unsigned char B0:1;           /*    Bit 0     */
        } BIT;
    }DR;
}

#define IO  (*(volatile struct st_io *)ポートのアドレス);

int main( void ){
    unsigned char *p;
....
    p = &(IO.DR.BIT.B3);  ← エラーになる
    hoge(p);  // 共通化したい処理
....
    return 0;
}

ビットマスクを配列で持って、添え字をパラメータにするしかないか?

// ビットマスクのテーブル
const unsigned char BIT_MSK[ 8 ] = {
  0x01,
  0x02,
  0x04,
  0x08,
  0x10,
  0x20,
  0x40,
  0x80
};

int main( void ){
    unsigned short a;
....
    a = 3;
    hoge(a);  // 共通化したい処理
....
    return 0;
}

void hoge( unsigned short a ){
    unsigned short x;
    x = ( IO.DR.BYTE & BIT_MSK[ a ] ) ? 1 : 0;
....
}