当前位置:嗨网首页>书籍在线阅读

20-位字段示例

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

15.4.1 位字段示例

通常,把位字段作为一种更紧凑存储数据的方式。例如,假设要在屏幕上表示一个方框的属性。为简化问题,我们假设方框具有如下属性:

  • 方框是透明的或不透明的;
  • 方框的填充色选自以下调色板:黑色、红色、绿色、黄色、蓝色、紫色、青色或白色;
  • 边框可见或隐藏;
  • 边框颜色与填充色使用相同的调色板;
  • 边框可以使用实线、点线或虚线样式。

可以使用单独的变量或全长(full-sized)结构成员来表示每个属性,但是这样做有些浪费位。例如,只需 1 位即可表示方框是透明还是不透明;只需1位即可表示边框是显示还是隐藏。8种颜色可以用3位单元的8个可能的值来表示,而3种边框样式也只需2位单元即可表示。总共10位就足够表示方框的5个属性设置。

一种方案是:一个字节存储方框内部(透明和填充色)的属性,一个字节存储方框边框的属性,每个字节中的空隙用未命名字段填充。 struct box_props 声明如下:

struct box_props {
     bool opaque                : 1 ;
     unsigned int fill_color    : 3 ;
     unsigned int               : 4 ;
     bool show_border           : 1 ;
     unsigned int border_color  : 3 ;
     unsigned int border_style  : 2 ;
     unsigned int               : 2 ;
};

加上未命名的字段,该结构共占用16位。如果不使用填充,该结构占用10位。但是要记住,C以 unsigned int 作为位字段结构的基本布局单元。因此,即使一个结构唯一的成员是1位字段,该结构的大小也是一个 unsigned int 类型的大小, unsigned int 在我们的系统中是32位。另外,以上代码假设C99新增的 _Bool 类型可用,在 stdbool.h 中, bool_Bool 的别名。

对于 opaque 成员,1表示方框不透明,0表示透明。 show_border 成员也用类似的方法。对于颜色,可以用简单的RGB(即red-green-blue的缩写)表示。这些颜色都是三原色的混合。显示器通过混合红、绿、蓝像素来产生不同的颜色。在早期的计算机色彩中,每个像素都可以打开或关闭,所以可以使用用1位来表示三原色中每个二进制颜色的亮度。常用的顺序是,左侧位表示蓝色亮度、中间位表示绿色亮度、右侧位表示红色亮度。表15.3列出了这8种可能的组合。 fill_color 成员和 border_color 成员可以使用这些组合。最后, border_style 成员可以使用 012 来表示实线、点线和虚线样式。

表15.3 简单的颜色表示

| 位组合 | 十进制 | 颜色 | | :----- | :----- | :----- | :----- | :----- | | 000 | 0 | 黑色 | | 001 | 1 | 红色 | | 010 | 2 | 绿色 | | 011 | 3 | 黄色 | | 100 | 4 | 蓝色 | | 101 | 5 | 紫色 | | 110 | 6 | 青色 | | 111 | 7 | 白色 |

程序清单15.3中的程序使用 box_props 结构,该程序用 #define 创建供结构成员使用的符号常量。注意,只打开一位即可表示三原色之一。其他颜色用三原色的组合来表示。例如,紫色由打开的蓝色位和红色位组成,所以,紫色可表示为 BLUE|RED

程序清单15.3  fields.c 程序

/* fields.c -- 定义并使用字段 */
#include <stdio.h>
#include <stdbool.h>   // C99定义了bool、true、false
/* 线的样式 */
#define SOLID   0
#define DOTTED  1
#define DASHED  2
/* 三原色 */
#define BLUE    4
#define GREEN   2
#define RED     1
/* 混合色 */
#define BLACK   0
#define YELLOW  (RED | GREEN)
#define MAGENTA (RED | BLUE)
#define CYAN    (GREEN | BLUE)
#define WHITE   (RED | GREEN | BLUE)
const char * colors[8] = { "black", "red", "green", "yellow",
"blue", "magenta", "cyan", "white" };
struct box_props {
     bool opaque : 1;        // 或者 unsigned int (C99以前)
     unsigned int fill_color : 3;
     unsigned int : 4;
     bool show_border : 1;    // 或者 unsigned int (C99以前)
     unsigned int border_color : 3;
     unsigned int border_style : 2;
     unsigned int : 2;
};
void show_settings(const struct box_props * pb);
int main(void)
{
     /* 创建并初始化 box_props 结构 */
     struct box_props box = { true, YELLOW, true, GREEN, DASHED };
     printf("Original box settings:\n");
     show_settings(&box);
     box.opaque = false;
     box.fill_color = WHITE;
     box.border_color = MAGENTA;
     box.border_style = SOLID;
     printf("\nModified box settings:\n");
     show_settings(&box);
     return 0;
}
void show_settings(const struct box_props * pb)
{
     printf("Box is %s.\n",
          pb->opaque == true ? "opaque" : "transparent");
     printf("The fill color is %s.\n", colors[pb->fill_color]);
     printf("Border %s.\n",
          pb->show_border == true ? "shown" : "not shown");
     printf("The border color is %s.\n", colors[pb->border_color]);
     printf("The border style is ");
     switch (pb->border_style)
     {
     case SOLID:  printf("solid.\n"); break;
     case DOTTED: printf("dotted.\n"); break;
     case DASHED: printf("dashed.\n"); break;
     default:      printf("unknown type.\n");
     }
}

下面是该程序的输出:

Original box settings:
Box is opaque.
The fill color is yellow.
Border shown.
The border color is green.
The border style is dashed.
Modified box settings:
Box is transparent.
The fill color is white.
Border shown.
The border color is magenta.
The border style is solid.

该程序要注意几个要点。首先,初始化位字段结构与初始化普通结构的语法相同:

struct box_props box = {YES, YELLOW , YES, GREEN, DASHED};

类似地,也可以给位字段成员赋值:

box.fill_color = WHITE;

另外, switch 语句中也可以使用位字段成员,甚至还可以把位字段成员用作数组的下标:

printf("The fill color is %s.\n", colors[pb->fill_color]);

注意,根据 colors 数组的定义,每个索引对应一个表示颜色的字符串,而每种颜色都把索引值作为该颜色的数值。例如,索引1对应字符串 "red" ,枚举常量 red 的值是 1