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
206: Fixed-Width Integer Types with stdint.h Revisited
Imagine you're managing a global shipping operation. You tell your team to use "medium-sized boxes" for all shipments. In your local warehouse in New York, a "medium box" is 12 inches wide. But your partner warehouse in Tokyo interprets "medium" as 30 centimeters. When the boxes arrive, nothing fits the shelving units, and the whole system crashes because of a slight discrepancy in what "medium" actually means.
In C, the standard int or long types are those "medium boxes." On some systems, an int is 16 bits; on others, it's 32. If you're just writing a script to calculate your grocery bill, it doesn't matter. But the moment you start writing software that talks to hardware, reads a binary file, or sends data over a network, that ambiguity becomes a nightmare. That's why we use stdint.h.
The Cost of Ambiguity
I once worked on a project where we were reading a binary log file generated by an embedded sensor. The sensor developer told us the timestamp was a "long." We wrote the parser on a 64-bit Linux machine where long is 8 bytes. The sensor, however, was an ARM Cortex-M, where long was 4 bytes. We spent two days chasing a "ghost bug" where the timestamps looked like random gibberish, only to realize we were reading 8 bytes for every 4 bytes the sensor actually wrote.
When you use stdint.h, you stop guessing. You stop relying on the compiler's mood or the CPU architecture. You specify exactly how many bits you need.
Locking Down Your Bit-Width
Instead of the vague types you've used so far, you'll want to reach for these specific aliases. The naming convention is simple: [signed/unsigned][bit-width]_t.
#include <stdint.h>
uint8_t small_unsigned = 255; // Exactly 8 bits (0 to 255)
int8_t small_signed = -128; // Exactly 8 bits (-128 to 127)
uint32_t large_unsigned = 4000; // Exactly 32 bits
int64_t massive_signed = -1000000000000LL; // Exactly 64 bits
Notice the _t suffix? That just tells anyone reading your code, "This is a type definition." It's a convention that keeps your custom types and standard types distinct.
Mapping to a Real-World Protocol
Let's look at how this actually looks when you're defining a data structure for something like a network packet. If you're building a packet header, you can't afford a single bit of drift between the sender and the receiver.
#include <stdint.h>
struct PacketHeader {
uint8_t version; // 1 byte: Protocol version
uint8_t payload_type; // 1 byte: Type of data being sent
uint16_t payload_len; // 2 bytes: Length of the following data
uint32_t sequence_num; // 4 bytes: Packet order ID
};
If I had used int for all of those, the PacketHeader size would change depending on whether I compiled it for an Arduino, a Raspberry Pi, or a high-end gaming PC. By using uint8_t and uint32_t, I've guaranteed that this struct is exactly 8 bytes wide, regardless of the platform. It's the "ISO Standard Container" of the coding world.
When to Stick with Standard Ints
You might be wondering: "Should I just use int32_t for everything now?" Not necessarily. I still use int for simple loop counters (like for (int i = 0; i < 10; i++)) or when the exact size truly doesn't matter. The compiler is often very good at optimizing the native int for the specific CPU you're targeting. Save the fixed-width types for data structures, file I/O, and hardware interfaces.
📋 Practical Task
Exercise: Implementing a Binary Telemetry Parser
You are writing a driver for a satellite telemetry system. The satellite sends a fixed-width binary packet every second. Your job is to create a structure that maps exactly to this packet and write a function to print the values. If you use the wrong types, the data will shift and the readings will be incorrect.
The Packet Specification:
- Satellite ID: 1 byte (unsigned)
- Battery Voltage: 2 bytes (unsigned, represents millivolts)
- Internal Temperature: 2 bytes (signed, represents centi-degrees Celsius)
- Uptime Seconds: 4 bytes (unsigned)
Requirements:
- Define a
struct TelemetryPacketusing the correctstdint.htypes to match the specification. - Write a function
void print_telemetry(struct TelemetryPacket *p)that prints these values to the console. - In your
mainfunction, initialize a packet with sample data and pass it to your print function.
There are no comments for now.