C++中的数组和字符串是处理数据集合和文本的基础工具。数组用于存储相同类型的元素集合,而字符串则专门用于处理文本数据。C++提供了两种主要的字符串处理方式:C风格字符串(字符数组)和C++的std::string类。

📊 1. 数组 (Arrays)

数组是相同类型元素的集合,这些元素在内存中连续存储。数组的大小在定义时确定,且不能改变。

一维数组

  • 声明与初始化

    数组的声明需要指定元素类型、数组名和大小。初始化可以在声明时进行。

    int numbers[5];// 声明一个包含5个整数的数组,未初始化int numbers[5] = {1, 2, 3, 4, 5};// 声明并初始化int numbers[] = {1, 2, 3, 4, 5};// 编译器自动计算大小
    
    

    如果初始化值少于数组元素,剩余元素会自动初始化为0(对于基本数据类型)

  • 访问元素

    通过索引访问数组元素,索引从0开始。

    int first = numbers[0];// 访问第一个元素
    numbers[4] = 10;// 修改第五个元素
    
    
  • 特点

    • 内存连续,访问高效。
    • 大小固定,无法动态扩容。
    • 需注意数组越界访问,这可能引发未定义行为。

多维数组

多维数组,如二维数组,可视为“数组的数组”,常用于表示矩阵或表格。

  • 声明与初始化

    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};// 2行3列int matrix[2][3] = {1, 2, 3, 4, 5, 6};// 按顺序初始化
    
    
  • 访问元素

    使用多个索引访问元素。

    int value = matrix[1][2];// 访问第二行第三列(值为6)
    
    

动态数组

使用newdelete[]在堆上动态分配和释放内存。

int size = 5;
int* dynamicArray = new int[size];// 动态分配// 使用 dynamicArray...delete[] dynamicArray;// 释放内存

动态数组需手动管理内存,防止内存泄漏。

🔤 2. 字符串 (Strings)

C++中有两种主要的字符串表示方式:C风格字符串和std::string类。

C风格字符串 (C-Style Strings)

C风格字符串本质是以空字符'\0'结尾的字符数组

  • 声明与初始化

    char str1[] = "Hello";// 编译器自动添加'\0',数组长度为6char str2[6] = {'H', 'e', 'l', 'l', 'o', '\0'};// 手动添加终止符
    
    

    字符串字面量用双引号括起,编译器会自动在其末尾添加空字符

  • 常用函数

    使用C标准库函数(需包含<cstring>)进行操作:

    #include <cstring>char str1[20] = "Hello";
    char str2[] = "World";
    
    strlen(str1);// 获取字符串长度(不包括'\0')strcpy(str1, str2);// 复制字符串(需确保目标空间足够)strcat(str1, "!");// 连接字符串(需确保目标空间足够)strcmp(str1, "Hello World!");// 比较字符串(相等返回0)
    
    

    注意:使用strcpystrcat等函数时,必须确保目标字符数组有足够的空间,否则可能导致缓冲区溢出

std::string类

C++标准库提供的std::string类(需包含<string>头文件)封装了字符串操作,自动管理内存,更安全便捷

  • 声明与初始化

    #include <string>
    std::string s1;// 空字符串
    std::string s2 = "Hello, World!";// 初始化std::string s3(s2);// 拷贝构造
    
    
  • 常用操作

    std::string支持丰富的操作:

    s1 = "Hello"; s2 = "World";
    std::string s4 = s1 + ", " + s2;// 字符串拼接(使用+运算符)
    s1.append(" World!");// 追加字符串int len = s1.length();// 或 s1.size(),获取字符串长度char ch = s1[0];// 通过索引访问字符(不检查边界)
    ch = s1.at(0);// 使用at()访问(会检查边界,越界抛出异常)
    
    std::string sub = s1.substr(7, 5);// 提取子串(从位置7开始,长度为5)size_t pos = s1.find("World");// 查找子串,返回首次出现的位置(未找到返回std::string::npos)
    s1.replace(7, 5, "C++");// 替换子串(从位置7开始,替换5个字符为"C++")
    s1.insert(7, "Beautiful ");// 插入字符串
    s1.erase(7, 10);// 删除子串(从位置7开始,删除10个字符)if (s1 == s2) {/* 比较字符串 */ }// 支持关系运算符比较
    
    
  • 输入输出

    使用getline读取整行文本(包括空格):

    std::string name;
    std::cout << "Enter your full name: ";
    std::getline(std::cin, name);// 读取整行
    std::cout << "Hello, " << name << std::endl;
    
    

    使用cin进行输入时,遇到空格、制表符或换行符会停止读取

转换

  • std::string与 C风格字符串互转

    std::string s = "Hello";
    const char* cstr1 = s.c_str();// 转换为C风格字符串(只读,指向s的内部数据)const char* cstr2 = s.data();// C++17前与c_str()类似,C++17起可能不以空字符结尾char arr[20];
    std::strcpy(arr, s.c_str());// 将s的内容复制到可修改的字符数组arr中const char* cstr = "World";
    std::string s_from_c = cstr;// C风格字符串转换为std::string
    
    

    注意:c_str()返回的指针在std::string对象发生修改后可能失效

⚖️ 3. 对比与选择

特性C风格字符串std::string
内存管理手动管理,易出错自动管理,避免泄漏
安全性易缓冲区溢出/越界更安全,边界检查
功能基础函数(strlen, strcpy等)丰富方法(拼接、查找、替换等)
性能开销小,适合底层或嵌入式动态扩容可能带来开销,但通常更便捷

建议:在现代C++开发中,优先使用std::string,除非在与C代码交互或对性能有极端要求的场景下才考虑使用C风格字符串

💡 4. 注意事项

  1. 数组越界:访问数组时,确保索引在[0, size-1]范围内,越界访问会导致未定义行为。
  2. 字符串结束符:操作C风格字符串时,务必确保其以'\0'结尾,否则相关字符串函数可能无法正常工作甚至导致程序崩溃。
  3. 空间充足:对C风格字符串进行拼接、复制等操作前,务必确认目标字符数组有足够的空间,防止缓冲区溢出。
  4. std::stringc_str():不要存储c_str()返回的指针并在后续修改std::string对象,除非已重新获取。该指针指向的数据可能在std::string修改后失效。

掌握数组和字符串的基础知识是进行有效C++开发的关键。根据具体场景选择合适的工具,并始终牢记安全操作的准则。

更多推荐