The document discusses C programming concepts including bit fields, enumerated data types, and unions. It provides examples of using bit fields to pack boolean variables into a structure to save memory. Enumerated types allow using symbolic names for integer values. Unions allocate space for the largest member, and all members share the same memory location, allowing different types to overlay the same space.
2. C - Bit Fields
Suppose your C program contains a number of TRUE/FALSE variables grouped in a
structure called status, as follows −
struct {
unsigned int widthValidated;
unsigned int heightValidated;
} status;
This structure requires 8 bytes of memory space but in actual, we are going to store
either 0 or 1 in each of the variables. The C programming language offers a better
way to utilize the memory space in such situations.
If you are using such variables inside a structure then you can define the width of a
variable which tells the C compiler that you are going to use only those number of
bytes. For example, the above structure can be re-written as follows −
struct {
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status;
The above structure requires 4 bytes of memory space for status variable, but only 2
bits will be used to store the values.
If you will use up to 32 variables each one with a width of 1 bit, then also the status
structure will use 4 bytes. However as soon as you have 33 variables, it will allocate
the next slot of the memory and it will start using 8 bytes. Let us check the following
example to understand the concept −
#include <stdio.h>
#include <string.h>
/* define simple structure */
struct {
unsigned int widthValidated;
unsigned int heightValidated;
} status1;
/* define a structure with bit fields */
struct {
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2;
int main( ) {
printf( "Memory size occupied by status1 : %dn",
sizeof(status1));
printf( "Memory size occupied by status2 : %dn",
sizeof(status2));
return 0;
}
3. When the above code is compiled and executed, it produces the following result −
Memory size occupied by status1 : 8
Memory size occupied by status2 : 4
Bit Field Declaration
The declaration of a bit-field has the following form inside a structure −
struct {
type [member_name] : width ;
};
The following table describes the variable elements of a bit field −
Sr.No. Element & Description
1
type
An integer type that determines how a bit-field's value is interpreted. The type may be int, signed
int, or unsigned int.
2
member_name
The name of the bit-field.
3
width
The number of bits in the bit-field. The width must be less than or equal to the bit width of the
specified type.
The variables defined with a predefined width are called bit fields. A bit field can hold
more than a single bit; for example, if you need a variable to store a value from 0 to
7, then you can define a bit field with a width of 3 bits as follows −
struct {
unsigned int age : 3;
} Age;
The above structure definition instructs the C compiler that the age variable is going
to use only 3 bits to store the value. If you try to use more than 3 bits, then it will not
allow you to do so. Let us try the following example −
#include <stdio.h>
#include <string.h>
struct {
unsigned int age : 3;
} Age;
4. int main( ) {
Age.age = 4;
printf( "Sizeof( Age ) : %dn", sizeof(Age) );
printf( "Age.age : %dn", Age.age );
Age.age = 7;
printf( "Age.age : %dn", Age.age );
Age.age = 8;
printf( "Age.age : %dn", Age.age );
return 0;
}
When the above code is compiled it will compile with a warning and when executed,
it produces the following result −
Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0
Summary of Struct bit fields
On occasion it is desired to hold a number of small integer items in a structure.
To save space, the fields within a structure are not required to occupy a full
word. Instead, they can occupy a specified number of bits.
Multiple consecutive bit fields in a structure will share a single word of
memory, insofar as each field fits completely. This reduces storage
requirements, at the expense of slower execution.
If the next bit field does not fit in the currently unallocated portion of the
current word, then it will be put entirely into the next word. The remainder of
the current word will be wasted.
The size of a bit field is indicated by appending a colon and the number of
desired bits after the field name.
If a bit field size is specified as zero, it forces the next bit field to be placed on a
word boundary. These variables are more quickly accessed. The field name is
not required for zero length bit fields.
Structure bit fields must be of an integral type. Most implementations treat
them as unsigned.
Example: struct Packed_data {
unsigned int is_element:1; /* = 1 if element *
unsigned int atomic_number:8; /* Maximum 128 */
unsigned int is_reactant:1;
unsigned int is_product:1;
unsigned int is_catalyst:1;
unsigned int Stock_Index:16; /* Maximum 65,535 */
} chemical_inventory[ 10000 ];
5. Each data item in the above array takes up one 32-bit word ( with four bits
wasted ), for a total of 10,000 words of storage for the entire array, as
opposed to 60,000 words of storage if bitfields were not used.
6. Enumerated Data Types
Enumerated data types are a special form of integers, with the following
constraints:
o Only certain pre-determined values are allowed.
o Each valid value is assigned a name, which is then normally used
instead of integer values when working with this data type.
Enumerated types, variables, and typedefs, operate similarly to structs:
enum suits { CLUBS, HEARTS, SPADES, DIAMONDS, NOTRUMP } trump;
enum suits ew_bid, ns_bid;
typedef enum Direction{ NORTH, SOUTH, EAST, WEST } Direction;
Direction nextMove = NORTH;
Values may be assigned to specific enum value names.
o Any names without assigned values will get one higher than the
previous entry.
o If the first name does not have an assigned value, it gets the value of
zero.
o It is even legal to assign the same value to more than one name.
o Example:
enum Errors{ NONE=0, // Redundant. The first one would be
zero anyway
MINOR1=100, MINOR2, MINOR3, // 100, 101, and 102
MAJOR1=1000, MAJOR2, DIVIDE_BY_ZERO=1000 }; // 1000,
1001, and 1000 again.
Because enumerated data types are integers, they can be used anywhere
integers are allowed. One of the best places in in switch statements:
switch( nextMove ) {
case NORTH:
y++;
break;
// etc.
The compiler will allow the use of ordinary integers with enumerated
variables, e.g. trump = 2; , but it is bad practice.
7. Union
Unions are declared, created, and used exactly the same as
struts, EXCEPT for one key difference:
o Structs allocate enough space to store all of the fields in the struct.
The first one is stored at the beginning of the struct, the second is
stored after that, and so on.
o Unions only allocate enough space to store the largest field listed, and
all fields are stored at the same space - The beginnion of the union.
This means that all fields in a union share the same space, which can be used
for any listed field but not more than one of them.
In order to know which union field is actually stored, unions are often nested
inside of structs, with an enumerated type indicating what is actually stored
there. For example:
struct Flight {
enum { PASSENGER, CARGO } type;
union {
int npassengers;
double tonnages; // Units are not necessarily tons.
} cargo;
};
Struct Flight flights[ 1000 ];
flights[ 42 ].type = PASSENGER;
flights[ 42 ].cargo.npassengers = 150;
flights[ 20 ].type = CARGO;
flights[ 20 ].cargo.tonnages = 356.78;
The example above does not actually save any space, because the 4 bytes
saved by using a union instead of a struct for the cargo is lost by the int
needed for the enumerated type. However a lot of space could potentially be
saved if the items in the union were larger, such as nested structs or large
arrays.
Unions are sometimes also used to break up larger data items into smaller
pieces, such as this code to extract four 8-bit bytes from a 32-bit int:
int nRead;
union {
unsigned int n;
unsigned char c[ 4 ];
} data;
// ( Code to read in nRead, from the user or a file, has been
omitted in this example )
data.n = nRead;
for( int i = 0; i < 4; i++ )
printf( "Byte number %d of %ud is %udn", i, nRead, data.c[ i ]
);