C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
180: Bitwise AND, OR, XOR, and NOT
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), andEXEC = 1(001 in binary). - Start with an unsigned integer variable
my_permissionsset to 0. - Use the OR operator to add
READandEXECpermissions. - Use the AND operator to check if the
WRITEpermission is set (it should not be). - Use the XOR operator to toggle the
EXECpermission (turning it off). - Use the AND operator again to verify that only
READremains.
Print the value of my_permissions at each step to prove the bits are changing as expected.
There are no comments for now.