C/C++ 字符串详记(char[], char*, string)

前言

C 和 C++ 的字符串存储形式是一致的,但各自的操作方法确大不相同,奈何 LZ 对此理解的一直不是很透彻,导致刷到字符串相关题时感觉无从下手,遂有此文。



C 字符串

存储

C语言没有专门用于存储字符串的变量类型,字符串都被存储在 char 类型的数组中,且以字符 \0结尾;这是空字符(null character),C语言用它标记字符串的结束

C语言中字符串声明及初始化

char str[4] = "s v"; // char str[3] = "s v";是错的
char str[] = "s v";
char str[4] = {'s', ' ', 'v', '\0'};

上述三种写法是等价的。

再看下面的例子

char c1[] = "abc";
char c2[] = "abc\0";
char c3[4] = "ab\0";
char c4[4] = "abc\0";
char c5[] = {'a', 'b', 'c', 'd'};
char c6[4] = {'a', 'b', 'c', 'd'};
char c7[] = {'a', 'b', 'c', '\0'};
char c8[4] = {'a', 'b', 'c', '\0'};
char c9[4] = {'a', 'b', '\0'};
char c10[4] = {'a', 'b', '\0', 'c'};
char c11[4] = {'a', 'b', 'c', '\0', 'd'};

分别用 sizeof、strlen 计算字符串长度

printf("%p %s %lu %lu", &c, c, sizeof(c), strlen(c));
stringaddresscontentsizeofstrlen
c10x7fff978fa3e8abc43
c20x7fff978fa3e3abc53
c30x7fff978fa3dfab42
c4error: initializer-string for array of chars is too long’
c50x7fff978fa3d7abcdabc47
c60x7fff978fa3d3abcdabcdabc411
c70x7fff978fa3cfabc43
c80x7fff978fa3cbabc43
c90x7fff978fa3c7ab42
c100x7fff978fa3c3abc42
c11too many initializers for ‘char [4]’

当然也可通过 char* 定义指向字符串的指针

char a[] = " as df?"
char *p = a;// p指向 char 数组 a 的首元素地址,可通过*p 改变数组 a 的内容

char *str;
str = "sd??a"

char *str 与 char str[ ] 的本质区别:当定义 char str[4] 时,编译器会给数组分配 4 个单元,每个单元的数据类型为字符。而定义 char *str 时, 这是个指针变量,只占四个字节,32位,用来保存一个地址。

那么,来思考一个问题,考虑如下代码,我们能根据指针 p 来使字符串a变长呢(即在原有字符串后追加字符)?

char a[] = "a s"
char *p = a;// p指向 char 数组 a 的首元素地址,可通过*p 改变数组 a 的内容

前面讲到 C 语言用 \0标记字符串的结,所以我们可以利用指针 p 追加字符,但追加完后,要以\0字符结尾。
如下代码:

char a[] = "a d"; // 等价于 char a[4] = "a d"
char *p = a;

printf("%s\n", a);
printf("sizeof(a):%zd\n", sizeof(a));

*(p+3) = '%';	// 将字符串中'\0'改为 '%', a就找不到尾巴了😂

printf("%s\n", a);
printf("sizeof(a):%zd\n", sizeof(a));
printf("%c\n", a[4]);

*(p+4) = '\0';

printf("%s\n", a);
printf("sizeof(a):%zd\n", sizeof(a));
printf("%c\n", a[4]);

输出为

a d
sizeof(a):4
a d%�
sizeof(a):4
�
a d%
sizeof(a):4

最后一行为不可见的空字符'\0',网页内不显示。可以看出在给 a 追加字符后,a的大小并没有变化,但是打印出来后面追加的字符却显示了出来,而且后面还有乱码字符(第三行);追加了尾巴'\0'之后,打印 a 就能正常显示了(第六行)。但值得注意的是 a 的容量大小一直没变!数组的内存是动态分配的,到了末元素以后的元素都是新分配的,并不一定是空。因为数组是一片连续的空间,有可能末元素后的空间是有数据的,那么C语言会将其读取出来,

C语言中的一些字符串相关函数

  1. size_t strlen(const char*)
    提到 strlen() 又不得不说 sizeof()sizeof 运算符以字节为单位给出对象的大小,它把不可见字符 '\0' 也计算在内,而 strlen运算符给出的是字符串中的字符数,并不计算字符 '\0'

  2. char *strcpy(char *dest, const char *src);
    把从 src 地址开始且含有NULL结束符('\0')的字符串复制到以dest开始的地址空间,但并不对 dest 空间大小做检测,且不检测 src 和 dest 的内存空间是否有内存重叠,所以容易溢出。

  3. int strcmp(const char *s1, const char *s2);
    当s1<s2时,返回为负数;
    当s1=s2时,返回值= 0;
    当s1>s2时,返回正数。
    即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇’\0’为止。如:

    • “A”<“B”

    • “A”<“AB”

    • “Apple”<“Banana”

    • “A”<“a”

    • “compare”<“computer”

特别注意:strcmp(const char *s1,const char * s2)这里面只能比较字符串,即可用于比较两个字符串常量,或比较数组和字符串常量,不能比较数字等其他形式的参数。
ANSI标准规定,返回值为正数,负数,0 。而确切数值是依赖不同的C实现的(gcc7.4.0测试结果输出为第一个不同字符ASCII值的差)。

  1. char *strcat(char *dest, const char *src);
    连接字符串 src 到字符串 dest 的末尾。
    warning:dest 和 src 所指内存区域不可以重叠且 dest 必须有足够的空间来容纳 src 的字符串。
  2. char *strchr(const char *s, int c);
    在参数 s 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置,返回一个指向该字符串中第一次出现的字符的指针,如果字符串中不包含该字符则返回NULL空指针。
  3. char *strstr(const char *haystack, const char *needle);
    用于判断字符串 needle 是否是 haystack 的子串。如果是,则该函数返回 needle 在 haystack 中首次出现的位置;否则,返回NULL。

C++ 字符串

C++ 提供了以下两种类型的字符串表示形式:

  • C 风格字符串(上述)
  • C++ 引入的 string 类类型

string 字符串

string 类常用的构造函数:
1.	string str;
eg:	string s = "ab\0\0c";       // s 内容为 ab
2.	string str("ab\0\0c");  		// 等价于 str="ab\0\0c"
3.	string str(string s, size_type n);
eg:	string str("ab\0\0c", 5);  //  将"ab\0\0c"的前5个字节存到 str 里
4.	string str(string s, char c, size_type n);
eg:	string str("ab\0\0c", b, 4);   //将"ab\0\0c"的 b 位置开头、长度为4字节的字符串存到 str 里
5.	string str(size_type n, char c);
eg:	string str(4, 'A')  //存储 4 个'A'到s里

warning: 可以看到如果想要在字符串中存储空字符'\0',只能用第3种方式构造。在第3,4种方法中,都限定了字符串长度,此时要格外注意,n不能超出字符串 s 的所含字符个数下标,否则构造出的字符串会包含一些未知字符。

string 类类型字符串的操作
string str = str1 + str2;// 字符串拼接
str[2];					 // 访问str中的第三个字符,无边界检查,所以推荐使用下一种一种访问方法
str.at(2);				 // 访问str中的第三个字符,有边界检查
str.empty();			 // 判断str是否为空,若 str 为空则返回true,否则返回 false 
str.length()// 获取字符串长度
str.size();         // 获取字符串数量,等价于length()
str.capacity();      // 获取容量,容量包含了当前string里不必增加内存就能使用的字符数
str.front();[C++11]			// 返回str的首字符
str.back();	[C++11]			// 返回str的末字符
str.resize(10);      // 表示设置当前string里的串大小,若设置大小大于当前串长度,则用字符\0来填充多余的.
str.resize(10, char c);  // 设置串大小,若设置大小大于当前串长度,则用字符c来填充多余的
str.reserve(10);     //设置string里的串容量,不会填充数据.
str.swap(str1);         //替换str 和 str1 的字符串
str.puch_back('A');    //在str末尾添加一个'A'字符,参数必须是字符形式
str.append("ABC");     //在str末尾添加一个"ABC"字符串,参数必须是字符串形式
str.insert(2, "ABC");   //在str的下标为2的位置,插入"ABC"
str.erase(2);          //从下标为2的位置开始删除后面的字符,比如:"ABCD" --> "AB"
str.erase(2, 1);         //从下标为2的位置删除1个字符,比如: "ABCD"  --> "ABD"
str.clear();            //清空str内容,str.size()=str().length()=0, 但不同于
						//std::vector::clear,C++标准不显式要求此函数不更改capacity,即不释放分配的内存,若str="ABC", str.capacity()=3
str.replace(2,2, "ABCD"); //从下标为2的位置,替换2个字节为"ED",比如:"ABCCA" --> "ABEDA"
str.starts_with("as");[C++20]// 判断str是否以"as"开头,是返回true,否则返回false
str.ends_with("as");[C++20]// 判断str是否以"as"结尾,是返回true,否则返回false
str.substr(2);	//结果为 str从 第三个字符开始往后的子串,比如:"ASCDS" --> "CDS"
str.substr(2, 2); //结果为 str从 第三个字符为开始、第4个字符结束的子串,比如:"ASCDS" --> "CD" substr为 [pos, pos+size)
str.find("ah");	// 返回str中字符串 "ah" 首次出现的位置 str.find(string s, size_type n=0)
				// 如果没找到则返回 -1
str.find("as", 2);	// 从str第三个字符开始寻找 "as" 第一次出现的位置,如果没找到则返回 -1
str.rfind("ah");	//在str中反向搜索字符串 "ah" 首次出现的位置
str.rfind("as", 2);// 从str第三个字符开始往后的字符中反向寻找 "as" 第一次出现的位置
str.find_first_of('a');	//查找 str 中 a 第一次出现的位置;如果str中没有字符 a,则返回 std::string::npos(18446744073709551615)
str.find_first_of('a'5);	//从 str 的第六个字符开始查找 a 第一次出现的位置;如果str中没有字符 a,则返回 std::string::npos(18446744073709551615)
str.find_first_not_of('a');	//查找 str 中第一个非 a 字符的位置;如果str中全是 a,则返回 std::string::npos(18446744073709551615)
str.find_first_not_of('a'5);	//从 str 的第六个字符开始查找 str 中第一个非 a 字符的位置;如果从 str 的第六个字符开始全是 a,则返回std::string::npos(18446744073709551615)

字符串类型转换

char → string

// char[] -> string,直接赋值即可
char a[] = "a s_Q";
string str = a;	

// char* -> string,直接赋值
const char *c = "asd";
string str = c;

string → char

// string -> char[],只能通过 strncpy() 拷贝实现
string str = "I Love u";
char c[] = str;	// wrong!!!!
char c1[] = "this string should longer than str";	// c1长度必须要大于str长度
strncpy(c1, str.c_str(), str.length()+1); // 不能漏掉 \0 ,所以要加1

// string -> char*,通过类型转换
string str = "I Love u";
const char *c = str.c_str(); // 不可修改版,str.c_str()将 string 类型转化为 const char*
char *c = const_cast<char*>(str.c_str()); // 可修改版

string → num (C++11)

转化为有符号整数
  • int stoi(string str);
  • long stol(string str);
  • long long stoll(string str);

warning: 字符串必须以数字开头,否则会抛出错误 std::invalid_argument

string str = ".9as_20.2dd3"
string str1 = "-12e33"
string str2 = "2.23e33"
cout << stoi(str) << endl;	// throw error
cout << stoi(str1) << endl;	// -12
cout << stoi(str2) << endl;	// 2
转化为无符号整数
  • unsigned long stoul(string str);
  • unsigned long long stoull(string str);
转化为浮点数
  • float stof(string str);
  • double stod(string str);
  • long double stold(string str);
string str = "as_20.2dd3";
string str1 = ".2e33";
string str2 = "2.23e33";
string str3 = "2.333333333333AAA1";
cout << stof(str) << endl;	// throw error
cout << stof(str1) << endl;	// 0.2
cout << stof(str2) << endl;	// 2.23
cout << stod(str3) << endl;	// 2.33333

number → string

直接使用 to_string() 函数

int i = -23;
float a = 1.23;
double b = -2.33;
double c = 1.3e12;
double d = 1.3e-4;
double e = 2.2e-10;
string i1 = to_string(i);	// -23
string a1 = to_string(a);	// 1.230000
string b1 = to_string(b);	// -2.330000
string c1 = to_string(c);	// 1300000000000.000000
string d1 = to_string(d);	// 0.000130
string e1 = to_string(e);	// 0.000000
  • 10
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
在MySQL中,my.ini文件用于配置MySQL服务的参数和选项。如果你的MySQL安装目录中没有my.ini文件,你可以根据以下步骤创建一个my.ini文件: 1. 打开文本编辑器,例如记事本。 2. 在编辑器中创建一个新的文本文件。 3. 将以下内容复制并粘贴到新文件中: ``` <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [MySQL安装后没有my.ini配置文件](https://blog.csdn.net/qq_21467113/article/details/122552803)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【详记MySql问题大全集】三、安装之后没有my.ini配置文件怎么办](https://blog.csdn.net/weixin_30765475/article/details/96935741)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Mysql应用安装后找不到my.ini文件](https://blog.csdn.net/m0_67391120/article/details/126803348)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值