Introduction to C Language | Lecture 4: Deep Understanding of Number Systems and Code Systems, Mastering the Secrets of Basic Data Types

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. 1. Sign-Magnitude: Sign bit + Value bits
    +6: 0110 
    -6: 1110  // The highest bit 1 indicates a negative sign
  2. 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. 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. 1. Number System Conversion: Understanding the mutual conversion between binary, octal, and hexadecimal
  2. 2. Code System Principles: Deep understanding of the concepts and applications of sign-magnitude, one’s complement, and two’s complement
  3. 3. Data Types: Familiarity with the characteristics and selection of various basic data types in C language
  4. 4. Storage Mechanism: Understanding how data is actually stored in memory
  5. 5. Precision Issues: Mastering the precision limits of floating-point numbers and correct comparison methods
  6. 6. Type Conversion: Understanding the rules and pitfalls of automatic and explicit conversions

🤔 Reflection Questions

  1. 1. Why is the range of <span>char</span> type from -128 to 127, rather than -127 to 128?
  2. 2. If you want to store Chinese characters, is the <span>char</span> type sufficient?
  3. 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! 🌟

Leave a Comment