C++固定大小数组获取垃圾值

3
我尝试将一个字符串循环移位1次以实现旋转。以下是简单的代码:
#include <bits/stdc++.h>
using namespace std;

int main() {
  string s = "1010";
  char a[s.length()];
  for (int i = 0; i < s.length() - 1; i++) {
    a[i] = s[i+1];
  }
  a[s.length()-1] = s[0];
  std::cout << a << '\n';
  std::cout << strlen(a) << '\n';
}

当我运行这段代码时,会得到以下输出:
aditya@aditya-Inspiron-3558:~/miscCodes$ ./a.out 
0101�
6

我的数组长度如何变化? 由于垃圾值的存在,它会发生什么?

4
这不是有效的C++代码:char a[s.length()]; - Matthieu Brucher
除了越界访问之外,您还必须使用空字符结尾C字符串才能将它们与std :: coutstrlen一起使用。 - Yksisarvinen
除了上面的两个评论之外,你还应该给你的C风格字符串添加空终止符。 - Qubit
1
char a[s.length() + 1] = {0}; 这行代码将修复你的程序。 - 273K
4个回答

10

可变长度数组(VLA)不是标准C++的一部分。请在此处了解更多。


但假设您使用允许使用VLA的编译器扩展,那么问题在于您在此使用了一个C函数:

std::cout << strlen(a) << '\n';

函数期望一个C字符串,也就是以NULL结尾的字符串

这意味着你应该让你的数组足够大来容纳NULL终止字符,像这样:

char a[s.length() + 1];

对于字符串 "1010",string::length() 返回的是 4。这意味着 C 字符串应该是这样的: "1010\0",即实际字符串后面加上空字符(NULL terminating character)。因此,你需要一个大小为5的数组来存储该字符串。

一个简单的解决方案是:

char a[s.length() + 1] = {0};
使用这个方法将每个数组单元都置为空值(NULL),然后再用字符覆盖每个单元,除了最后一个专门保留为 NULL 结尾符的单元。
另一种方法是只给你的字符串的最后一个单元分配 NULL 结尾符,如 a[s.length()] = '\0';。注意,s.length() 现在是您数组的最后一个元素的索引。
标准 C 字符串函数(如 strlen())依赖于 NULL 结束字符来标记字符串的结尾。在没有此重要字符的情况下,它们无法知道何时停止,从而访问超过他们应该访问的内存点。
这会导致未定义行为 (UB),即在计算机上访问带有垃圾值的内存。

2
老司机了,我一直都遇到这种情况 ;) 现在回答很好 :) - Matthieu Brucher
@gsamaras,你能否解释一下为什么我的数组长度显示为6?我没有完全理解。 - anonymous
这就是未定义行为的情况,@anonymous.. strlen() 会继续从为字符串分配的内存中读取更多内容,包括添加到长度上的一个或多个字符,直到找到 NULL(比如零),在这种情况下。 - gsamaras
1
是的@anonymous,你无法确定它本来应该是什么(这就是未定义行为的情况)。例如,在您的计算机上,它是6,在我的计算机上可能是另一个数字(即使我很不幸,也可能是正确的数字(意思是我不会收到警报)),而在TedLyngmo的计算机上是42 :) - gsamaras
1
是的,它可以读取数组之外三个、四个或任意数量的字符。这就是未定义行为的情况。编辑:好的,每个人都比我快。 - ChilliDoughnuts
显示剩余2条评论

8

首先,你的代码不是有效的C++代码,因为你使用了可变长度数组。请改用std::vector代替:

std::vector<char> a(s.length() + 1, 0); // Adding +1 to add space for the 0-terminated string

for (int i = 0; i < a.size() - 2; i++) { // Because of the null terminator and the first offset
  a[i] = s[i+1];
}
a[a.size()-2] = s[0];

然后在使用data输出后:
std::cout << a.data() << '\n';
std::cout << a.size() << '\n'; // Will give you +1 because we give the size of the container and not thew size of the string

在你的for循环中,不应该是for(int i = 0; i < s.length()-1;i++)吗? - anonymous
无论如何,如果 s = "1010",我们的向量 a 包含 "010",请检查一下。 - anonymous
这是其结果,1的赋值也必须为-2。 - Matthieu Brucher
抱歉,我没听懂你的意思。 - anonymous
修改了我的答案:a[a.size()-2] = s[0]; 否则最后一个元素不会被更新。 - Matthieu Brucher

4
您已经得到了很好的答案,这只是一个补充。 #include <bits/stdc++.h> 不是标准头文件,它会让你变懒,因为它包含了你可能需要的所有内容和更多。只包含您需要的头文件,特别是如果您要使用 using namespace std;,这也是一个不好的想法
除此之外,为了解决您字符串中旋转元素的问题,请查看 std::rotate。它不仅可以旋转字符串,还可以旋转std::vector等。以下是一个左旋一步的示例:
#include <iostream>
#include <algorithm> // std::rotate

int main() {
    std::string s = "1234";
    std::string cpy = s;

    //          first elem   new first elem   last elem
    std::rotate(cpy.begin(), cpy.begin() + 1, cpy.end());
    std::cout << s.size() << " " << s << "\n";
    std::cout << cpy.size() << " " << cpy << "\n";
}

输出

4 1234
4 2341

-2

不要使用 char a[s.length()];

请使用 char a[s.length() + 1]


4
不行,和下面的回答一样的原因。那不是有效的C++代码。 - Ted Lyngmo
@HolyBlackCat 实际上,gsmaras编辑了他的答案,最初是完全错误的。很可能是bass king在没有看到其他答案的情况下编写的。此外,虽然VLA不是有效的C++,但这是对所提出问题的半正确(缺少设置0)答案,即“为什么会有垃圾输出和不同的长度”。 - Kegluneq
@Kegluneq 我能理解这个答案可能被视为回答了问题,这也是为什么它应该被评为低级别,以便没有人真正使用它来“解决”他们的问题。 - Ted Lyngmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接