在C/C++中,字符串以'\0'结尾是一种强制要求,或者说,只有满足这个要求的
字符数组才能被称为字符串。否则,你所做的所有操作结果都是未定义的!
C标准库string.h中所有关于字符串的函数都有一个特性,对于输入字符串,默认为是以'\0'结尾的
,否则就会出现未定义行为,比如strlen,实现就依赖了这一点:
int len = 0;
while(*s++)len++;对于输出字符串(一把是返回一个char*指针),也保证在末尾加上一个'\0'
本来,如果把字符串完全当成整体(像std::string那样)的话,是不用担心这个问题的,但是C/C++里面
字符串又是以字符数组的形式存在的,所以会出现这样的代码
char a[] = {'a', 'b', 'c', 'd'};
size_t len = strlen(a);
能通过编译,因为类型检查不会出错,但是运行时行为却很诡异,基本是错的,正确的定义方式
应该是这样的char a[] = {'a', 'b', 'c', 'd', '\0'};
这个坑也影响到了stl里面string的实现,比如string的data()方法和c_str()方法默认返回的char* 就一定是以'\0'结尾的
但可怕之处就在于string又可以拆开来用,看下面代码:
#include<iostream> #include<string.h> #include<string> using namespace std; int main() { char a[] = { 'a', 'b', 'c', 'd','\0'}; string s(a); cout << s.size() << endl; cout << s.length() << endl; s[4] = 'e'; cout << s << endl; cout << s.size() << endl; cout << s.c_str() << endl; cout << s.data() << endl; return 0; }
有没发现开始的size和length的输出都符合C里面关于一个字符串的定义(排除那个'\0')
但是对于s[4] = 'e'这种会破坏字符串结构的行为,编译器竟无动于衷,导致后面s.c_str()输出就是乱码。
www.cplusplus.com里面关于string类的operator[](int)方法和at(int)方法的说明如下:
char& operator[] (size_t pos); const char& operator[] (size_t pos) const;
If pos is equal to the string length, the function returns a reference to the null character that follows the last character in the string (which should not be modified).
索引允许等于length,但是should not be modified! 也就是如果修改就是未定义行为
char& at (size_t pos); const char& at (size_t pos) const;
Returns a reference to the character at position pos in the string.
The function automatically checks whether pos is the valid position of a character in the string (i.e., whether pos is less than the string length), throwing an out_of_range exception if it is not.
这个函数倒是严格规定不许索引到length处!否则会有异常!
总结:字符串和字符数组不是一回事!而标准库里string类仍旧是以普通字符数组的形式来实现字符串的,
所以也留下了可能破坏字符串结构的隐患!
C语言string.h中所有不带n的字符串函数其实都假设了输入是合法的null terminated string,否则会造成未定义行为
比如 strcpy 应该改用 strncpy
strcmp 应该改用strncmp
strcat 应该改用strncat
这里面的n全部都是包括'\0'在内的总字节数,简单解释就是,一般我们的字符串操作函数都是循环直到'\0'为止,现在
多了一个结束出口,就是到达n处为止!
另外像strlen 甚至C++里面char_traits::length对于不以'\0'结尾的字符串也是有隐患的,输入未定义行为!