C++检查和修改字符串/字符串下标超出范围
Posted
技术标签:
【中文标题】C++检查和修改字符串/字符串下标超出范围【英文标题】:C++ Check and modify strings / String subscript out of range 【发布时间】:2017-02-24 13:50:33 【问题描述】:我正在尝试制作一个以特定方式修改单词的程序: 它应该首先检查单词的结尾,然后继续修改它们。我不会详细解释它,因为它在英语中没有多大意义。 我写了以下内容:
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
cout << "Por favor, introduzca los gentilicios separados por la tecla enter, para finalizar, escriba OK" << '\n';
string name[10];
string place[10];
for (int i(0); (i < 10); i++)
getline(cin, name[i]);
if (name[i] == "OK") //Error here
break;
for (int i(0); (i < 10); i++)
place[i] = name[i];
if (name[i][name[i].length() - 1] == 'c')
if (name[i][name[i].length()] == 'a' || (name[i][name[i].length()] == 'o') || (name[i][name[i].length()] == 'u'))
place[i][place[i].length() - 1] = 'q';
place[i][place[i].length()] = 'u';
place[i] = place[i] + "istan";
else if (name[i][name[i].length()] == 'a' || name[i][name[i].length()] == 'e' || name[i][name[i].length()] == 'i' || name[i][name[i].length()] == 'o' || name[i][name[i].length()] == 'u')
place[i][place[i].length()] = 'i';
place[i] = place[i] + "stan";
if (name[i][name[i].length()] == 's')
place[i] = place[i] + "tan";
else
place[i] = place[i] + "istan";
place[i][0] = toupper(place[i][0]);
for (int i(0); (i < 10); i++)
cout << place[i] << '\n';
return 0;
现在我收到错误 "String subscript out of range" 。我想知道错误到底在哪里。我知道当我在第 18 行写“OK”时它会提示。
【问题讨论】:
这段代码有很多风格问题,可能是也可能不是问题的一部分。使用 C 数组(以及相关的sizeof
滥用)、“裸”指针和指针算术而不是其中的 []
访问。此外,错误消息中的行号或整个错误消息(而不是转述的)会有所帮助。
*(p+ (name[i].length()-2))
与p[name[i].length()-2]
相同,与name[i + name[i].length() - 2]
相同。
【参考方案1】:
条件i <= sizeof(name)
。 sizeof(name)
以 bytes 为单位返回数组的大小,不是其中元素的数量。即使返回元素个数,<=
也是错误的,会导致越界访问(应该是<
)。
要遍历数组中的所有元素,可以使用基于范围的 for 循环:
for(auto& n : name)
getline(cin, n);
if (n == "OK")
break;
或者用 C 风格的 for 循环以正确的方式来做:
for (int i(0); i < sizeof(name)/sizeof(name[0]; i++)
…
【讨论】:
【参考方案2】:这里:
for (int i(0); (i <= sizeof(name)); i++)
sizeof(name) 是以字节为单位的数组大小,因为它是一个 std::string 数组,实际上是没有意义的。如果你想迭代超过 10 个项目,只需这么说(还要注意小于或等于这里也是错误的):
for (int i = 0; i < 10; i++)
这里:
getline(cin, name[i]);
每当您执行输入时,您必须检查输入函数的返回值并处理任何错误:
if( ! getline(cin, name[i]) )
// handle error somehow
这里:
string * p;
你确实不想要处理指向字符串的指针。如果要访问字符串的内容,可以在字符串上使用 operator[] 或其他字符串成员函数。
【讨论】:
【参考方案3】:std::string
s 不像 cstrings。您可以使用std::string*
获取其中的一部分。当你这样做时
*(p+ (name[i].length()-2))
您实际上是说将存储在p
中的地址提前name[i].length()-2
数量并访问该字符串。如果您越过 name
数组的末尾,那么这是未定义的行为。如果不是,您仍然有一个std::string
,它无法与char
进行比较。如果你想检查字符串是否以"ca"
结尾,那么你可以使用
if (name[i].substr(name[i].size() - 2) == "ca")
【讨论】:
【参考方案4】:你的最后一个循环正在做一些非常时髦的事情。没必要走那么远。你可以这样做:
if (name[i][name[i].length - 2] == 'c')
将倒数第二个字符与 c 进行比较。还有一个非常相似的测试,将最后一个与 a 进行比较。
为了澄清为什么你做的不对,你首先得到p
作为指向当前元素的字符串的指针。然后你做一些指针算术p + (name[i].length - 2)
,它仍然会产生一个指向字符串的指针。最后,您取消引用它,从而产生一个字符串。这是你无法与 char 相比的。此外,指针指向内存中的某个任意地址,因此取消引用会产生一个字符串,其中包含 very bad 数据。很随意,有人可能会说。如果你试图使用它,你会破坏你的程序
您似乎在使用该字符串,就像使用类似 C 的字符串 char*
一样。尽管它们代表相同的概念,但两者并不相同。 C++ 字符串通常有一个 size 字段,里面有一个 char*
指针,还有一堆其他的逻辑来使它使用 char-m。
【讨论】:
感谢您的解释!【参考方案5】:因为您没有与字符串中的特定字符进行比较,您正在与字符串进行比较。
考虑以下代码:
*(p + (name[i].length() - 2))
这将评估为一个字符串,因为您正在使用 p (一个字符串*)并将一个字符连接到它。这意味着它仍然是一个字符串(即使它是一个单字符的字符串),因此等式的另一边无法与之相比。
你需要的是这个:
if (name[i][name[i].length() - 2] == 'c')
由于 name[i] 已经是一个字符串,我们可以使用上面的代码从中获取 char。这确实返回字符,所以它是可比较的。这也允许您摆脱不需要的整个 string* 位。
【讨论】:
非常感谢!我确实想过这个。但我认为访问数组内部的数组不会像 name[x][y] 这么简单【参考方案6】:首先(i <= sizeof(name))
是错误的,应该是i < sizeof(name) / sizeof(*name)
。 sizeof(array)
返回array
的大小(以字节为单位),您需要除以array
的元素大小才能实际获得数组的最大元素数。如果您觉得这很复杂,请使用std::vector
:
vector<string> name(10); //a vector of size 10
for (size_t i = 0; i < name.size(); i++) //name.size(), simple
其次,您需要跟踪name
数组中有多少个字符串。或者您需要检查name[i] == "OK"
是否然后中断第二个循环(类似于第一个循环)。 name[i]
后面的"OK"
无效。
第三,不要使用*(p+ (name[i].length()-2))
。如果要name[i]
的倒数第二个字符,可以写成name[i][name[i].size()-2]
或name[i].end()[-2]
或end(name[i])[-2]
如果要检查单词是否以“ca”结尾,则可以使用substr
:
if (name[i].substr(name[i].size() - 2) == "ca")
//...
【讨论】:
以上是关于C++检查和修改字符串/字符串下标超出范围的主要内容,如果未能解决你的问题,请参考以下文章