C++ 中的字符数组和 C 风格字符串是处理文本数据的基础方式。虽然现代 C++ 推荐使用 std::string,但理解字符数组和 C 风格字符串仍然非常重要。

字符数组基础

字符数组的声明和初始化

#include <iostream>
#include <cstring>
 
int main() {
    // 1. 声明字符数组(未初始化)
    char arr1[10];  // 包含垃圾值
    
    // 2. 逐个字符初始化
    char arr2[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
    
    // 3. 部分初始化
    char arr3[10] = {'A', 'B', 'C'};  // 剩余位置自动填充 '\0'
    
    // 4. 全零初始化
    char arr4[10] = {};  // 所有元素初始化为 '\0'
    char arr5[10] = {0}; // 等价写法
    
    // 5. 字符串字面量初始化
    char arr6[10] = "Hello";     // 自动添加 '\0'
    char arr7[] = "World";       // 自动推导大小为 6(包括 '\0')
    
    // 6. C++11 统一初始化
    char arr8{'H', 'i', '\0'};
    char arr9{"Hello"};
    
    // 输出字符数组
    std::cout << "arr2: " << arr2 << std::endl;
    std::cout << "arr6: " << arr6 << std::endl;
    std::cout << "arr7: " << arr7 << std::endl;
    
    // 显示数组大小
    std::cout << "Size of arr6: " << sizeof(arr6) << std::endl;  // 10
    std::cout << "Size of arr7: " << sizeof(arr7) << std::endl;  // 6
    
    return 0;
}

字符数组 vs 字符指针

#include <iostream>
 
int main() {
    // 字符数组:在栈上分配,可修改
    char arr[] = "Hello";
    std::cout << "Array: " << arr << std::endl;
    std::cout << "Array address: " << static_cast<void*>(arr) << std::endl;
    std::cout << "Array size: " << sizeof(arr) << std::endl;  // 6
    
    // 修改字符数组
    arr[0] = 'h';
    std::cout << "Modified array: " << arr << std::endl;
    
    // 字符指针:指向字符串字面量,通常不可修改
    const char* ptr = "World";
    std::cout << "Pointer: " << ptr << std::endl;
    std::cout << "Pointer address: " << static_cast<const void*>(ptr) << std::endl;
    std::cout << "Pointer size: " << sizeof(ptr) << std::endl;  // 8 (64位系统)
    
    // ptr[0] = 'w';  // 错误!字符串字面量通常存储在只读内存中
    
    // 字符指针指向字符数组
    char* mutablePtr = arr;
    mutablePtr[1] = 'E';
    std::cout << "Through pointer: " << mutablePtr << std::endl;
    
    return 0;
}

C 风格字符串

C 风格字符串的特点

#include <iostream>
#include <cstring>
 
void demonstrateNullTerminator() {
    char str1[] = "Hello";
    char str2[10];
    
    // 手动构建字符串
    str2[0] = 'W';
    str2[1] = 'o';
    str2[2] = 'r';
    str2[3] = 'l';
    str2[4] = 'd';
    str2[5] = '\0';  // 必须手动添加空终止符
    
    std::cout << "str1: " << str1 << std::endl;
    std::cout << "str2: " << str2 << std::endl;
    
    // 显示字符串的实际内容(包括 '\0')
    std::cout << "str1 characters: ";
    for (int i = 0; i <= 5; ++i) {
        if (str1[i] == '\0') {
            std::cout << "\\0 ";
        } else {
            std::cout << str1[i] << " ";
        }
    }
    std::cout << std::endl;
    
    // 没有空终止符的"字符串"
    char notString[5] = {'H', 'e', 'l', 'l', 'o'};  // 没有 '\0'
    std::cout << "Not a proper string: " << notString << std::endl;  // 可能输出垃圾字符
}
 
int main() {
    demonstrateNullTerminator();
    return 0;
}

字符串长度和大小

#include <iostream>
#include <cstring>
 
int main() {
    char str1[20] = "Hello";
    char str2[] = "World";
    
    // sizeof:数组的总大小(字节数)
    std::cout << "sizeof(str1): " << sizeof(str1) << std::endl;  // 20
    std::cout << "sizeof(str2): " << sizeof(str2) << std::endl;  // 6
    
    // strlen:字符串的实际长度(不包括 '\0')
    std::cout << "strlen(str1): " << strlen(str1) << std::endl;  // 5
    std::cout << "strlen(str2): " << strlen(str2) << std::endl;  // 5
    
    // 自定义 strlen 实现
    auto myStrlen = [](const char* str) -> size_t {
        size_t length = 0;
        while (str[length] != '\0') {
            ++length;
        }
        return length;
    };
    
    std::cout << "myStrlen(str1): " << myStrlen(str1) << std::endl;
    
    // 显示字符串的内存布局
    std::cout << "str1 memory layout: ";
    for (size_t i = 0; i < sizeof(str1); ++i) {
        if (str1[i] == '\0') {
            std::cout << "\\0 ";
        } else if (str1[i] == 0) {
            std::cout << "0 ";
        } else {
            std::cout << str1[i] << " ";
        }
    }
    std::cout << std::endl;
    
    return 0;
}

字符串操作函数

基本字符串函数

#include <iostream>
#include <cstring>
#include <cctype>
 
int main() {
    char str1[50] = "Hello";
    char str2[50] = "World";
    char str3[50];
    char str4[50] = "Hello World";
    
    // 1. strcpy - 字符串复制
    strcpy(str3, str1);
    std::cout << "strcpy result: " << str3 << std::endl;
    
    // 2. strcat - 字符串连接
    strcat(str1, " ");
    strcat(str1, str2);
    std::cout << "strcat result: " << str1 << std::endl;
    
    // 3. strcmp - 字符串比较
    int cmp1 = strcmp("abc", "abc");  // 0 (相等)
    int cmp2 = strcmp("abc", "abd");  // < 0 (第一个小于第二个)
    int cmp3 = strcmp("abd", "abc");  // > 0 (第一个大于第二个)
    
    std::cout << "strcmp results: " << cmp1 << ", " << cmp2 << ", " << cmp3 << std::endl;
    
    // 4. strchr - 查找字符
    char* found = strchr(str4, 'W');
    if (found) {
        std::cout << "Found 'W' at position: " << (found - str4) << std::endl;
    }
    
    // 5. strstr - 查找子字符串
    char* substr = strstr(str4, "World");
    if (substr) {
        std::cout << "Found 'World' at position: " << (substr - str4) << std::endl;
    }
    
    // 6. 安全版本的函数
    char safe1[10];
    char safe2[10] = "Test";
    
    strncpy(safe1, "Very long string", sizeof(safe1) - 1);
    safe1[sizeof(safe1) - 1] = '\0';  // 确保空终止
    std::cout << "strncpy result: " << safe1 << std::endl;
    
    strncat(safe2, " String", sizeof(safe2) - strlen(safe2) - 1);
    std::cout << "strncat result: " << safe2 << std::endl;
    
    return 0;
}

字符处理函数

#include <iostream>
#include <cctype>
#include <cstring>
 
void processString(char* str) {
    std::cout << "Original: " << str << std::endl;
    
    // 转换为大写
    char upper[100];
    strcpy(upper, str);
    for (int i = 0; upper[i]; ++i) {
        upper[i] = toupper(upper[i]);
    }
    std::cout << "Uppercase: " << upper << std::endl;
    
    // 转换为小写
    char lower[100];
    strcpy(lower, str);
    for (int i = 0; lower[i]; ++i) {
        lower[i] = tolower(lower[i]);
    }
    std::cout << "Lowercase: " << lower << std::endl;
    
    // 统计字符类型
    int letters = 0, digits = 0, spaces = 0, others = 0;
    for (int i = 0; str[i]; ++i) {
        if (isalpha(str[i])) letters++;
        else if (isdigit(str[i])) digits++;
        else if (isspace(str[i])) spaces++;
        else others++;
    }
    
    std::cout << "Statistics - Letters: " << letters 
              << ", Digits: " << digits 
              << ", Spaces: " << spaces 
              << ", Others: " << others << std::endl;
}
 
int main() {
    char text[] = "Hello World 123!";
    processString(text);
    
    return 0;
}

字符串输入输出

不同的输入方式

#include <iostream>
#include <cstring>
 
int main() {
    char name[50];
    char sentence[200];
    char word[50];
    
    std::cout << "Enter your name: ";
    std::cin >> name;  // 读取到空白字符为止
    std::cout << "Hello, " << name << std::endl;
    
    // 清除输入缓冲区
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    
    std::cout << "Enter a sentence: ";
    std::cin.getline(sentence, sizeof(sentence));  // 读取整行
    std::cout << "You said: " << sentence << std::endl;
    
    std::cout << "Enter another word: ";
    std::cin.get(word, sizeof(word));  // 类似 getline,但不消费换行符
    std::cout << "Word: " << word << std::endl;
    
    // 使用 fgets(更安全)
    std::cout << "Enter text with fgets: ";
    std::cin.ignore();  // 清除之前的换行符
    if (fgets(sentence, sizeof(sentence), stdin)) {
        // fgets 包含换行符,需要移除
        size_t len = strlen(sentence);
        if (len > 0 && sentence[len-1] == '\n') {
            sentence[len-1] = '\0';
        }
        std::cout << "fgets result: " << sentence << std::endl;
    }
    
    return 0;
}

格式化输入输出

#include <iostream>
#include <cstdio>
#include <cstring>
 
int main() {
    char buffer[100];
    char name[50];
    int age;
    double salary;
    
    // sprintf - 格式化输出到字符串
    sprintf(buffer, "Name: %s, Age: %d, Salary: %.2f", "John", 30, 50000.75);
    std::cout << "sprintf result: " << buffer << std::endl;
    
    // snprintf - 安全版本
    snprintf(buffer, sizeof(buffer), "Safe formatting: %d", 12345);
    std::cout << "snprintf result: " << buffer << std::endl;
    
    // sscanf - 从字符串解析
    strcpy(buffer, "Alice 25 45000.50");
    if (sscanf(buffer, "%s %d %lf", name, &age, &salary) == 3) {
        std::cout << "Parsed - Name: " << name 
                  << ", Age: " << age 
                  << ", Salary: " << salary << std::endl;
    }
    
    // 自定义格式化函数
    auto formatPerson = [](char* dest, size_t size, const char* name, int age) {
        snprintf(dest, size, "[Person: %s, %d years old]", name, age);
    };
    
    formatPerson(buffer, sizeof(buffer), "Bob", 35);
    std::cout << "Custom format: " << buffer << std::endl;
    
    return 0;
}

字符串数组

字符串数组的不同表示方法

#include <iostream>
#include <cstring>
 
int main() {
    // 1. 二维字符数组
    char names1[3][20] = {
        "Alice",
        "Bob", 
        "Charlie"
    };
    
    // 2. 字符指针数组
    const char* names2[] = {
        "David",
        "Eve",
        "Frank"
    };
    
    // 3. 字符指针数组(可修改指针)
    char* names3[] = {
        const_cast<char*>("Grace"),
        const_cast<char*>("Henry"),
        const_cast<char*>("Ivy")
    };
    
    std::cout << "Method 1 - 2D char array:" << std::endl;
    for (int i = 0; i < 3; ++i) {
        std::cout << "names1[" << i << "]: " << names1[i] << std::endl;
        std::cout << "  Address: " << static_cast<void*>(names1[i]) << std::endl;
        std::cout << "  Size: "