前言
个人学习笔记
一、C风格字符串
C风格字符串以’\0’(空字符)结尾,其中C语言定义字符串有2种形式:一是字符数组(char str[]);二是字符指针(char *pstr)。对于函数而言,函数名,就是函数的入口地址;同样,数组名,就是数组的入口地址,对于字符数组而言,它的内容可以更改,但是本身不能更改。与字符数组相反,字符指针的内容不可以更改,但是本身可以更改,是一个常量指针,所以用字符指针定义C风格字符串时,会加一个const,表明这是一个常量,其内容不可以更改。对字符串进行操作的C函数定义在头文件<string.h>或<cstring>(对C标准库中<string.h>的封装)中,有以下常用操作:
函数 | 功能 |
---|---|
strlen(const char *str) | 返回字符串的长度,不包括字符串结尾的空字符 |
sizeof(const char *str) | 1、字符数组定义字符串时,返回字符串长度,包括字符串结尾的空字符;2、字符指针定义字符串时,需要注意的是返回的是指针的长度(在64位系统中,占8个字节),所以返回的并不是字符串的真正长度。 |
int strcmp(const char *lhs, const char *rhs) | 比较lhs和rhs是否相同,lhs等于rhs,返回0;lhs大于rhs,返回正数;lhs小于rhs,返回负数 |
char *strchr(const char *str, int ch) | 在str中查找首次出现ch字符的位置,若查找不到,则返回空指针。 |
char *strstr(const char *str, const char *substr) | 在str中查找首次出现子串substr的位置,若查找不到,则返回空指针。 |
char *strcpy(char *dest, const char *src) | 将src复制给dest,返回dest |
char *strcat(char *dest, const char *src) | 把src所指向的字符串追加到dest所指向的字符串的结尾,返回dest |
strcpy(char *dest, const char *src)拷贝字符串时,如果被拷贝的字符串(src)的长度大于拷贝字符串(dest)的长度,就会发生内存越界,踩踏了其他程序的内存空间,安全性不高,所以可以通过strncpy(char *dest, const char *src, size_t n)指定拷贝的长度,这样就不会发生内存越界。
代码如下(示例):
#include <string.h>//C的头文件,定义的是一些对C风格字符串的处理函数。
#include <stdlib.h>
#include <iostream>
#include <string>//C++头文件,通过模板编写
//标准命名空间
using std::cout;
using std::endl;
using std::string;//把string的实体从标准命名空间std引出来
void test1()
{
char str1[] = "11111";
char *pstr = "22222";
//C语言拼接字符串
size_t len1 = sizeof(str1);
size_t len2 = strlen(pstr);
size_t len = len1 + len2;
char *pstr1 = static_cast<char *>(malloc(len));
memset(pstr1, 0, len);
strcpy(pstr1, str1);
strcat(pstr1, pstr);
printf("pstr1 = %s\n", pstr1);
free(pstr1);
pstr1 = nullptr;
cout << strlen(pstr) << endl;
cout << sizeof(pstr) << endl;
cout << strlen(str1) << endl;
printf("len1 = %lu\n", len1);
cout << len1 << endl;
}
void test2()
{
char str1[] = "hello";
char str2[] = "world";
str1[0] = 'H';
printf("str1 = %s\n", str1);
/* str1 = nullptr; */ //error
const char *pstr = "1234";
printf("pstr = %s\n", pstr);
pstr = "aaaaaaa";
printf("pstr = %s\n", pstr);
/* pstr[0] = 'A'; */
printf("pstr = %s\n", pstr);
}
int main()
{
test2();
return 0;
}
二、C++风格字符串
C++提供了std::string类用于字符串的处理,string类定义在C++头文件<string>中,C++支持C风格字符串,但在C++程序中最好还是不要使用它们。因为C风格字符串使用起来不太方便,容易引发程序漏洞,是很多安全问题的根本原因。与C风格字符串相比,C++风格字符串不必考虑内存、字符串长度、结尾的空字符等。并且string作为一个类出现,其集成的成员操作函数功能强大,几乎能满足所有的需求。从另一个角度上说,完全可以把string当成是C++的内置数据类型,放在和int、double等内置类型同等位置上。string类本质上其实是basic_string类模板关于char型的实例化。
需要注意的是,C风格字符串可以转为C++风格字符串(隐式转换),而C++风格字符串转为C风格字符串需要通过对象运算符. ,通过对象运算符.调用成员函数c_str()。
string对象可以使用下标操作符[]和函数at()对字符串中包含的字符进行访问。需要注意的是操作符[]并不检查索引是否有效,如果索引超出范围,会引起未定义的行为。而at()会检查,如果使用at()的时候索引无效,会抛出out_of_range异常。除此以外,还可以使用迭代器进行遍历访问。
代码如下(示例):
#include <string.h>//C的头文件
#include <stdlib.h>
#include <iostream>
#include <string>//C++头文件,通过模板编写
//标准命名空间
using std::cout;
using std::endl;
using std::string;//把string的实体从标准命名空间std引出来
//C++风格字符串
void test3()
{ //1、可以从C风格字符串可以转为C++风格字符串
// C++ C
string s1 = "hello";
string s2 = "world";
string s3 = s1 + s2;//不用考虑与内存相关的概念
cout << "s1 = " << s1 << endl
<< "s2 = " << s2 << endl
<< "s3 = " << s3 << endl;
cout << endl;
//2、C++风格字符串转为C风格字符串
/* const char *pstr = s3; */ //error
const char * pstr = s3.c_str();// 这个.是对象运算符,对象通过.调用成员函数
cout << "pstr = " << pstr << endl;
cout << endl;
//3、获取C++风格字符串的长度
size_t len1 = s3.size();
size_t len2 = s3.length();
cout << "len1 = " << len1 << endl
<< "len2 = " << len2 << endl;
//4、遍历C++风格字符串
for(int idx = 0; idx != s3.size(); ++idx)
{
cout << s3[idx] << " " ;
}
cout << endl;
//迭代器遍历
string::iterator it = s3.begin();
while(it != s3.end())
{
cout << *it << " ";
++it;
}
//5、C++风格字符串的拼接
string s4 = s3 + "s3";
cout << "s4 = " << s4 << endl;
string s5 = s4 + 'A';
cout << "s5 = " << s5 << endl;
string s6 = 'A' + s5;
cout << "s6 = " << s6 << endl;
s6.append(s1);
cout << "s6 = " << s6 << endl;
//6、查找字串
size_t pos = s6.find("world");
cout << "pos = " << pos << endl;
//7、截取子串
string substr = s6.substr(pos);
cout << "substr = " << substr << endl;
}
int main()
{
test3();
return 0;
}