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
9: Integer Types and Overflow
In most modern languages, you don't really think about how much space a number takes up. You just create an integer and let the language handle the rest. C isn't like that. In C, you're the one managing the memory, and if you pick a type that's too small for your data, C won't stop you—it'll just wrap your number around in a way that can crash your program or create some truly bizarre bugs.
Let's build a simple high-score tracker for a game to see this in action. I want to keep it lightweight, so I'll start with the smallest integer type I can think of.
Starting with a lean score counter
I'm going to start by using a signed char for the score. Since a char is typically 8 bits, it feels efficient for a simple prototype. I'll write a loop that simulates a player scoring points rapidly.
#include <stdio.h>
int main() {
signed char score = 120;
printf("Starting score: %d\n", score);
for (int i = 0; i < 10; i++) {
score += 10;
printf("Added 10 points! Current score: %d\n", score);
}
return 0;
}
At first glance, this looks fine. I'm starting at 120 and adding 10 points ten times. I expect to end up at 220. But if you run this, you'll see something weird happen around the fourth or fifth iteration.
Watching the score go negative
Wait, why did my score suddenly jump from 127 to -128? This is the "overflow" I mentioned. A signed char has a range of -128 to 127. The moment I added 10 to 120, I hit 130, which is outside that range. In C, when a signed integer overflows, it wraps around to the lowest possible value for that type. It's like a clock; once you hit 12, you go back to 1.
This is a classic mistake. I was trying to be "efficient" by using a char, but I didn't actually consider the maximum possible value my variable would need to hold. In a real game, your player would be very upset to see their high score suddenly become a massive negative number.
Switching to predictable types with stdint.h
I could just use int, but the size of an int actually changes depending on the architecture you're compiling for (it might be 16 bits on an Arduino but 32 bits on your laptop). As a professional, I don't like that ambiguity. I want to know exactly how many bits I'm using.
That's why I'm bringing in <stdint.h>. This header gives us types like int32_t (exactly 32 bits) and uint64_t (an unsigned 64-bit integer). Since a game score should never be negative, I'll use an unsigned int. Unsigned types can hold larger positive numbers because they don't need to reserve a bit to track whether the number is positive or negative.
#include <stdio.h>
#include <stdint.h>
int main() {
// uint32_t is an unsigned 32-bit integer.
// Range: 0 to 4,294,967,295. Plenty for our game.
uint32_t score = 120;
printf("Starting score: %u\n", score);
for (int i = 0; i < 10; i++) {
score += 10;
printf("Added 10 points! Current score: %u\n", score);
}
return 0;
}
Notice I changed the printf specifier to %u. That tells C we are printing an unsigned integer. If you use %d with an unsigned type, you might get the right answer for small numbers, but you'll get garbage output once the number gets large enough to flip the sign bit. Be precise with your types and your format specifiers, or C will bite you.
📋 Practical Task
The Galactic Distance Overflow Fixer
You are maintaining a space navigation system. The current code uses a signed short to track the distance in kilometers from a space station. However, ships are now traveling much further than the original developers anticipated, and the distance is "wrapping around" to negative values, causing the navigation system to crash.
Your Task: Modify the provided code to ensure the distance can handle values up to 1,000,000 kilometers without overflowing. You must use a specific type from <stdint.h> and update the printf format specifier to match.
#include <stdio.h>
int main() {
// BUG: This type is too small for galactic distances!
signed short distance = 30000;
printf("Current Distance: %d km\n", distance);
// Simulate traveling further
distance += 5000;
printf("New Distance: %d km\n", distance);
return 0;
}
There are no comments for now.