C++使用字符串之一(C风格字符串、字符串字面量)

       我们编写的程序几乎都会使用字符串。在C语言中,只能使用普通的以null结尾的字符数组来表示字符串。这种表示会有很多问题,例如缓冲区溢出等。然而,C++ STL(standard template library标准模板库)包含了一个安全易用的std::string类,这个类就没有这些缺点。

1. C风格的字符串

       在C语言中,字符串就表示为字符的数组。字符串中的最后一个字符是空字符('\0'),只有这样,操作字符串的代码才知道字符串在哪结束。官方将这个空字符定义为NUL,注意,NUL与NULL指针不是一回事,还有nullptr。尽管C++里面有更好的字符串抽象,但是如今这个各种语言遍地跑的年代,没准哪天就用到了C风格的数组。

使用C风格的字符串,最重要的就是要为'\0'分配空间(在我们眼里,空  表示没用,但是在计算机世界里,这个'\0'也要用一串二进制码表示,并不是不存在)。例如,字符串"Hello"看上去是五个字符,但是在内存中需要六个字符的空间才能保存这个字符串的值。

                                     

C++中有一些来自C语言的字符串操作函数,它们在<cstring>(不是<string>)头文件中定义。通常这些函数不直接操作内存分配。例如strcpy()函数有两个字符串参数。这个函数将第二个字符串复制到第一个字符串,而不考虑第一个字符串是否装得下第二个字符串。来看下面这段代码:

char* CopyString(const char* str)

{

char* result = new char[strlen(str)];

strcpy(result,str);  //这里有bug,跟上句话有关系。

return result;
}

这里strlen(str)返回的是不包含末尾的'\0'的字符串的长度,对于"Hello"来说,strlen("Hello")返回的值是5,而不是6。但是实际上,result需要六个存储char类型字符的空间,上段代码只给了result五个这样的空间。

所以正确的代码应该是:

char* CopyString(const char* str)

{

char* result = new char[strlen(str)+1];

strcpy(result,str); 

return result;
}

要思考的问题是:如果要将三个字符串合并成一个字符串时怎么做?依旧是三个字符串的strlen()操作之后加一,这也是为什么strlen只返回实际字符的数目,而不是实际所占空间的大小。

strcat()函数可以将字符串串联:

char* appendStrings(const char* str1,const char* str2,const char* str3)

{

char* result=new char[strlen(str1)+strlen(str2)+strlen(str3)+1];

strcpy(result,str1);

strcat(result,str2);

strcat(result,str3);

return result;

}

既然说了strlen(),把sizeof()也一并说了。C和C++中的sizeof()操作符可用于获得给定数据类型或变量的大小。例如,sizeof(char)为1,因为char的大小是1字节。但是,在C中,sizeof()和strlen()是不同的。绝对不要通过sizeof()获得字符串的大小。(这里提一下C风格的字符串的两种表示方法)。

char text1[]="abcdef";

size_t s1=sizeof(text); //返回值为7

size_t s2=strlen(text); //返回值为6

但是,如果C风格的字符串存储为char*,sizeof()就返回指针的大小

const char* text1="abcdef";

size_t s3=sizeof(text1); // 32为系统返回的是4,64位系统返回的是8

size_t s4=strlen(text1); //返回值为6

可以在<cstring>头文件中找到操作字符串的C函数的完整列表。


2. 字符串字面量

C++程序中的字符串要用 "" 包围。

cout<<"Hello"<<endl;

这段代码输出字符串hello。这段代码包含这个字符串本身而不是包含这个字符串的变量就是说不是这样的

char str[]="Hello";

cout<<str<<endl;

cout<<"Hello"<<endl;中,"Hello"是一个字符串字面量,因为这个字符串以值的形式写出,而不是一个变量。与字符串字面量关联的真正内存 在内存的只读部分中。通过这种方式,编译器可以重用等价字符串字面量的引用,来优化内存的使用。换个说法就是,你的程序里有100个cout<<"Hello"<<endl;这种语句,也就是使用了100次"Hello"字符串字面量,编译器也只在内存中创建了1个Hello实例,而不是100个。这种技术称为字面量池(literal pooling)

字符串字面量可以赋值给变量,但因为字符串字面量位于内存的只读部分,并且使用了字面量池,所以这么做是有一定风险的(想想字符串字面量赋值给一个变量之后,又去修改这个变量)。C++标准正式指出:字符串字面量的类型为"n个const char 数组"。但是,为了向后兼容老版本的不支持const 的代码(版本兼容问题烦得很),大部分编译器不会强制程序将字符串字面量赋值给const char *类型的变量。这些编译器允许将字符串赋值给不带有const 的char*,而且整个程序可以正常运行,除非试图修改字符串。一般情况下,试图修改字符串的行为是没有定义的。这种行为可能会导致很多种结果,可能没什么影响(最好是这样,不然你根本想不到bug在哪),也有可能程序崩溃,最可怕的是行为的不同取决于编译器(编译器也是人写的,学过编译原理就会写了)。看代码:

char* ptr="Hello"; //将一个字符串字面量赋值给变量ptr

ptr[1]='a'; //这是一个没有定义的行为,一般情况下这个应该是没错的。

一种更安全的编码方法是在引用字符串字面量时,使用指向const字符的指针。下面的代码包含同样的bug,但是由于这段代码将字符串字面量赋值给const char*,所以编译器会捕捉到任何写入只读内存的企图,然后,错。

const char* ptr = "Hello";

ptr[1]='a';//报错

还可以将字符串字面量用作字符数组的初始值。这种情况下,编译器会创建一个能够装得下这个字符串的数组,然后将字符串复制到这个数组。因此,编译器不会将字面量放在只读的内存中,也不会进行字面量的池操作。

char str[]="Hello";

arr[1]='a';


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值