最大的区别就是C风格的字符串是静态的,不可以动态变化,使用极为麻烦。
而C++的std::string类型动态管理,非常方便。
C风格字符串和char数组是不一样的,看下面两种定义:
char carr1 = {'a', 'b', 'c'};
char carr2 = {'a', 'b', 'c', '\0'};
看上面,carr2可以说成是C风格字符串,carr1就不是C风格字符串,C风格字符串必须要以'\0'结尾的。
string类是标准库的类,并不是内置类型,标准库就像是我们自己定义的类差不多的,string类型对象没有标配'\0'结尾的。如果需要用string类给C风格字符串赋值的话,后面是需要添加'\0'的。
一、字符串字面值
字符串字面值是一串常量字符,字符串字面值常量用双引号括起来的零个或多个字符表示,为兼容C语言,C++中所有的字符串字面值都由编译器自动在末尾添加一个空字符。
字符串没有变量名字,自身表示自身
1. string literal:字符串直接量:
复制代码代码如下:
cout<<"hello"<
代码中通过包含"hello"字符串自身来将其输出,并未包含该字符串的变量。
2. 字符串直接量可以赋值给变量,但是与字符串直接量相关联的内存空间位于只读部分,因此它是常量字符数组。
复制代码代码如下:
char* ptr = "hello";
ptr[1] = 'a';//crash! attemps to write to read-only memory.
因此,当引用字符串直接量的时候使用指向const的字符数组:
复制代码代码如下:
const char* ptr = "hello";
ptr[1] = 'a';//bug! attempts to write to read-only memory.
3. 当将字符串直接量赋值给字符数组的初始值的时候。
由于字符数组存放与栈中,不允许引用其他地方的内存,因此编译器会将字符串直接量复制到栈的数组内存中。因此,可以进行相应的修改。
复制代码代码如下:
char stackArray[] = "hello";
stackArray[1] = 'a';
二、C++风格字符串(string 对象)
C++风格字符串:使用C++风格字符串的时候,要将它当做是一个普通的类型,如int,这样反而会避免将string作为一个类来理解所带来的很多问题。
1. 支持中许多函数完成的同样操作。
2. 字符串定义:
复制代码代码如下:
string myString = “hello”;
3. 操作符 = :复制字符串;比如:
复制代码代码如下:
string newone = original;
会将后者复制给前者,不会出现两个变量同样指向一个内存的情况。
4. 可以像int一样使用 == 之类的操作符
5. 可以改变字符串中的某一个字符。 如
复制代码代码如下:
string myString = "hello"; mystring[0] = 'l';
这中操作是允许的。
2.1 C风格字符串的使用
C++语言通过(const) char *类型的指针来操纵C风格字符串。
复制代码代码如下:
#include
//
cstring是string.h头文件中的C++版本,而string.h是C语言提供的标准库
//操纵C风格字符串的标准库函数(参数类型省略,都是char *类型):
strlen(s) // 返回s的长度,不包括字符串结束符null
strcmp(s1, s2) //当s1s2时,返回值>0
strcat(s1, s2) // 将字符串s2连接到s1后,并返回s1
strcpy(s1, s2) // 将s2复制给s1,并返回s1
strncat(s1, s2, n) // 将s2的前n个字符连接到s1后面,并返回s1
strncpy(s1, s2, n) // 将s2的前n个字符复制给s1,并返回s1
if(cp1 < cp2) // compares address, not the values pointed to
const char *cp1 = "A string example";
const char *cp2 = "A different string";
int i=strcmp(cp1, cp2); // i is positive
i=strcmp(cp2, cp1); // i is negative
i=strcmp(cp1, cp1); // i is zero
2.3 永远不要忘记字符串结束符null
复制代码代码如下:
char ca[]={'C', '+', '+'}; // not null-terminated
cout << strlen(ca) << endl; // disaster: ca isn't null-terminated
2.4 调用者必须确保目标字符串具有足够的大小
复制代码代码如下:
// Dangerous:What happens if we miscalculate the size of largeStr?
char largeStr[16+18+2]; // will hold cp1 a space and cp2
strcpy(largeStr, cp1); // copies cp1 into largeStr
strcat(largeStr, " "); // adds a space at end of largeStr
strcat(largeStr, cp2); // concatenates cp2 to largeStr
// prints A string example A different string
cout << largeStr << endl;
2.5 使用strn函数处理C风格字符串
复制代码代码如下:
char largeStr[16+18+2] // to hold cp1 a space and cp2
strncpy(largeStr, cp1, 17); // size to copy includes the null
strncat(largeStr, " ", 2); // pedantic, but a good habit
strncat(largeStr, cp2, 19); // adds at most 18 characters, plus a null
2.6 尽可能使用标准库类型string
复制代码代码如下:
string largeStr = cp1; // initialize largeStr as a copy of cp1
largeStr += " "; // add space at end of largeStr
largeStr += cp2; // concatenate cp2 onto end of largeStr
此时,标准库负责处理所有的内在管理问题。
三、C风格字符串
字符串字面值的类型实质是const char类型的数组。C++从C语言继承下来的一种通用结构是C风格字符串,而字符串字面值就是该类型的实例。C风格字符串是以空字符null结束的字符数组:
复制代码代码如下:
char ca1[]={'C', '+', '+'}; //
no null, not C-style string
char ca2[]={'C', '+', '+', '\0'}; // explicit null
char ca3[]="C++"; // null terminator added automatically
const char *cp="C++"; // null terminator added automatically
char *cp1=ca1; // points to first element of a array, but not C-style string
char *cp2=ca2; // points to first element of a null-terminated char array
ca1和cp1都不是C风格字符串:ca1是一个不带结束符null的字符数组,而指针cp1指向ca1,因此,它指向的并不是以null结束的数组。
字符串的连接:
1.c++中string可以替代c中的char数组且前者用起来更方便。连接两个string对象只需用'+';c字符串是用char数组实现的。以下都称c字符串为char数组
例如:
复制代码代码如下:
string s1="hello",s2="world";
string s3=s1+s2; //也可以s3=s1+"world"
cout<
当然还可以用+=连接。
2.还可以这样连接一个string对象和char数组。
例如:
复制代码代码如下:
string s1="hello";
char s2[]="world";
cout<
但不能这样连接两个char数组或字符字面值。
例如:
复制代码代码如下:
string s1="hello";
string s2="world";
string s3=s1+"world";//正确,可以连接一个string对象和字符串字面值
string s4="hello"+"world";//错误,不能这样连接连个字符串字面值
char s5[]="world";
string s6=s1+s5;//正确,可以连接一个string对象和char数组
char s7[]="hello";
stirng s8=s7+s5;//错误,不能这样连接两个char数组。
总而言之只能用+或+=连接两个string对象或一个string对象和字符串字面值或一个string对象和char数组。
连接一个string对象和字符串字面值或char数组或返回的都是string对象,所以可以连接一个string对象和字符串字面值(或char数组)后再连接一个字符串字面值(或char数组)。
例如:
复制代码代码如下:
string s;//初始化为空
char s1[]="hello";
char s2[]="world";
s=s+s1+s2;//正确
标准库的string类提供了3个成员函数来从一个string得到c类型的字符数组:c_str()、data()、copy(p,n)
①这个数组的数据是临时的,当有一个改变这些数据的成员函数被调用后,其中的数据就会失效。因此要么现用先转换,要么把它的数据复制到用户自己可以管理的内存中。注意。看下例:
const char * c;
string s= "1234" ;
c = s.c_str();
cout<
s= "abcd" ;
cout<
|
上面如果继续用c指针的话,导致的错误将是不可想象的。就如:1234变为abcd
其实上面的c = s.c_str(); 不是一个好习惯。既然c指针指向的内容容易失效,我们就应该按照上面的方法,那怎么把数据复制出来呢?这就要用到strcpy等函数(推荐)。
std::string str ("Please split this sentence into tokens");
char * cstr = new char [str.length()+1];
std::strcpy (cstr, str.c_str());
|
注意:不能再像上面一样①所示了,const还怎么向里面写入值啊;也不能②所示,使用了未初始化的局部变量“c”,运行会出错的 。
② c_str()返回一个客户程序可读不可改的指向字符数组的指针,不需要手动释放或删除这个指针。
2. data():与c_str()类似,但是返回的数组不以空字符终止。
3. copy(p,n,size_type _Off = 0):从string类型对象中至多复制n个字符到字符指针p指向的空间中。默认从首字符开始,但是也可以指定开始的位置(记住从0开始)。返回真正从对象中复制的字符。------用户要确保p指向的空间足够保存n个字符。
#include
#include
int main ()
{
char buffer[20];
std::string str ("Test string...");
std::size_t length = str.copy(buffer,6,5);
buffer[length]='\0';
std::cout << "buffer contains: " << buffer << '\n';
return 0;
}
|