Skip to Content
Course content

180: Bitwise AND, OR, XOR, and NOT

Click on the "Edit" button in the top corner of the screen to edit your slide content.

One of the biggest hurdles I see when people first hit bitwise operators in C is the assumption that & and &&—or | and ||—are just shorthand versions of each other. It seems logical, right? They both mean "AND" in a general sense. But in C, they operate on entirely different levels of reality.

Logical AND is not Bitwise AND

If you use a logical operator when you meant a bitwise one, your code might compile perfectly, but it will behave in ways that will make you want to pull your hair out. Let me show you exactly why. Imagine we're checking if the lowest bit of an integer is set (which is a common way to check if a number is odd).

int x = 2; // Binary: 0010
if (x & 1) { 
    printf("Odd\n"); 
} else { 
    printf("Even\n"); 
}

if (x && 1) { 
    printf("This prints anyway\n"); 
}

In the first block, x & 1 compares 0010 and 0001. No bits match, so the result is 0. The program correctly prints "Even". But in the second block, x && 1 treats x as a boolean. Since 2 is non-zero, it's "True". Since 1 is non-zero, it's also "True". True AND True is True. The result is 1, and that second block executes regardless of whether the number is odd or even.

Using AND to Mask and OR to Set

When we move into bitwise territory, we stop thinking about "true or false" and start thinking about "which switches are on." I like to think of the Bitwise AND (&) as a mask. If you want to isolate a specific bit and ignore everything else, you "mask" it. If you have a status byte where the 3rd bit represents "Error State," you'd use status & (1 << 2) to ignore every other bit in that byte.

On the flip side, Bitwise OR (|) is how we turn things on. If you want to ensure a specific bit is set to 1 without messing with the other bits, you OR it with a value. For example, if you're writing a driver and need to set the "Read Only" flag on a register, you'd use register |= READ_ONLY_MASK;. It doesn't matter if the bit was already 1 or 0; after an OR operation with 1, it's guaranteed to be 1.

XOR for Toggling and the Tilde's Traps

The Bitwise XOR (^) is a bit more exotic. It returns 1 only if the bits are different. In the real world, we mostly use this for toggling. If you XOR a bit with 1, it flips: 0 becomes 1, and 1 becomes 0. It's incredibly efficient for things like blinking an LED in an embedded system.

Then there's the Bitwise NOT (~). This one is a trap for the unwary because of how C handles signed integers (Two's Complement). You might expect ~0 to be some small number, but if you're using a signed 32-bit integer, ~0 becomes -1. Why? Because the NOT operator flips every single bit, including the sign bit at the very front. I highly recommend using unsigned int when doing bitwise manipulation to avoid these "phantom" negative numbers popping up in your debug logs.




📋 Practical Task

Building a File Permission Bitmask System

In early Unix-like systems, permissions are handled using bitmasks (Read, Write, Execute). Your task is to create a small program that manages these permissions using bitwise operators.

Requirements:

  • Define three constants: READ = 4 (100 in binary), WRITE = 2 (010 in binary), and EXEC = 1 (001 in binary).
  • Start with an unsigned integer variable my_permissions set to 0.
  • Use the OR operator to add READ and EXEC permissions.
  • Use the AND operator to check if the WRITE permission is set (it should not be).
  • Use the XOR operator to toggle the EXEC permission (turning it off).
  • Use the AND operator again to verify that only READ remains.

Print the value of my_permissions at each step to prove the bits are changing as expected.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.