C风格字符串

2023-08-30 20:32:34 浏览数 (1)

C风格字符串

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

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

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

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

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

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

C语言约定:如果字符型(char)数组的末尾包含了空字符(也就是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)好像可以不需要加也可以)

c++

0 人点赞