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
8: Variables and Data Types
When you first start writing C, there is a strong temptation to treat every number as an int. It's the path of least resistance. You don't have to think about the size of the data or the sign of the number; you just declare it and move on. I've seen this in countless code reviews from developers moving from Python or JavaScript, where "number" is just a generic concept. In C, that mindset is a recipe for subtle, nightmarish bugs.
The "Just Use an Int" Trap
Let's say we're building a simple system to track player stats for a game. You might be tempted to write something like this:
int player_health = 100;
int player_gold = 500;
int player_experience = 1200;
int movement_speed = 5;
On the surface, this works perfectly. But here is where we run into trouble: int is signed. That means player_gold could technically be -500. If you have a bug in your shop logic that allows a player to spend more than they have, your gold variable will just happily slide into negative numbers. Even worse, if you're tracking something like player_experience in a long-running RPG, a standard 32-bit signed integer caps out at about 2.1 billion. While that sounds like a lot, in the world of "idle games" or high-score trackers, you'll hit that ceiling faster than you think. Once you hit that limit, the number wraps around to -2.1 billion. I can't tell you how many hours I've spent debugging "integer overflow" because someone just used a generic int for a counter that grew too large.
Right-Sizing Your Data
The professional way to handle this is to ask yourself: "What is the absolute nature of this value?" If a value can never be negative, use an unsigned type. If it's a small number that will never exceed 255, use a char (which is essentially an 8-bit integer). If you need precision, you have to choose between float and double.
Here is how I would actually write those player stats:
unsigned int player_gold = 500; // Gold can't be negative
unsigned long player_experience = 1200; // Give XP more room to grow
unsigned char player_health = 100; // Health is 0-100, 8 bits is plenty
float movement_speed = 5.5f; // Speed needs decimals for fine-tuning
By doing this, you're documenting your intent. When another engineer looks at unsigned int player_gold, they immediately know that negative gold is an illegal state. You're also being mindful of the memory layout. In a small program, the difference between a 1-byte char and a 4-byte int is negligible. But if you're creating an array of 10,000 NPCs, those bytes add up quickly. C gives you this control because it's designed to run on everything from a toaster to a supercomputer; using the right type is how you keep your program lean.
The Precision Trade-off
Then we have the floating-point dilemma. You'll see float and double. I usually lean toward double by default because it has double the precision (64-bit vs 32-bit). However, in game development or embedded systems, float is often preferred because it's faster for the hardware to process and takes up half the space.
The catch is that floating-point numbers are approximations. Never, and I mean never, use a float or double for currency. If you use a float for player_gold, you'll eventually run into a situation where 0.1 + 0.2 equals 0.30000001. That's a great way to lose a customer or break a game economy. For money, stick to integers—represent your gold in cents or smallest possible units instead of using decimals.
📋 Practical Task
Refactoring the Inventory Memory Layout
You have been handed a piece of legacy code for an inventory system. The original developer used int for everything, which is wasting memory and allowing for impossible values (like negative item counts).
Your Task: Rewrite the following variable declarations using the most appropriate C data types based on the requirements provided in the comments. Use unsigned types where values cannot be negative, and choose the smallest integer type that safely fits the described range.
// TODO: Refactor these variables
int item_count = 50; // Range: 0 to 255
int total_weight = 1200; // Range: 0 to 65,535
int item_id = 1000000; // Range: 0 to 4 billion
int item_price = 19; // Range: 0 to 2 billion
int durability_percentage = 85; // Range: 0 to 100
float item_volume = 1.25f; // Needs precision, but memory is tight (32-bit is fine)
Submit your refactored declarations. Think about whether unsigned char, unsigned short, unsigned int, or unsigned long is the best fit for each.
There are no comments for now.