首页 > 编程语言 >C++中的字符和字符串

C++中的字符和字符串

时间:2024-09-07 13:53:08浏览次数:15  
标签:std 字符 数组 C++ char str 字符串

一:引言

1、错误分析

请先看一下以下代码

#include <iostream>
#include <utility> // 包含pair和make_pair的定义

int main() {
    // 创建pair对象
    std::pair<int, std::string> p1(1, "one");

    // 使用make_pair创建pair对象
    auto p2 = std::make_pair(2, "two");
    //make_pair 通过参数 x 和 y 的类型来自动推导出 pair 中的 first 和 second 的类型。
    // 访问pair的成员
    std::cout << "p1: (" << p1.first << ", " << p1.second << ")\n";
    std::cout << "p2: (" << p2.first << ", " << p2.second << ")\n";

    // 比较pair对象
//pair 支持比较操作,按照词典顺序比较,即先比较 first,如果相等则比较 second。
    if (p1 < p2) {
        std::cout << "p1 is less than p2\n";
    } else {
        std::cout << "p1 is not less than p2\n";
    }
    return 0;
}

 以上代码 创建pair对象,并且比较pair对象看似没啥错误,实际上(p1<p2)处出现了一下错误

 [Error] no match for 'operator<' (operand types are 'std::pair<int, std::__cxx11::basic_string<char> >' and 'std::pair<int, const char*>')

这个错误信息表明,在尝试比较两个 pair 对象时,类型不匹配,导致编译器无法使用 < 运算符进行比较。具体来说,问题在于两个 pairsecond 元素类型不相同,一个是 std::string 类型 (std::__cxx11::basic_string<char>) ,另一个是 const char* 类型。这种不匹配会导致 operator< 无法直接应用。

auto p1 = std::make_pair(2, "two"); 这行代码中,make_pair 根据传入的参数 2"two" 推导出 pair<int, const char*> 类型。相当于手动定义:std::pair<int, const char*> p2(2, "two");

 这个错误的解决方案

1、使用string的构造函数匿名对象,使得 make_pair 使用 std::string 类型

auto p2 = std::make_pair(2, std::string("two"));

2、使用 const char* 一致类型

std::pair<int, const char*> p1(1, "one");

2、小结

  • pair 的元素类型必须一致才能进行比较操作。
  • std::stringconst char* 是不同的类型,尽管它们可以通过转换进行交互,但不能直接比较。
  • 确保 pair 中的类型一致,可以避免类型不匹配的编译错误。

二: 字符与字符串

在 C++ 中,字符和字符串是用于处理文本的基本数据类型和数据结构。它们在编程中非常常见,用于存储和操作文本数据。以下是对 C++ 中字符和字符串的详细讲解,包括字符、C 风格字符串(C-strings)和 C++ 标准库中的 std::string

1. 字符(char

char 类型

  • char 是 C++ 中最基本的字符数据类型,用于表示单个字符。
  • 占用 1 字节(8 位),可以存储 ASCII 码值(0 到 127)以及扩展字符集(0 到 255)。
  • 常用于处理单个字符,如字母、数字或符号。
#include <iostream>

int main() {
    char ch = 'A'; // 定义一个字符变量,'A' 是字符常量,必须用单引号括起来
    std::cout << "Character: " << ch << std::endl;
    std::cout << "ASCII Value: " << static_cast<int>(ch) << std::endl; 
// 转换为整数类型,char 类型的变量可以通过 static_cast<int>(ch) 转换为对应的 ASCII 整数值。
    return 0;
}

2. C 风格字符串(C-Strings)

C 风格字符串定义

  • C 风格字符串是一个以 \0(空字符)结尾的字符数组。字符串必须以 \0 结尾,这标识字符串结束,否则操作字符串时可能导致未定义行为。
  • 它是 C 语言遗留下来的字符串处理方式,在 C++ 中也可用。
  • 常见的字符数组操作有:定义、初始化、输入输出、字符串函数(如 strlenstrcpy 等)。

C 风格字符串常用函数

  • strlen():返回字符串的长度。
  • strcpy():复制字符串。
  • strcat():拼接两个字符串。
  • strcmp():比较两个字符串。
#include <iostream>
#include <cstring>// 包含C风格字符串操作函数

int main() {
    // 声明和初始化
    char str1[] = "Hello";// 自动推导为 char str1[6]
    char str2[20] = "World";// 数组大小为 20, 但只用掉了前6个位置, 其余元素初始化为 \0。

    // 字符串长度
    std::cout << "str1长度: " << strlen(str1) << std::endl;

    // 字符串复制
    char str3[20];
    strcpy(str3, str1);
    std::cout << "str3: " << str3 << std::endl;

    // 字符串连接
    strcat(str3, " ");
    strcat(str3, str2);
    std::cout << "连接后的str3: " << str3 << std::endl;

    // 字符串比较
    if (strcmp(str1, str2) == 0) {
        std::cout << "str1 和 str2 相等" << std::endl;
    } else {
        std::cout << "str1 和 str2 不相等" << std::endl;
    }

    return 0;
}
str1长度: 5
str3: Hello
连接后的str3: Hello World
str1 和 str2 不相等

指定大小不初始化:  如果明确指定数组大小,大小必须足够大以容纳字符串及终止符 \0

未定义的字符数组:未初始化的字符数组包含随机值,使用前必须手动赋值并添加终止符。

#include <iostream>

int main() {
    char str[10];              // 大小为 10 的字符数组,内容未定义 
//如果你只指定大小但不进行初始化,数组中的内容将是未定义的随机数据(垃圾值)。
    // str 的内容未定义,不能安全输出
    str[0] = 'C';              // 手动设置字符
    str[1] = '+';
    str[2] = '+';
    str[3] = '\0';             // 添加终止符,表示字符串结束

    std::cout << "str: " << str << std::endl;// 输出 str: C++
    return 0;
}

3. C++ 字符串(std::string

std::string 类型

  • std::string 是 C++ 标准库提供的字符串类,定义在 <string> 头文件中。
  • 提供了对字符串的更高层次的抽象,支持动态管理字符数组的大小。
  • 支持丰富的操作符和成员函数,如拼接、查找、替换、插入等。

常用成员函数

  • size()length():获取字符串长度。
  • append():在字符串末尾追加内容。
  • substr():返回子字符串。
  • find():查找字符或子字符串。
  • replace():替换字符串中的部分内容。
  • insert():插入内容到指定位置。
  • erase():删除指定位置的字符或子串。
#include <iostream>
#include <string>

int main() {
    // 声明和初始化
    std::string s1 = "Hello";
    std::string s2("World");
    std::string s3(5, 'A');  // 创建包含5个'A'的字符串

    // 字符串连接
    std::string s4 = s1 + " " + s2;
    std::cout << "s4: " << s4 << std::endl;

    // 获取长度
    std::cout << "s4长度: " << s4.length() << std::endl;

    // 访问单个字符
    std::cout << "s4的第一个字符: " << s4[0] << std::endl;

    // 子字符串
    std::cout << "s4的子串: " << s4.substr(0, 5) << std::endl;

    // 查找
    size_t pos = s4.find("World");
    if (pos != std::string::npos) {
        std::cout << "找到 'World' 在位置: " << pos << std::endl;
    }

    // 替换
    s4.replace(0, 5, "Hi");
    std::cout << "替换后的s4: " << s4 << std::endl;

    // 插入
    s4.insert(2, " there");
    std::cout << "插入后的s4: " << s4 << std::endl;
	
	// 删除
	s4.erase(8,6);
	std::cout << "删除后的s4: "<< s4 << std::endl; 
    return 0;
}
s4: Hello World
s4长度: 11
s4的第一个字符: H
s4的子串: Hello
找到 'World' 在位置: 6
替换后的s4: Hi World
插入后的s4: Hi there World
删除后的s4: Hi there

三:深入探讨

1.字符数组的错误使用分析

在 C++ 中,当定义一个字符数组(C 风格字符串)时,如 char str[] = "helle";,字符数组会被初始化为包含特定字符的数组,数组的大小是根据初始化字符串的长度自动决定的(包括终止字符 \0)。但字符数组的名字 str 是一个不可修改的指针常量,指向这个数组的第一个元素。直接将数组 str4 赋值为另一个字符串是非法的,因为 C++ 不允许对数组名进行重新赋值。

char str[] = "hello";  // 声明并初始化字符数组
str = "world";         // 错误!无法将新字符串赋值给字符数组

1. char str[] = "hello";

  • 这行代码定义了一个字符数组 str,并用字符串 "hello" 对其进行初始化。
  • 编译器会自动为这个数组分配足够的空间,大小为 6 个字节('h', 'e', 'l', 'l', 'e', '\0')。
  • 数组 str 的类型是 char[6],它是一个固定大小的数组,数组的名字 str 是一个不可修改的指针常量,即不能让它指向新的地址。

2. str = "world";

  • 这行代码试图将 str 指向新的字符串 "world",这是错误的。
  • 原因是数组名 str 是一个指向数组首元素的指针常量,不能被修改为指向其他内存位置。它在初始化时已经绑定到分配的内存地址,无法通过赋值操作改变。

合法的字符串修改方法

要修改 str 中的内容,可以使用字符串函数(如 strcpy),或者逐个修改字符。以下是几种正确的方法:

 方法 1:使用 strcpy 函数
#include <iostream>
#include <cstring> // 包含strcpy函数

int main() {
    char str[] = "hello";  // 声明并初始化字符数组

    // 使用strcpy复制新字符串到数组中
    std::strcpy(str, "world");

    std::cout << "str: " << str << std::endl;  // 输出修改后的字符串
    return 0;
}
  • strcpy 函数从源字符串 "world" 复制字符到 str 所指向的内存空间。
  • 确保目标数组足够大以容纳新字符串及其结束符,否则会导致未定义行为(如内存越界)。
 方法 2:手动逐个修改字符
#include <iostream>

int main() {
    char str[] = "hello";  // 声明并初始化字符数组

    // 手动修改每个字符
    str[0] = 'w';
    str[1] = 'o';
    str[2] = 'r';
    str[3] = 'l';
    str[4] = 'd';
    str[5] = '\0'; // 添加字符串结束符

    std::cout << "str: " << str << std::endl;  // 输出修改后的字符串
    return 0;
}

2. 不同字符串操作方式

const char* str

  • 定义:C风格字符串,本质上是指向字符数组第一个元素的常量指针。const 修饰的是指针指向的内容,而不是指针本身。可以改变指针 str 的指向,让它指向新的字符串字面量。
  • 特点: a. 固定大小:一旦定义就不能改变其内容(因为是const)。 b. 以空字符('\0')结尾。 c. 使用C标准库函数进行操作,如 strlen(), strcpy(), strcat() 等。
const char* str = "hello";
str = "world"; // 允许更改指向,但不能修改字符串内容

char str[]const char* str 的区别

  • 内存分配:
    • char str[]:在栈上分配内存。数组的大小在编译时确定,不能改变。
    • const char* str:只分配指针本身的内存(通常是4或8字节)。字符串内容通常存储在只读数据段。
  • 可变性:
    • char str[]:数组内容可以修改。例如,str[0] = 'H'; 是合法的。
    • const char* str:指向的内容不能修改(因为是const)。尝试修改会导致编译错误或运行时错误。
  • 指针操作:
    • char str[]:数组名会退化为指向数组首元素的指针,但不能重新赋值。
    • const char* str:可以重新赋值为指向其他字符串。
  • 大小信息:
    • char str[]:使用 sizeof(str) 可以得到数组的总大小(包括结尾的空字符)。
    • const char* strsizeof(str) 只会返回指针的大小,而不是字符串的长度。
  • 初始化:
    • char str[] = "Hello";:编译器会分配6个字节的内存(5个字符加1个空字符)。char str[]必须在声明时指定大小或初始化。
    • const char* str = "Hello";:str指向一个字符串字面量,通常存储在只读内存中。const char* str可以只声明了指针,不需要立即初始化,但使用前必须指向有效的地址。
#include <iostream>
#include <cstring>

int main() {
   
    char str1[] = "Hello";// 正确的数组声明和初始化
    std::cout << "str1: " << str1 << std::endl;
    std::cout << "Size of str1: " << sizeof(str1) << std::endl;
    
    str1[0] = 'h';// 允许修改 char 数组内容
    std::cout << "Modified str1: " << str1 << std::endl;

    const char* str2 = "World";// 正确的指针声明和初始化 str2指向字符串字面量
    std::cout << "str2: " << str2 << std::endl;
    std::cout << "Size of str2: " << sizeof(str2) << std::endl;//(通常是4或8字节)

    // 不能修改 const char* 指向的内容
    // str2[0] = 'w';  // 这行会导致编译错误,指向的内容是常量

   
    str2 = "New World"; // 但可以改变指针指向的地址
    std::cout << "New str2: " << str2 << std::endl;

    // char 数组的指针算术
    for (int i = 0; i < 5; ++i) {
        std::cout << "str1[" << i << "]: " << str1[i] << ' ';
    }
	std::cout<<std::endl;
    // const char* 的指针算术
    for (int i = 0; i < 5; ++i) {
        std::cout << "str2[" << i << "]: " << str2[i] << ' ';
    }

    return 0;
}

 输出结果

str1: Hello
Size of str1: 6
Modified str1: hello
str2: World
Size of str2: 8
New str2: New World
str1[0]: h str1[1]: e str1[2]: l str1[3]: l str1[4]: o
str2[0]: N str2[1]: e str2[2]: w str2[3]:   str2[4]: W

小结

  • char str1[] 是一个字符数组,存储实际的数据,可以修改内容,但大小在定义时固定。
  • const char* str2 是一个指针,指向字符串字面量或其他字符数组,指针可以修改其指向的位置,但不能修改其指向的数据内容。
  • 两者在内存管理、使用方式和修改权限上都有显著不同,不可混淆使用。

四:总结:

本文深入探讨了C++中字符和字符串的处理方法,涵盖了从基本的char类型到复杂的std::string类。我们详细讲解了以下几个关键点:

  1. 字符(char)类型的基本使用和ASCII值转换。
  2. C风格字符串(C-Strings)的定义、初始化和常用操作函数,如strlen、strcpy等。
  3. C++标准库中std::string类的优势和丰富的成员函数。
  4. char数组和const char*指针的深度比较,包括它们在内存分配、可变性、指针操作等方面的区别。

通过本文的学习,我们可以得出以下结论:

  • 在现代C++编程中,除非有特殊需求,否则通常推荐使用std::string来处理字符串。它提供了更安全、更灵活的字符串处理方式。
  • 理解C风格字符串的工作原理对于处理遗留代码和底层操作仍然重要。
  • 正确区分和使用char数组和const char*指针可以帮助我们更好地管理内存和提高代码的安全性。
  • 在使用字符串时,始终注意类型匹配,特别是在使用std::pair等模板类时。

 

标签:std,字符,数组,C++,char,str,字符串
From: https://blog.csdn.net/py_cpp_java_c/article/details/141949841

相关文章

  • 迷宫,返回所有路径并排序(C++)(回溯+dfs)
    题意说明:要求给出一个m*n的矩阵迷宫,0代表入口,1代表道路,2代表墙体,3代表出口,要求返回从入口到出口的所有移动路径并按长短从小到大排序。移动路径:就是wasd的移动路径,如一共走三步:下,右,下,则输出:sds。代码如下:#include<iostream>#include<string>#include<vector>#include<alg......
  • 【C++】模板初阶
    【C++】模板初阶1.函数模板(1).函数模板概念(2).函数模板格式(3).函数模板的原理(4).函数模板的实例化2.类模板(1).类模板的定义格式(2).类模板的实例化1.函数模板(1).函数模板概念函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据......
  • C++(clock())
    目录1.clock_t2.clock()2.1函数定义3.示例4.注意事项在C++中,clock_t和clock()是与时间度量和性能测量相关的库函数,主要用于计算程序运行的时间。1.clock_tclock_t是在<ctime>或<time.h>中定义的一个类型,通常用于存储由clock()返回的处理器时间值。这个类型......
  • C++(std::vector)
    目录1.特性2.常用成员函数2.1构造函数2.2元素访问2.3修改容器2.4容量相关2.5迭代器3.内存管理与效率4.示例:5.性能分析:std::vector是C++标准库中的一个动态数组容器,位于#include<vector>头文件中。它是一个模板类,可以存储任何类型的对象,并根据需要动态调整其大......
  • C++顺序结构(1)
    1、C++程序的样子2、流输出流COUT<<3、一个实例及解析//001程序的基本结构 //单行注释/*多行注释 被注释过的内容不会被运行,可以用来做笔记。基本结构:1.头文件 程序包含某个头文件后,程序中的代码就可以使用这个头文件里的功能。2.命名空间3.主函数 类似Scr......
  • 解决Windows 远程登陆后vscdoe,chrome,cursor 等程序假死无法输入字符的问题(键盘没有
    作过程试着使用鼠标点击vscode左侧的文件列表,打开一个新的文件。如果鼠标没有反应,可以切换到其他软件(比如浏览器),再切换回来就能恢复鼠标的点击了。将鼠标移到vscode打开的文件的窗口,按住键盘s键(其他按键也行)不松开,然后用鼠标点击vscode打开的文件窗口;点击后切换到其他打开的软件(......
  • 字符串查找函数strchr 、 strrchr和strstr的简介
    目录一、函数简介1.1. strchr 函数1.2.strrchr函数1.3. strstr 函数二、函数原型2.1. strchr 函数参数返回值2.1. strchr 函数参数返回值2.2. strstr 函数参数返回值三、函数实现(伪代码)3.1.strchr实现3.2.strrchr实现3.3. strstr实现四、......
  • letcode 438 找到字符串中所有字母异位词
    ##给定两个字符串,找到s中所有p的yi子串,返回这些子串的起始索引。##不考虑答案输出的顺序。异位词指由相同字母重排列形成的字符串(包括相同的字符串)。输入s=“cbaebabacd”,p=“abc”输出【0,6】输入s=”abab“,p=“ab”classsloution:defcharyiwei(self,nums1,nums2):has......
  • C++:构造函数、析构函数
    目录一、类的默认成员函数二、构造函数构造函数的特点三、析构函数析构函数的特点一、类的默认成员函数    默认成员函数就是用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数,一个类,我们不写的情况下编译器会默认生成以下6个默认成员函数,默认成员函......
  • 南沙信C++陈老师解一本通题: 1101:不定方程求解
    ​ 【题目描述】给定正整数a,b,c。求不定方程 ax+by=c关于未知数x和y的所有非负整数解组数。【输入】一行,包含三个正整数a,b,c两个整数之间用单个空格隔开。每个数均不大于1000。【输出】一个整数,即不定方程的非负整数解组数。【输入样例】2318【输出样例】4......