C++编程基础(五):字符数组和字符串

文章目录

在 C++ 中处理文本数据主要有两种方式:继承自 C 语言的字符数组(C 风格字符串) ,以及 C++ 标准库提供的 std::stringstd::string 提供了更强大、更安全、更易用的接口,是现代 C++ 编程的首选。

本文将详细介绍这两种方式的定义、使用和关键区别。

一、C 风格字符串(字符数组)

C 风格字符串本质上是一个以特殊字符 \0(称为 NULL 终止符)结尾的 char 数组。

1. 定义与初始化

字符数组的声明与其他数组类似,但可以使用字符串字面量(用双引号括起)来快速初始化。

cpp 复制代码
// 数据类型 数组名[长度];
char arr1[10];

// 方式一:使用字符串字面量初始化
// 编译器会自动在末尾添加 \0
char arr2[10] = "hello"; // arr2 内容为 {'h', 'e', 'l', 'l', 'o', '\0', ?, ?, ?, ?}
char arr3[] = "world";   // 编译器自动推断长度为 6 (world 5个 + \0 1个)

// 方式二:使用字符列表初始化
// 必须手动添加 \0 才能作为字符串使用
char arr4[10] = {'a', 'b', 'c', '\0'};

2.字符和字符串的区别

char是字符,也是0~127的无符号整数。通常能用一个char表示的被称为ASCII编码。

字符串是以NULL结尾的连续地址。常用转义字符\0表示。

为字符串字面量分配空间时,必须确保数组长度至少为字符数 + 1(为 \0 留出空间)。

cpp 复制代码
char arrName[5] = "abcde"; // 错误!没有空间存放 \0!
char arrName[6] = "abcde"; // 正确(5 个字符 + 1 个 \0)
char arrName[5] = {'a', 'b', 'c', 'd', 'e'}; // 正确,是字符数组,不是字符串

3.字符数组的地址

对于字符数组,不能直接取地址,否则输出的是整个字符串,直到遇到\0。所以就算是普通的char单个字符地址,我们也需要(void*)来强转它的类型。(void*)为无类型指针,一般被称为通用指针或泛指针。

cpp 复制代码
char c1[10] = "abcde";
char c2[10] = {'a', 'b', 'c', 'd', 'e'};
char c3[10] = {"abcde"};
cout << c1 << endl; // abcde
cout << &c1[0] << endl;  // abcde
cout << &c1[1] << endl; // bcde

cout << (void*)c1 << endl; // 
cout << (void*)&c1[0] << endl;
cout << (void*)&c1[1] << endl;

4.字符数组的输入输出

cincout

  • cout 在遇到 char* 类型时,会假定它是一个 C 风格字符串,并打印直到 \0 为止。
  • cin 在读取 char[] 时,会以空格、制表符或换行符作为分隔符,导致无法读取包含空格的完整句子。
cpp 复制代码
char c[100];

cin >> c; // 如果输入 ab scdd
cout << c; // 输出 ab

安全的行输入:cin.getline()

要读取包含空格的一整行,可以使用 cin.getline()

cpp 复制代码
char line[100];
cin.getline(line, 100); // 最多读取 99 个字符(为 \0 留空cout << line << std::endl; // 输出与输入一样
return 0;

5. 常用 C 风格字符串函数

需要包含头文件 <cstring>

函数 描述
strlen(p) 返回字符串 p 的长度(不包括 \0)。
strcpy(p1, p2) p2 复制到 p1
strncpy(p1, p2, n) 最多将 p2n 个字符复制到 p1
strcat(p1, p2) p2 拼接到 p1 的末尾。
strncat(p1, p2, n) 最多将 p2n 个字符拼接到 `p1。
strcmp(p1, p2) 按字典序比较 p1p2p1<p2 返回-1, ==返回0, p1>p2 返回1)。
strncmp(p1, p2, n) 比较前 n 个字符。
stricmp(p1, p2)/strcasecmp(p1, p2) 不区分大小写比较两个字符串
strnicmp(p1, p2, n) 不区分大小写比较字符串p1p2n个字符。
strchr(p, c) 查找 cp 中首次出现的位置,返回指针,否则返回 NULL
strrchr(p, c) 查找 cp 中最后出现的位置。
strlwr 将字符串中大写字母转成小写字母
strupr 将字符串中小写字母转成大写字母
memset(void *s, int c, size_t n) s 指向的 n 字节内存设为字节值 c;常用于清零,仅对 0 或 -1 安全用于多字节类型。

strcpystrcat 假定目标数组 p1 有足够的空间,如果空间不足,它们会写入数组边界之外,破坏内存。在 C++ 中应避免使用它们,优先使用 std::stringmemset 按字节赋值,假设 int 是 4 字节,那么每个 int 会被设为 0x01010101(十进制 16843009),而不是 1。

6. 字符检查

头文件 <cctype> 提供了一系列用于检查单个字符类型的函数。

函数 作用
isalpha(int c) 检查字符是否为字母(大小写)
isupper(int c) 检查是否为大写字母('A'--'Z')
islower(int c) 检查是否为小写字母('a'--'z')
isdigit(int c) 检查是否为十进制数字('0'--'9')
isxdigit(int c) 检查是否为十六进制数字('0'--'9', 'a'--'f', 'A'--'F')
isspace(int c) 检查是否为空白字符(空格、换行、回车、制表等)
iscntrl(int c) 检查是否为控制字符(如 \n, \r, \t, \0 等)
ispunct(int c) 检查是否为标点符号(可打印但非字母、数字、空格)
isalnum(int c) 检查字符是否为字母或数字(alphanumeric)
isprint(int c) 检查是否为可打印字符(包括空格)
isgraph(int c) 检查是否为图形字符,等效于isalnum(int c)

二、std::string 类

std::string 是 C++ 标准库中的类(在 <string> 头文件中),定义隐藏了字符串的数组性质,可以像处理普通变量一样处理字符串。string对象和字符数组的主要区别是:可以将string对象声明为简单变量,而不是数组。

1.构造与初始化

cpp 复制代码
// 1. 默认构造
std::string s1; // s1 = ""

// 2. C 风格字符串构造
std::string s2 = "Hello";
std::string s3("World");

// 3. 复制构造
std::string s4(s2); // s4 = "Hello"
std::string s5 = s3; // s5 = "World"

// 4. 填充构造 (n 个 c)
std::string s6(10, 'A'); // s6 = "AAAAAAAAAA"

2.常用操作

std::string 提供了丰富的成员函数来处理字符串。

访问与长度

cpp 复制代码
std::string s = "Hello";

// 获取长度
std::cout << s.length() << std::endl; // 5
std::cout << s.size() << std::endl;   // 5 (与 length 相同)

// 判断是否为空
std::cout << s.empty() << std::endl; // 0 (false)

// 访问单个字符 (同数组)
std::cout << s[1] << std::endl; // 'e'

// 安全访问 (带边界检查,会抛出异常)
std::cout << s.at(1) << std::endl; // 'e'

修改:拼接与附加

cpp 复制代码
std::string s1 = "Hello";
std::string s2 = "World";

// 1. 使用 + 运算符
std::string s3 = s1 + " " + s2; // s3 = "Hello World"

// 2. 使用 += 运算符
s1 += "!"; // s1 = "Hello!"

// 3. 使用 append()
s2.append(" C++"); // s2 = "World C++"

// 4. 使用 push_back() (仅限单个字符)
s2.push_back('!'); // s2 = "World C++!"

修改:插入、删除、替换

cpp 复制代码
std::string s = "Hello C++";

// 1. 插入 (insert)
s.insert(6, "Beautiful "); // 在索引 6 处插入
// s = "Hello Beautiful C++"

// 2. 删除 (erase)
s.erase(6, 10); // 从索引 6 开始,删除 10 个字符
// s = "Hello C++"

// 3. 替换 (replace)
s.replace(6, 3, "World"); // 从索引 6 开始,替换 3 个字符
// s = "Hello World"

比较

可以直接使用关系运算符进行字典序比较。

cpp 复制代码
std::string s1 = "abc";
std::string s2 = "abd";
if (s1 < s2) {
    std::cout << "s1 小于 s2" << std::endl;
}
if (s1 == "abc") {
    std::cout << "s1 等于 \"abc\"" << std::endl;
}

查找与子串

find() 是最常用的查找函数,如果找不到,它返回一个特殊值 std::string::npos

cpp 复制代码
std::string s = "Hello World, World";

// 1. 查找 (find)
size_t pos1 = s.find("World"); // 从头开始查找
if (pos1 != std::string::npos) {
    std::cout << "第一次出现 'World' 的索引: " << pos1 << std::endl; // 6
}

// 2. 反向查找 (rfind)
size_t pos2 = s.rfind("World"); // 从末尾开始查找
std::cout << "最后一次出现 'World' 的索引: " << pos2 << std::endl; // 13

// 3. 截取子串 (substr)
std::string sub = s.substr(6, 5); // 从索引 6 开始,截取 5 个字符
std::cout << "子串: " << sub << std::endl; // "World"

输入输出

cin 同样会遇到空格停止。读取整行应使用 std::getline

cpp 复制代码
string str1,str2;
//读入方式1遇到换行停止
getline(cin, str1);
//读入方式2遇到空格停止
cin>>str2;

//输出方式1
printf("%s\n",str1.c_str());
//输出方式2
cout<<str2<<end1;

字符串反转

使用<algorithm> 头文件里面的reverse函数

cpp 复制代码
#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    string s1;
    cin >> s1;
    
    reverse(s1.begin(),s1.end());
    cout << s1 << endl;
    
    return 0;
}

三、字符串与数值的转换

1. 字符串 -> 数值

传统 C 方式 (<cstdlib>)

这些函数不进行错误检查。如果转换失败,通常返回 0。

  • atoi(p): 字符串 (const char*) 转 int
  • atof(p): 字符串转 double
  • atol(p): 字符串转 long

现代 C++ 方式 (C++11+, <string>)

这些函数更安全,如果无法转换会抛出异常。

  • std::stoi(s): std::stringint
  • std::stod(s): std::stringdouble
  • std::stol(s): std::stringlong
cpp 复制代码
#include <string>
#include <iostream>

int main() {
    std::string s_num = "123.45";
    int i = std::stoi(s_num);     // 转换 "123" (遇到 . 停止)
    double d = std::stod(s_num);  // 转换 "123.45"
    std::cout << "Int: " << i << ", Double: " << d << std::endl;

    return 0;
}

2. 数值 -> 字符串 (C++11+, <string>)

使用 std::to_string 是最简单的方式。

cpp 复制代码
#include <string>
#include <iostream>

int main() {
    int i = 42;
    double pi = 3.14;

    std::string s_i = std::to_string(i);    // "42"
    std::string s_pi = std::to_string(pi);  // "3.140000"

    std::cout << s_i << " 和 " << s_pi << std::endl;
    return 0;
}

四、字符数组 vs. 字符串

特性 C 风格字符串 (char[]) std::string
内存管理 手动(大小固定,易溢出) 自动(动态调整大小)
安全性 低(strcpy, gets 等) 高(边界检查,at())
功能 基础(需 ) 丰富(查找、替换、插入等)
易用性 复杂,易错 简单直观
相关推荐
无敌最俊朗@3 小时前
C++ STL中 std::list 的高频面试题与答案
开发语言·c++·list
星光一影3 小时前
Java医院管理系统HIS源码带小程序和安装教程
java·开发语言·小程序
敲代码的瓦龙3 小时前
C语言?大小端!!!
c语言·开发语言·c++·1024程序员节
软件开发技术深度爱好者4 小时前
使用Python实现播放“.gif”文件增强版
开发语言·python
想唱rap4 小时前
C++list类的模拟实现
linux·运维·服务器·数据结构·c++·windows·list
紫荆鱼4 小时前
设计模式-代理模式(Proxy)
c++·后端·设计模式·代理模式
li_qi_zhen4 小时前
【基础】导弹拦截
c++
李辉20034 小时前
Python简介及Pycharm
开发语言·python·pycharm
赵谨言4 小时前
基于python大数据的城市扬尘数宇化监控系统的设计与开发
大数据·开发语言·经验分享·python