转:http://www.linuxforu.com/2012/01/joy-of-programming-understanding-bit-fields-c/

By S.G. Ganesh on January 30, 2012 in CodingColumns · 2 Comments

One important feature that distinguishes C as a systems programming language is its support for bit-fields. Let us explore this feature in this column.

In C, structure members can be specified with size in number of bits, and this feature is known as bit-fields. Bit-fields are important for low-level (i.e., for systems programming) tasks such as directly accessing systems resources, processing, reading and writing in terms of streams of bits (such as processing packets in network programming), cryptography (encoding or decoding data with complex bit-manipulation), etc.

Consider the example of reading the components of a floating-point number. A 4-byte floating-point number in the IEEE 754 standard consists of the following:

  • The first bit is reserved for the sign bit — it is 1 if the number is negative and 0 if it is positive.
  • The next 8 bits are used to store the exponent in the unsigned form. When treated as a signed exponent, this exponent value ranges from -127 to +128. When treated as an unsigned value, its value ranges from 0 to 255.
  • The remaining 23 bits are used to store the mantissa.

Here is a program to print the value of a floating-point number into its constituents:

struct FP {
// the order of the members depends on the
// endian scheme of the underlying machine
      unsigned int mantissa : 23;
     unsigned int exponent : 8;
      unsigned int sign : 1;
} *fp;
 
int main() {
       float f = -1.0f;
       fp = (struct FP *)&f;
 
printf(" sign = %s, biased exponent = %u,
mantissa = %u ", fp->sign ? "negative" : "positive",
fp->exponent, fp->mantissa);
}

For the floating-point number -1.0, this program prints:

sign = negative, biased exponent = 127, mantissa = 0

Since the sign of the floating-point number is negative, the value of the sign bit is 1. Since the exponent is actual 0, in unsigned exponent format, it is represented as 127, and hence that value is printed. The mantissa in this case is 0, and hence it is printed as it is.

To understand how floating-point arithmetic works, see this Wikipedia article.

An alternative to using bit-fields is to use integers directly, and manipulate them using bitwise operators (such as &|~, etc.). In the case of reading the components of a floating-point number, we could use bitwise operations also. However, in many cases, such manipulation is a round-about way to achieve what we need, and the solution using bit-fields provides a more direct solution and hence is a useful feature.

There are numerous limitations in using bit-fields. For example, you cannot apply operators such as & (addressof)sizeof to bit-fields. This is because these operators operate in terms of bytes (not bits) and the bit-fields operate in terms of bits (not bytes), so you cannot use these operators. In other words, an expression such as sizeof(fp->sign) will result in a compiler error.

Another reason is that the underlying machine supports addressing in terms of bytes, and not bits, and hence such operators are not feasible. Then how does it work when expressions such as fp->sign, or fp->exponent are used in this program?

Note that C allows only integral types as bit-fields, and hence expressions referring to the bit-fields are converted to integers. In this program, as you can observe, we used the %u format specifier, which is for an unsigned integer — the bit-field value was converted into an integer and that is why the program worked.

Those new to bit-fields face numerous surprises when they try using them. This is because a lot of low-level details come into the picture while using them. In the programming example for bit-fields, you might have noticed the reversal in the order of the sign, exponent and mantissa, which is because of the underlying endian scheme followed. Endian refers to how bytes are stored in memory (see this Wikipedia article for more details).

Can you explain the following simple program that makes use of a bit-field?

struct bitfield {
    int bit : 1;
} BIT;
int main() {
   BIT.bit = 1;
   printf(" sizeof BIT is = %d\n", sizeof(BIT));
   printf(" value of bit is = %d ", BIT.bit);
 
}

It prints:

 sizeof BIT is = 4
 value of bit is = -1

Why? Note that it is not a compiler error to attempt to find the sizeof(BIT) because it is a structure; had we attempted sizeof(BIT.bit), that will not compile.

Now, coming to the output, if we had used only one bit in the BIT structure, why is thesizeof(BIT) 4 bytes? It is because of the addressing requirement of the underlying machine. The machine might perhaps require all structs to start in an address divisible by 4; or perhaps, allocating the size of a WORD for the structure is more efficient even if the underlying machine may require that structs start at an even address. Also, the compiler is free to add extra bits between any struct members (including bit-field members), which is known as “padding”.

Now let us come to the next output. We set BIT.bit = 1; and the printf statement printed -1! Why was that?

Note that we declared bit as int bit : 1; where the compiler treated the bit to be a signed integer of one bit size. Now, what is the range of a 1-bit signed integer?

It is from 0 to -1 (not 0 and 1, which is a common mistake). Remember the formula for finding out the range of signed integers: 2(n-1) to 2(n-1)-1 where N is the number of bits. For example, if N is 8 (number of bits in a byte), i.e., the range of a signed integer of size 8 is -2(8-1) to 2(8-1)-1, which is -128 to +127. Now, when N is 1, i.e., the range of a signed integer of size 1, it is -2(1-1)to 2(1-1)-1, which is -1 to 0!

No doubt, bit-fields are a powerful feature for low-level bit-manipulation. The cost of using bit-fields is the loss of portability. We already saw how padding and ending issues can affect portability in our simple program for reading the components of a floating-point number. Bit-fields should be used in places where space is very limited, and when functionality is demanding. Also, the gain in space could be lost in efficiency: bit-fields take more time to process, since the compiler takes care of (and hides) the underlying complexity in bit-manipulation to get/set the required data. Bugs associated with bit-fields can be notoriously hard to debug, since we need to understand data in terms of bits. So, use bit-fields sparingly and with care.

Feature image courtesy: Dean Terry. Reused under the terms of CC-BY-NC-ND 2.0 License.

Joy of Programming: Understanding Bit-fields in C的更多相关文章

  1. Core Java Volume I — 4.4. Static Fields and Methods

    4.4. Static Fields and MethodsIn all sample programs that you have seen, the main method is tagged w ...

  2. Questions that are independent of programming language. These questions are typically more abstract than other categories.

    Questions that are independent of programming language.  These questions are typically more abstract ...

  3. (转)Awesome Courses

    Awesome Courses  Introduction There is a lot of hidden treasure lying within university pages scatte ...

  4. Async/Await FAQ

    From time to time, I receive questions from developers which highlight either a need for more inform ...

  5. .NET并行编程1 - 并行模式

    设计模式——.net并行编程,清华大学出版的中译本. 相关资源地址主页面: http://parallelpatterns.codeplex.com/ 代码下载: http://parallelpat ...

  6. Lock-Free 编程

    文章索引 Lock-Free 编程是什么? Lock-Free 编程技术 读改写原子操作(Atomic Read-Modify-Write Operations) Compare-And-Swap 循 ...

  7. <转载>国外程序员推荐的免费编程书籍资源

    一.George Stocker 提供了一大串,分类如下: How to Design Programs: An Introduction to Computing and Programming 2 ...

  8. MySQL Crash Course #05# Chapter 9. 10. 11. 12 正则.函数. API

    索引 正则表达式:MySQL only supports a small subset of what is supported in most regular expression implemen ...

  9. The history of programming languages.(transshipment) + Personal understanding and prediction

    To finish this week's homework that introduce the history of programming languages , I surf the inte ...

随机推荐

  1. 自己封装的Socket组件,实现服务端多进程共享Socket对象,协同处理客户端请求

    DotNet.Net.MySocket是SLB.NET(Server Load Balance服务器负载均衡)项目中的核心组件. 在实际的项目中发现,单进程的服务端处理高并发的客户请求能力有限. 所以 ...

  2. NovaMind *的安装、和谐破解到永久使用

    XMind *思维导图的安装步 同类型的软件,这两款软件: XMind 和 NovaMind,各有所长.建议,都安装,合适的时候方便使用. XMind界面如下: NovaMind界面如下: 本博文,主 ...

  3. 常见的mongo shell命令

    启动mongo shell 在windows下,双击mongo.exe可以启动mongo shell 查询库.表及选择库 查询所有库命令: show dbs 应用某一个db use jxs_datab ...

  4. quartz中关键类

    job job是一个接口,你对某一类job的定义,可以通过实现该接口来实现.例如为销售报告定义一个SalesReportJob,包含变量name. job可以使用的几个注解 @DisallowConc ...

  5. [原创]mac终端前面的计算机名怎么改??

    1.修改-之前的名称 mac环境,系统 OS X Yisemite,打开终端, 执行下面命令“Tmp”是你想要改的电脑名称 sudo scutil --set HostName Tmp 执行前,执行后 ...

  6. AJAX的工作原理及其优缺点

    1.什么是AJAX?AJAX全称为“Asynchronous JavaScript and XML”(异步JavaScript和XML),是一种创建交互式网页应用的网页开发技术.它使用:使用XHTML ...

  7. Android_UI_点击按钮切换背景效果实现

    实现按钮按下和释放,按钮背景图片相应切换效果的方法这里介绍两种,一种是在代码里实现,另一种是在xml文件里实现 一.在xml文件里 首先现在layout的一个xml文件下定义Button如下所示: [ ...

  8. Castle IOC容器构建配置详解(二)

    主要内容 1.基本类型配置 2.Array类型配置 3.List类型配置 4.Dictionary类型配置 5.自定义类型转换 一.基本类型配置 在Castle IOC的配置文件中,大家可能都已经注意 ...

  9. 未能加载文件或程序集"System.Data,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"或它的某一个依赖项。系统找不到指定的文件。

    sqlserver 2005打开出现无法正常访问数据,提示信息: 未能加载文件或程序集"System.Data,Version=2.0.0.0,Culture=neutral,PublicK ...

  10. javascript 汉字生成拼音

    在网上下载的一个汉字生成拼音的js,很有用,大家一起分享! var PinYin = {"a":"/u554a/u963f/u9515","ai&qu ...