首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >理解C++字符串

理解C++字符串
EN

Stack Overflow用户
提问于 2017-01-04 18:50:20
回答 4查看 171关注 0票数 4

我试图理解字符串在C++中是如何工作的,因为我只是在遇到意外的行为后感到非常困惑。

考虑到字符串,我使用[]运算符插入一个字符(不使用[]):

代码语言:javascript
复制
string str;
str[0] = 'a';

让我们打印字符串:

代码语言:javascript
复制
cout << "str:" << str << endl;

输出值为空:

代码语言:javascript
复制
str:

好的,让我们尝试打印字符串中的唯一字符:

代码语言:javascript
复制
cout << "str[0]:" << str[0] << endl;

输出:

代码语言:javascript
复制
str[0]:a

Q1.那里发生了什么?为什么在第一种情况下没有打印a

现在,我做了一些应该抛出编译错误的事情,但是它没有,我的问题是,为什么。

代码语言:javascript
复制
str = 'ABC';

Q2.为什么这不是一个不正确的语义,即将一个字符(实际上不是一个字符,但本质上是单引号中的一个字符串)分配给一个字符串?

现在,更糟的是,当我打印字符串时,它总是打印最后一个字符即C(我期待的是第一个字符,即A):

代码语言:javascript
复制
cout << "str:" << str << endl;

输出:

代码语言:javascript
复制
str:C

Q3.为什么最后一个字符不是第一个打印出来的?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-01-04 19:59:03

考虑到一个字符串,我使用[]运算符插入一个字符(不使用append()): 字符串str;str = 'a';

你没有插入一个字符。operator[](size_type pos)返回对pos中已经存在的字符的引用。如果是pos == size(),那么行为是未定义的。您的字符串是空的,因此size() == 0str[0]具有未定义的行为。

Q1。那里发生了什么?为什么在第一种情况下没有打印?

这种行为是不明确的。

现在,我做了一些应该抛出编译错误的事情,但是它没有,我的问题是,为什么。 str = 'ABC'; Q2。为什么这不是一个不正确的语义,也就是指定一个字符.一根绳子?

将字符分配给字符串并不是不正确的语义。它将字符串的内容设置为该单个字符。

Q2。..。一个字符(实际上不是一个字符,而是单引号中的字符串).

这是一个多字符的文字。多字符文字的类型是int。如果编译器支持多字符文字,那么语义就不会不正确。

没有接受int的字符串赋值运算符。但是,int是隐式可转换为char的,因此接受char的赋值操作符在转换后使用。

char不一定表示int能够表示的所有值,因此转换可能会溢出。如果char是有符号类型,则此溢出具有未定义的行为。

Q3。为什么最后一个字不是第一个印出来的?

多字符文字的值是实现定义的。您需要查阅编译器的手册,以确定是否支持多字符文字,以及应该期望的值。此外,您还需要考虑这样一个事实:将值转换为的char可能不能表示int的所有值。

但我没有收到任何警告

然后考虑得到一个更好的编译器。这就是GCC的警告:

警告:多字符常量-Wmultichar str = 'ABC'; 警告:隐式常量转换-Woverflow中溢出

str[0] = 'a'应该处理字符串,就像它处理char str[] = ""一样(但它不是我们看到的)。您能帮助我理解为什么[]运算符在处理字符数组时与字符串有不同的行为吗?

因为这就是标准如何定义std::string的行为和需求。

代码语言:javascript
复制
char str[] = "";

创建大小为1的数组,该数组由空终止符组成。数组的这个元素和任何其他元素一样,您可以自由地修改它:

代码语言:javascript
复制
str[0] = 'a';

这是很好的定义和好的。但是现在str不再包含以空结尾的字符串,因此试图将其用作空字符串具有未定义的行为:

代码语言:javascript
复制
out << "str:" << str << endl; // oops, str is not a null terminated string

因此,std::string的设计使得您无法处理最终的空终止符--只要您遵守std::string的要求。不允许触摸空终止符还允许实现永远不为空字符串分配内存缓冲区。不分配内存可能比分配内存更快,所以这是一件好事。

票数 5
EN

Stack Overflow用户

发布于 2017-01-04 19:14:10

你应该看看在…。也就是说,关于"If pos == size(),行为是未定义的“部分。

下面的行创建一个空字符串:

代码语言:javascript
复制
string str;

所以size()将返回0。

票数 2
EN

Stack Overflow用户

发布于 2017-01-04 20:03:41

您的声明str string; str[0]='a'是未定义的行为,尽管其原因在“C++11之前”和“从C++11 on”之间存在差异。注意,str是一个非const字符串。在C++11之前,像str[pos]这样的(读)访问( pos == size()str是一个非const字符串)会产生未定义的行为。从C++11开始,将允许读取访问(生成对'\0'-character的引用)。然而,一项修改在其行为上也是没有定义的。到目前为止,有关在…的Cpp引用。

但是现在让我们来解释一下与您的程序类似但具有定义行为的程序的行为(我将用这个类比来描述您程序的行为):

代码语言:javascript
复制
string str = "bbbb";

const char* cstr = str.data();
printf("adress: %p; content:%s\n", cstr, cstr);
// yields "adress: 0x7fff5fbff5d9; content:bbbb"

str[0] = 'a';
const char* cstr2 = &str[0];
printf("adress: %p; content:%s\n", cstr2, cstr2);
// yields "adress: 0x7fff5fbff5d9; content:abbb"

cout << "str:" << str << endl;
// yields "str:abbb"

程序几乎是不言自明的,但请注意,str.data()给出了指向内部数据缓冲区的指针,str.data()返回与&str[0]相同的地址。

如果我们现在用string str = ""将相同的程序更改为您的设置,那么行为甚至不会改变到很多(尽管这种行为是未定义的、不安全的、没有保证的,并且可能因编译器而异):

代码语言:javascript
复制
string str;  // is the same as string str = ""

const char* cstr = str.data();
printf("adress: %p; content:%s\n", cstr, cstr);
// yields "adress: 0x7fff5fbff5c1; content:"

str[0] = 'a';
const char* cstr2 = &str[0];
printf("adress: %p; content:%s\n", cstr2, cstr2);
// yields "adress: 0x7fff5fbff5c1; content:a"

cout << "str:" << str << endl;
// yields "str:"

请注意,str.data()返回与&str[0]相同的地址,并且'a'实际上已经写入到该地址(如果运气好,就不会访问未分配的内存,因为空字符串不能保证缓冲区就绪;也许我们真的很幸运)。因此,打印出str.data()实际上给出了a (如果我们有额外的运气,'a'后面的字符是一个字符串终止字符)。无论如何,语句str[0]='a'不会增加字符串大小(仍然是0 ),因此cout << str会给出一个空字符串。

希望这能帮上忙。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41471184

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档