【问题标题】:C++ dynamic array of ints sometimes causes crashC++ 动态整数数组有时会导致崩溃
【发布时间】:2015-07-27 22:26:52
【问题描述】:

我写了一个简单的代码如下:

void show(const int a[], unsigned elements);

int main()
{
    show(new int[]{1, 2, 3, 45}, 4); //does not work
}

void show(const int a[], unsigned elements)
{
    cout << "{ ";
    for (int i = 0; i < elements; i++)
    {
        cout << a[i];
        if (i != elements - 1)
            cout << ",";
        cout << " ";
    }
    cout << "}";
}

它应该只输出 { 1, 2, 3, 45 }。如果我在括号中包含尺寸

show(new int[4]{1, 2, 3, 45}, 4);

然后就可以了。所以很自然地,我会假设如果我以这种方式编写new,我必须指定大小(尽管我认为给它一个初始化列表会暗示大小)。但是,奇怪的是,当在 show 函数调用处设置断点并通过调试器逐步运行它时,程序正确输出所有内容并在 main 结束时终止,就像它应该的那样。如果我不使用调试器,它要么在输出 '{' 后崩溃,要么输出整个东西 "{ 1, 2, 3, 45 }" 和断言失败 "Program: ... "Expression: _CrtIsValidHeapPointer( pUserData) ... "

我很想知道它为什么会这样。另外,我在 Windows 8 上使用 Visual Studio。

编辑:我是using namepsace std。请不要评论使用命名空间或如何更好地编写此代码。我只对这个问题的原因感兴趣。

【问题讨论】:

  • 第一个问题是你使用了一个“动态数组”,这样做new 很可能会导致内存泄漏。当您使用“动态数组”时,您的下一个想法应该是std::vector
  • new T[] 的返回值是T *,而不是T[]。这看起来像是具有 Java 背景的人会写的东西。
  • @JoachimPileborg 我明白这一点,我通常不会那样使用它,或者我至少会确保删除内存,但我只是想知道它为什么会这样。
  • @nabijaczleweli 我实际上确实有 java 背景,但据我了解,您可以将指向动态分配数组的第一个元素的指针作为参数传递,其中指定的类型是数组。此外,这不是问题的原因,因为如果我指定数组的大小,它就可以正常工作。
  • 它的行为是这样的,因为new int[] 没有创建一个 4 int 的数组。从内存中,这样的代码甚至不应该根据标准编译,因为它正在使用由 4 个元素组成的初始化程序初始化不是 4 个元素的数组(new int [] 的结果)。

标签: c++ arrays memory dynamic crash


【解决方案1】:

编辑回复评论中的其他问题。

为了快速,是的,它“仍然”是一个指针,是的,当你添加 4 时它会使用 clang 和 gcc 编译。

不过,有几件事正在发生,我最初的回答是简化。问题是您的表达式一开始就格式不正确,因此不清楚它应该评估什么或应该是什么类型。考虑

如果 type 是数组类型,则除第一个维度之外的所有维度都必须指定为 std::size_t 类型(C++14 起)类型的正整数常量表达式(C++14 前)转换的常量表达式,但第一个维度可以是任何可转换为 std::size_t 的表达式。

来源:http://en.cppreference.com/w/cpp/language/new

正如它所说,无论哪种方式,括号中都必须有一个表达式。这使得很难说表达式是否会仍然计算为指针。一个格式良好的new 表达式确实会计算为一个指针,无论它有多少维,即使它为零。当我在这里说指针时,我严格指的是表示,而不是类型。

关键是类型,至少是“内部”new,取决于你有多少维度。所以,不管你这样做

new int
new int[6]
new int[12][14]

表示是相同的(指针),但new 看到的类型在每种情况下都不同。编译器能够响应new 中的不同类型(类比函数重载)。特别是当类型是数组类型时,可以用包含多个元素的大括号初始化列表来初始化新内存。

我最好的猜测是,由于 VS 接受没有表达式的括号,它为单个 intint[0] 分配内存。在前一种情况下,它错误地允许您将其初始化为数组类型,而在后一种情况下,分配的内存无论如何都不够。你的main 然后写了一个堆守卫,在那里可以在调试模式下捕获这种事情。在main 结尾或程序终止时检查此内容时,您会看到症状。输出中的不稳定是由于不同的堆布局或输出流中的缓冲造成的。

原答案

如果您的new 表达式格式正确,则它将具有标量类型,这意味着结果是“单个值”。该单个值是一个指向整数的指针,特别是指向您尝试创建的数组开头的那个。这就是“动态数组”在 C++ 中的表示方式。类型系统不“知道”它们的大小。

您正在尝试使用包含 4 个值的初始值设定项列表来初始化这个单指针值。这不应该工作。我不确定这是否应该编译。它当然没有用 clang 或 gcc 编译,我很惊讶它在 Visual Studio 中工作。

【讨论】:

  • 很有趣,但是如果我将 4 放在括号内,为什么它会起作用?该表达式仍将计算为指向单个值的指针,对吗?如果将 4 添加到括号中,它是否使用 gcc 或 clang 编译?
  • 谢谢。这就是我一直在寻找的解释。
猜你喜欢
  • 2013-09-28
  • 2015-02-15
  • 2013-02-13
  • 2013-01-09
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多