Introduction to C Language | Lecture 4: Deep Understanding of Number Systems and Code Systems, Mastering the Secrets of Basic Data Types
💡 Foreword: Many beginners in C language feel confused about number system conversion and data storage, such as why -1 is stored as 11111111 in computers? Why can’t floating-point numbers be directly compared using ==? This article will explain these concepts in the simplest and most understandable way!
1. 🌟 Introduction: Starting from Counting in Daily Life
Imagine you have 10 apples, which is represented as “10” in decimal. But if you can only count using your fingers (5 fingers on each hand), you might say “both hands are full.” This is the concept of different “number systems”!
In the world of computers, since circuits can only recognize two states: “on” and “off,” computers are inherently “binary.” Understanding this is key to mastering C language!
2. 📊 Detailed Explanation of Number Systems
(1) What is a Number System?
A number system is simply a “method of counting.” We usually use decimal because humans have 10 fingers. But computers use binary because circuits have only two states.
Real-life examples:
Decimal: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11...
Binary: 0, 1, 10, 11, 100, 101, 110, 111, 1000...
(2) Concept of Bit Weight: Each Position Has Its Own “Weight”
This is the core concept for understanding number system conversion! Each digit position has a fixed weight.
Decimal Example:
Meaning of the number 632:
6 × 100 + 3 × 10 + 2 × 1 = 632
↑ ↑ ↑
Hundreds Tens Units
(10²) (10¹) (10⁰)
Binary Example:
Binary 1011 converted to decimal:
1×2³ + 0×2² + 1×2¹ + 1×2⁰
= 1×8 + 0×4 + 1×2 + 1×1
= 8 + 0 + 2 + 1 = 11
So: 1011₂ = 11₁₀
(3) Hexadecimal: A Programmer’s Best Friend
Why Learn Hexadecimal?
- • 1 hexadecimal digit = 4 binary digits (just right!)
- • More concise representation of binary numbers
- • Frequently used in C language (e.g., memory addresses)
Conversion Table (recommended to memorize):
Hexadecimal | Binary | Decimal
---------|--------|--------
0 | 0000 | 0
1 | 0001 | 1
2 | 0010 | 2
3 | 0011 | 3
4 | 0100 | 4
5 | 0101 | 5
6 | 0110 | 6
7 | 0111 | 7
8 | 1000 | 8
9 | 1001 | 9
A | 1010 | 10
B | 1011 | 11
C | 1100 | 12
D | 1101 | 13
E | 1110 | 14
F | 1111 | 15
Practical Conversion Tips:
Binary: 1010 1100
Split: 1010 1100
Convert: A C
Result: 0xAC (hexadecimal prefix 0x)
3. 🔧 Code System — How Computers Store Numbers
(1) Unsigned Integers: The Simplest Storage Method
An unsigned number is “only positive,” stored directly in binary.
// Storage range for 1-byte unsigned integer: 0-255
unsigned char num = 200;
// Stored in memory as: 11001000 (binary)
// Corresponding hexadecimal: 0xC8
Why is it 0-255?
- • 1 byte = 8 bits
- • 8 bits binary: 00000000 to 11111111
- • Decimal: 0 to 2⁸-1 = 255
(2) Signed Integers: The Challenge of Storing Negative Numbers
This is a common difficulty for many beginners! How does a computer store negative numbers?
Comparison of Three Representations:
Taking -6 as an example (using 4 bits for simplification):
- 1. Sign-Magnitude: Sign bit + Value bits
+6: 0110 -6: 1110 // The highest bit 1 indicates a negative sign - 2. One’s Complement: For negative numbers, the sign bit remains unchanged, and other bits are inverted
+6: 0110 -6: 1001 // 0110 inverted except for the sign bit - 3. Two’s Complement: One’s complement + 1 (used by computers)
+6: 0110 -6: 1010 // 1001 + 1 = 1010
(3) Why Use Two’s Complement?
Problem 1: Defects of Sign-Magnitude
+0: 0000
-0: 1000 // Two zeros appear!
Problem 2: Complexity of Addition Operations
6 + (-6) using sign-magnitude:
0110 + 1110 = 10100 ≠ 0
Advantages of Two’s Complement:
// Using two's complement for subtraction: 6 - 3 = 6 + (-3)
// 6: 0110
// -3: 1101 (two's complement of -3)
// Adding:
// 0110
// + 1101
// ------
// 10011 // The highest bit overflow is discarded, resulting in 0011=3 ✓
Memory Tips:
- • Positive numbers: Sign-magnitude = One’s complement = Two’s complement
- • Negative numbers: Two’s complement = One’s complement + 1
- • To find a negative number: Invert the bits of the positive number + 1
4. 💾 Detailed Explanation of Basic Data Types in C Language
(1) Integer Family
#include <stdio.h>
int main() {
// Basic integer type (usually 4 bytes)
int age = 25; // Signed integer: -2³¹ ~ 2³¹-1
unsigned int score = 95; // Unsigned integer: 0 ~ 2³²-1
// Short integer (2 bytes)
short height = 175; // -32768 ~ 32767
// Long integer (8 bytes)
long long money = 1000000000LL; // Very large number, note the LL suffix
printf("age=%d, score=%u\n", age, score); // %d signed, %u unsigned
printf("height=%hd, money=%lld\n", height, money); // %hd short, %lld long long
return 0;
}
Selection Suggestions:
- • General integers: use
<span>int</span> - • Need to save space: use
<span>short</span> - • Very large numbers: use
<span>long long</span> - • Ensure non-negative: use
<span>unsigned</span>
(2) Character Type: Actually Small Integers
#include <stdio.h>
int main() {
char letter = 'A'; // Store character A
char number = 65; // Store ASCII code 65
printf("Character form: %c\n", letter); // Output: A
printf("Numeric form: %d\n", letter); // Output: 65
printf("Character form: %c\n", number); // Output: A
printf("Numeric form: %d\n", number); // Output: 65
// Character operations
char next = letter + 1; // A + 1 = B
printf("Next character: %c\n", next); // Output: B
return 0;
}
Important Concepts:
- • Characters actually store ASCII code values
- •
<span>'A'</span>‘s ASCII code is 65,<span>'a'</span>is 97 - •
<span>'0'</span>‘s ASCII code is 48 (not the number 0!)
(3) Floating Point Type: The Art of Storing Decimals
① Introduction to IEEE 754 Standard
Computers store decimals using scientific notation:
6.75 → 110.11₂ → 1.1011₂ × 2²
② float (4 bytes) storage format:
| Sign Bit | Exponent (8 bits) | Mantissa (23 bits) |
| 0 | 10000001 | 10110000... |
③ Floating Point Precision Issues
Why does 0.1 + 0.2 ≠ 0.3?
#include <stdio.h>
#include <math.h> // Requires fabs function
int main() {
float a = 0.1f;
float b = 0.2f;
float c = a + b;
printf("a+b = %.10f\n", c); // Output: 0.3000000119
printf("0.3 = %.10f\n", 0.3f); // Output: 0.3000000000
// Incorrect comparison method
if (c == 0.3f) {
printf("Equal\n");
} else {
printf("Not equal!\n"); // This will execute
}
// Correct comparison method
float epsilon = 1e-6f; // Error range
if (fabs(c - 0.3f) < epsilon) {
printf("Equal within error range\n");
}
return 0;
}
Reason Analysis: The decimal 0.1 is a repeating decimal in binary:
0.1₁₀ = 0.000110011001...₂ (repeating)
Computers can only store a limited number of bits, leading to errors.
5. ⚡ Type Conversion and Common Pitfalls
(1) Automatic Type Conversion
#include <stdio.h>
int main() {
int a = 5;
float b = 2.5f;
// Automatic conversion: int → float
float result = a + b; // 5 is automatically converted to 5.0f
printf("result = %.1f\n", result); // Output: 7.5
// Conversion order: char → int → float → double
char ch = 'A';
int num = ch; // char is automatically converted to int
printf("ASCII: %d\n", num); // Output: 65
return 0;
}
(2) Explicit Type Conversion
#include <stdio.h>
int main() {
float pi = 3.14159f;
// Explicit conversion: truncating the decimal part
int integer_pi = (int)pi;
printf("Truncated: %d\n", integer_pi); // Output: 3
// Note: Not rounding!
float num = 3.99f;
int truncated = (int)num;
printf("3.99 truncated: %d\n", truncated); // Output: 3
return 0;
}
(3) Common Pitfalls and Solutions
Pitfall 1: Integer Overflow
#include <stdio.h>
#include <limits.h> // Includes definitions for max values of types
int main() {
int max_int = INT_MAX; // Max value for int type: 2147483647
printf("Max value: %d\n", max_int);
// Demonstration of overflow
int overflow = max_int + 1;
printf("After overflow: %d\n", overflow); // Output: -2147483648 (becomes min value)
// Solution: Use a larger data type
long long safe = (long long)max_int + 1;
printf("Safe calculation: %lld\n", safe); // Output: 2147483648
return 0;
}
Pitfall 2: Sign Extension
#include <stdio.h>
int main() {
signed char sc = -1; // Signed: 11111111
unsigned char uc = 255; // Unsigned: 11111111
printf("Signed char: %d\n", sc); // Output: -1
printf("Unsigned char: %u\n", uc); // Output: 255
// Be careful during type conversion
int from_signed = sc; // -1 extends to: 11111111111111111111111111111111
int from_unsigned = uc; // 255 extends to: 00000000000000000000000011111111
printf("Extended signed: %d\n", from_signed); // Output: -1
printf("Extended unsigned: %d\n", from_unsigned); // Output: 255
return 0;
}
6. 🎯 Practical Applications and Programming Suggestions
(1) Data Type Selection Guide
#include <stdio.h>
int main() {
// 1. Counters, indices: use int
int count = 0;
int index = 0;
// 2. Large values: use long long
long long population = 7800000000LL; // World population
// 3. Ensure non-negative: use unsigned
unsigned int file_size = 1024; // File size cannot be negative
// 4. General decimals: use float
float temperature = 36.5f; // Body temperature
// 5. High precision calculations: use double
double pi = 3.141592653589793; // More precise π
// 6. Single character: use char
char grade = 'A'; // Grade level
return 0;
}
(2) Safe Programming Practices
#include <stdio.h>
#include <limits.h>
#include <float.h>
// Safe integer addition
int safe_add(int a, int b, int *result) {
// Check for overflow
if (a > 0 && b > INT_MAX - a) {
printf("Positive overflow!\n");
return -1; // Error code
}
if (a < 0 && b < INT_MIN - a) {
printf("Negative overflow!\n");
return -1; // Error code
}
*result = a + b;
return 0; // Success
}
// Safe floating-point comparison
int float_equal(float a, float b) {
return fabs(a - b) < FLT_EPSILON; // Use system-defined minimum error
}
int main() {
int result;
if (safe_add(2000000000, 2000000000, &result) == 0) {
printf("Safe addition result: %d\n", result);
}
if (float_equal(0.1f + 0.2f, 0.3f)) {
printf("Floating-point numbers are equal within error range\n");
}
return 0;
}
7. 🚀 Expanding Knowledge
(1) Big Endian and Little Endian
In multi-byte data storage, the order of bytes is important:
#include <stdio.h>
int main() {
int num = 0x12345678; // Hexadecimal number
char *p = (char*)# // Pointer to the bytes of num
printf("Byte order in memory:\n");
for (int i = 0; i < 4; i++) {
printf("Byte%d: 0x%02X\n", i, (unsigned char)p[i]);
}
// In little-endian systems, output:
// Byte0: 0x78
// Byte1: 0x56
// Byte2: 0x34
// Byte3: 0x12
return 0;
}
(2) Applications of Bitwise Operations
#include <stdio.h>
// Print binary representation
void print_binary(int num) {
for (int i = 31; i >= 0; i--) {
printf("%d", (num >> i) & 1);
if (i % 4 == 0) printf(" "); // Space every 4 bits
}
printf("\n");
}
int main() {
int a = 12; // 1100
int b = 10; // 1010
printf("a = %d, binary:", a);
print_binary(a);
printf("b = %d, binary:", b);
print_binary(b);
printf("a & b = %d, binary:", a & b);
print_binary(a & b);
printf("a | b = %d, binary:", a | b);
print_binary(a | b);
return 0;
}
8. 📝 Summary and Reflection
Through this article, you should have mastered:
- 1. Number System Conversion: Understanding the mutual conversion between binary, octal, and hexadecimal
- 2. Code System Principles: Deep understanding of the concepts and applications of sign-magnitude, one’s complement, and two’s complement
- 3. Data Types: Familiarity with the characteristics and selection of various basic data types in C language
- 4. Storage Mechanism: Understanding how data is actually stored in memory
- 5. Precision Issues: Mastering the precision limits of floating-point numbers and correct comparison methods
- 6. Type Conversion: Understanding the rules and pitfalls of automatic and explicit conversions
🤔 Reflection Questions
- 1. Why is the range of
<span>char</span>type from -128 to 127, rather than -127 to 128? - 2. If you want to store Chinese characters, is the
<span>char</span>type sufficient? - 3. In 32-bit and 64-bit systems, the size of
<span>int</span>type may differ. How can you write portable code?
9. 📚 Next Preview
In the next lecture, we will learn Lecture 5: Introduction to Arrays and Pointers, diving deeper into the most important and challenging core concepts of C language! Arrays and pointers are key features that distinguish C language from other high-level languages and are fundamental to understanding memory management and data structures. Once you master this part, you will truly step into the core realm of C language!
💡 Learning Suggestions: Combine theory with practice. It is recommended that readers run the code examples in this article themselves, observe the output results, and deepen their understanding. When encountering problems, use
<span>printf</span>for debugging to observe the values of variables in different situations.
If you find this article helpful, please like, bookmark, and share it, so that more C language beginners can benefit! 🌟