前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C风格字符串

C风格字符串

原创
作者头像
用户10731060
发布2023-08-30 20:32:34
1930
发布2023-08-30 20:32:34
举报
文章被收录于专栏:初学C++初学C++

C风格字符串

string使用方便,能自动扩展,不用担心内存问题。

string是C++的类,封装了C风格的字符串。

学习C风格字符串可以帮我们搞清楚string的本质,string虽然很方便,但是在某些应用场景中,C风格字符串会更方便,更高效。

大部分的开源库一定有C语言版本,但不一定有C++版本。例如数据库的接口函数,如MYSQL,只有C语言版本,没有C++版本。

在实际开发中,C的库函数和Linux的库函数不可能不用,还有,开源库对C++程序员很重要,可以节省很多时间。

所以如果打算深入的学习C++,必须掌握C风格的字符串。

C语言约定:如果字符型(char)数组的末尾包含了空字符\0(也就是0),那么该数组中的内容就是一个字符串。

因为字符串需要用0结尾,所以在声明字符数组的时候,要预留多一个字节用来存放0。

int main()

{ //转为ascii码显示 std::string XYZ = "abc";

std::cout << "str[0]:" << (int)XYZ[0] << std::endl; //97

std::cout << "str[1]:" << (int)XYZ[1] << std::endl; //98

std::cout << "str[2]:" << (int)XYZ[2] << std::endl; //99

std::cout << "str[3]:" << (int)XYZ[3] << std::endl; //0

return 0; }

char name[21]; //这个字符数组我们可以认为 声明了一个存放20个英文字符或10个中文(1个汉字由两个字节来存放,utf-8的汉字由3个字节存放)的字符串。

1、初始化方法

char name[11];                 // 可以存放10个字符,没有初始化,里面是垃圾值。

char name[11] ="hello";         // 初始内容为hello,系统会自动添加0。

char name[]   = { "hello" };      // 初始内容为hello,系统会自动添加0,数组长度是6。

char name[11] = {"hello" };      // 初始内容为hello,系统会自动添加0。

char name[11]   { "hello" };      // 初始内容为hello,系统会自动添加0。C++11标准。

char name[11] = { 0};         // 把全部的元素初始为0

声明字符串,如果没有初始化,危害非常大,远远超过其它数据类型,我们用下边这种没有初始化的方式,有的编译器,会打印烫烫烫烫烫烫烫烫烫烫,有的不会打印,但是会听到系统提示音,这代表没有初始化是有问题的

int main() { char name[11]; std::cout << "name = " << name << std::endl; return 0; }

但是使用后边几种方式初始化,就不会有问题。

2、清空字符串

(1) memset(name,0,sizeof(name)); //把全部元素置为0

(2) name[0] = 0; //不规范,有隐患,不推荐

3、字符串赋值或复制 strcpy() (建议使用strcpy_s(),strcpy()不安全)

char * strcpy(char * dest,char * src);

功能:将src字符串拷贝至dest所指的地址

返回值:返回dest的字符串起始地址

复制完字符串后,会在dest后追加0

如果dest所指的内存空间不够大,会导致数组越界

int main()

{ char name[11];

memset(name, 0, sizeof(char));

char name1[12] = { "hell0000000" };

strcpy(name, name1);

//建议使用strcpy_s(),strcpy()不安全

std::cout << name << std::endl; //这时赋值后就会报错

return 0; }

4、字符串赋值或复制 strncpy() (使用strncpy_s(),strncpy()不安全)

char * strncpy(char * dest,char * src,const size_t n);

功能:把src前n个字符的内容复制到dest中。

返回值:dest字符串起始地址。

如果src字符串长度小于n,则拷贝完字符串后,在dest后追加0,直到n个。

如果src的长度大于等于n,就截取src的前n个字符,不会在dest后追加0。

如果参数dest所指的内存空间不够大,会导致数组的越界。

这里有个坑:

如果不初始化 dest,那么如果复制的值的长度小于dest定义的长度,那么dest后边的内容会是垃圾值,如下所示

但是如果初始化清空后,则是正常的,如下

所以一定要记得初始化值,同样的,使用strcpy_s()函数也要注意。

所有操作字符串的操作,每次操作前都要清空。

5、获取字符串的长度 strlen()

size_t strlen(const char * str);

功能:计算字符串的有效长度,不包含0。

返回值:返回字符串的字符数。

strlen()函数计算的是字符串的实际长度,遇到0结束。

int main()

{ char name[11];

char name1[12] = {"hell000000"};

//memset(name, 0, sizeof(name));

strncpy_s(name, name1,12);

short a = strlen(name);

std::cout << a << std::endl; // 10

return 0; }

6、字符串拼接 strcat() (使用strcat_s() )

char* strcat(char * dest,const char * src);

功能:将src字符串拼接到dest所指的字符串尾部。

返回值:返回dest字符串起始地址。

dest最后原有的结尾字符0会被覆盖掉,并在连接后的字符串的尾部再增加一个0。

如果参数dest所指的内存空间不够大,会导致数组的越界。

int main()

{ char name[11] = { "abcde" };

char name1[12] = {"hell"};

//char name1[12] = { "helllllllll" };

//这种会报错,因为拼接后长度大于11

strcat_s(name, name1);

std::cout << name << std::endl;

return 0;

}

7、字符串拼接 strncat() (使用strncat_s() )

char* strncat(char* dest,const char* src,const size_t n);

功能:将src字符串的前n个字符拼接到dest所指的字符串尾部。

返回值:返回dest字符串的起始地址。

如果n大于等于字符串src的长度,那么将src全部追加到dest的尾部,如果n小于字符串src的长度,只追加src的前n个字符。

strncat会将dest字符串最后的0覆盖掉,字符追加完成后,再追加0。

如果参数dest所指的内存空间不够大,会导致数组的越界。

int main()

{

char name[11] = "abcde";

char name1[12] = {"hell00000"};

strncat_s(name, name1,5);

//strncat_s(name, name1,10); //这种会报错,因为拼接后长度大于11

std::cout << name << std::endl;

return 0; }

8、字符串比较 strcmp() 和 strncmp()

int strcmp(const char* str1,const char* str2);

功能:比较str1和str2的大小。

返回值:相等返回0,str1大于str2返回1,str1小于str2返回-1;

int strncmp(const char* str1,const char* str2,const size_t n);

功能:比较str1和str2前n个字符的大小。

返回值:相等返回0,str1大于str2返回1,str1小于str2返回-1;

两个字符串比较的方法是比较字符的ASCII码的大小,从两个字符串的第一个字符开始,如果分不出大小,就比较第二个字符,如果全部的字符都分不出大小,就返回0,表示两个字符串相等。在实际开发中,程序员一般只关心字符串是否相等,不关心哪个字符串更大或更小。

int main()

{ char str1[4] = "abc";

char str2[5] = "abcd"; // -1

//char str2[5] = "cba"; // -1 , 说明 是一个字符一个字符比较,只要分不出大小继续比较,分出大小则结束并返回

std::cout << strcmp(str1, str2) << std::endl;

std::cout << strncmp(str1, str2,1) << std::endl;

return 0;

}

9、字符串查找 strchr() 和 strrchr()

const char * strchr(const char* s,int c);

返回在字符串s中第一次出现c的位置,如果找不到,返回0。

const char * strrchr(const char* str,int c);

返回在字符串s中最后一次出现c的位置,如果找不到,返回0。

int main()

{ char str[10] = "abcdecfg";

char* ptr = nullptr;

ptr = strchr(str, 'c');

if (ptr != nullptr)

{ std::cout << strchr(str, 'c') << std::endl; //cdecfg

} else

{ std::cout << (int)strchr(str, 'c') << std::endl;

//0 }

ptr = strchr(str, 'h');

if (ptr != nullptr)

{ std::cout << strchr(str, 'h') << std::endl;

//cdecfg

}

else

{ std::cout << (int)strchr(str, 'h') << std::endl;

//0 要进行强转,否则会出现异常

}

//std::cout << strrchr(str, 'c') << std::endl; //cfg

//std::cout << (int)strrchr(str, 'n') << std::endl; //0 要进行强转,否则会出现异常

return 0;

}

10、查找字符串strstr()

const char* strstr(const char* str,const char* substr);

功能:检索子串在字符串中首次出现的位置。

返回值:返回字符串str中第一次出现子串substr的地址;如果没有检索到子串,则返回0。

int main()

{ char str[10] = "abcdefg";

char substr[4] = "cb";

char* ptr = nullptr;

ptr = strstr(str, substr);

if (ptr != NULL)

{ std::cout << strstr(str, substr); //bcefg

}

else

{ std::cout << (int)strstr(str, substr); //0 不进行强转会报错

}

return 0;

}

11、用于string的表达式

可以把C风格的字符串用于包含了string类型的赋值拼接等表达式中。

string aa = "";

char arr[10] = "abcdegf";

aa = arr;

std::cout << aa << std::endl; //abcdegf

12、注意事项

a)字符串的结尾标志是0,按照约定,在处理字符串的时候,会从起始位置开始搜索0,一直找下去,找到为止(不会判断数组是否越界)。

b)结尾标志0后面的都是垃圾内容。

c)字符串在每次使用前都要初始化,减少入坑的可能,是每次,不是第一次。(string好像不用初始化)

d)不要在子函数中对字符指针用sizeof运算,所以,不能在子函数中对传入的字符串进行初始化,除非字符串的长度也作为参数传入到了子函数中。

e)在VS中,如果要使用C标准的字符串操作函数,要在源代码文件的最上面

#define _CRT_SECURE_NO_WARNINGS (一些的ide环境(如vs2022)好像可以不需要加也可以)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档