To clear specific bits within a memory area you can use the bitwise And operator, &
, along with a mask. To create the mask, rather than thinking of which bits you want to clear, you should instead think of which bits you want to keep and create the mask using those bits.
The mask should be the same type of variable as the type of variable stored in the memory area. So if you are wanting to clear bits in a long
variable then you would create a mask in another long
variable which indicates the bits you want to keep.
Assume you have a variable of type unsigned long
which contains the bit pattern 00000000 00000000 00101100 11000100
and you want to clear the bits in the second byte from the right, the eight bits of 00101100
while keeping the other bits.
First you create a mask that has the bits you want to keep as in 11111111 11111111 00000000 11111111
or 0xffff00ff
. If you want to clear all other bits except for the least significant byte then you could use 0xff
for your mask.
Next you use the bitwise And operator &
to and the variable with the mask. Many times you would use the &=
operator with the variable containing the value with bits to clear on one side of the operator and the mask indicating which bits to keep on the other.
unsigned long x1 = 0x2cc4; // variable to mask.
unsigned long mask = 0xffff00ff; // mask to zero all bits in the second byte from the right
x1 &= mask; // keep only the bits we want to keep
Or you could hard code the mask as in:
unsigned long x1 = 0x2cc4;
x1 &= 0xffff00ff; // keep only the bits we want to keep
Or if you don't want to modify the original variable something like:
unsigned long x1 = 0x2cc4; // variable to mask.
unsigned long x2 = 0; // variable for new, masked value of x1
unsigned long mask = 0xffff00ff; // mask to zero all bits in the second byte from the right
x2 = x1 & mask; // keep only the bits we want to keep and put into a new variable
The bitwise And operator does an And operation on each bit and the resulting output bit for each bit is calculated using an And truth table. 0
And either 0
or 1
results in a 0
and only if both bits are 1
will a 1
result.
0 | 1
-----------
0 | 0 | 0
1 | 0 | 1
As a side note you can also set the bits you want to clear in the mask and then use the bitwise Not operator, ~
, and turn the mask from a mask of bits you want to clear to a mask of bits you want to keep. However this is not the way that it is usually done so may be confusing to other programmers reading the code.
unsigned long x1 = 0x2cc4;
unsigned long mask = ~0xff00; // mask to zero all bits in the second byte from the right
x1 &= mask; // And to keep only the bits we want to keep
Note also that the C Standard is a bit loose about how many bits are actually included in the most commonly used variable types such as int
, short
, long
, etc. There are guarantees about the minimum size but not necessarily the maximum size. This was done in order to provide backward compatibility as well as to allow portability of source code and flexibility for compilers targeting specific hardware platforms. However it means that if you depend on the implementation of int
as 64 bit in source code that is then moved to 32 bit hardware you may be surprised by the behavior.
The header file stdint.h
specifies a number of exact width integer types such as int8_t
and others. See stdint.h — Integer types however not all compiler vendors will provide support though most up to date compilers do.
As an aside, using hard coded numeric values such as 0xff
for a bit mask that is intended to be used in several places and has a specific meaning (e.g. buffer size field) is generally frowned upon. The normal practice is to use a #define
macro with a descriptive label, usually within an include file if the constant is needed in multiple source files, to name the bit mask in C programs. Creating a defined constant allows there to be a single point of definition for the bit mask constant thereby providing consistency of use, it will be right everywhere or wrong everywhere. Doing this also provides a unique, searchable identifier for the bit mask constant should you need to find where it is being used. In C++ a const
variable such as const unsigned long BitMaskA = 0x00ff;
is normally used instead of a #define
as such a variable defaults to internal linkage in C++, though it defaults to external linkage in C, and not depending on the Preprocessor is encouraged for C++ (see https://stackoverflow.com/a/12043198/1466970)
Note as well that using bitfield variable types is a different mechanism than using the bitwise operators with masks. For instance see When to use bit-fields in C? as well as this answer which talks to the portability problem https://stackoverflow.com/a/54054552/1466970