【问题标题】:Heap corruption in simple C++ program简单 C++ 程序中的堆损坏
【发布时间】:2019-03-08 08:09:23
【问题描述】:

我写了一个简单的 C++ 程序,代码如下:

// Вариант 72, задача 2.18
#include <iostream>

#define lint long long int

using std::cin;
using std::cout;

void input(lint arr[], lint arrLen) {
    for (lint i = 0; i < arrLen; ++i) {
        cout << "arr[" << i << "] = ";
        cin >> arr[i];
    }
}

void output(lint arr[], lint arrLen) {
    for (lint i = 0; i < arrLen; ++i) {
        cout << "newArr[" << i << "] = " << arr[i] << '\n';
    }
}

bool isNumberInArray(const lint arr[], lint arrLen, lint number) {
    bool isNumberPresent = false;
    for (lint i = 0; i < arrLen; ++i) {
        if (arr[i] == number) isNumberPresent = true;
    }
    return isNumberPresent;
}

void process(const lint arr[], lint arrLen, lint newArr[], lint &newArrLen, lint m, lint M) {
    newArrLen = 0;
    for (lint i = M; i >= m; --i) {
        if (!isNumberInArray(arr, arrLen, i)) {
            newArr[newArrLen] = i;
            ++newArrLen;
        }
    }
}

int main() {
    lint arrLen, m, M;

    cout << "Enter m\n> ";
    cin >> m;
    cout << "Enter M\n> ";
    cin >> M;
    cout << "Enter array length\n> ";
    cin >> arrLen;

    lint *arr = new lint[arrLen];

    cout << "Enter array elements:\n";
    input(arr, arrLen);

    lint *newArr = new lint[arrLen], newArrLen;

    process(arr, arrLen, newArr, newArrLen, m, M);

    cout << "\nResults:\n";
    output(newArr, newArrLen);

    delete[] arr;
    delete[] newArr;
    return 0;
}

当我使用 MSVC (x86 | Debug) 编译并运行它时,它正常工作并产生所需的结果,但执行后显示以下错误:

我尝试在 WSL 下使用 g++ 编译一个程序并使用 Valgrind 对其进行调试。这是我得到的:

root@seiba-laptop : /mnt/c/Users/saber-nyan/source/repos/Project1/Project1
[130] # g++ ./main.cpp -O0 -ggdb -o ./main

root@seiba-laptop : /mnt/c/Users/saber-nyan/source/repos/Project1/Project1
[0] # valgrind ./main
==316== Memcheck, a memory error detector
==316== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==316== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==316== Command: ./main
==316==
==316== error calling PR_SET_PTRACER, vgdb might block
Enter m
> 1
Enter M
> 5
Enter array length
> 2
Enter array elements:
arr[0] = 4
arr[1] = 2
==316== Invalid write of size 8
==316==    at 0x1093A4: process(long long const*, long long, long long*, long long&, long long, long long) (main.cpp:34)
==316==    by 0x1094E4: main (main.cpp:57)
==316==  Address 0x4d60560 is 0 bytes after a block of size 16 alloc'd
==316==    at 0x483850F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==316==    by 0x1094BA: main (main.cpp:55)
==316==

Results:
newArr[0] = 5
newArr[1] = 3
==316== Invalid read of size 8
==316==    at 0x1092B7: output(long long*, long long) (main.cpp:18)
==316==    by 0x10950A: main (main.cpp:60)
==316==  Address 0x4d60560 is 0 bytes after a block of size 16 alloc'd
==316==    at 0x483850F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==316==    by 0x1094BA: main (main.cpp:55)
==316==
newArr[2] = 1
==316==
==316== HEAP SUMMARY:
==316==     in use at exit: 0 bytes in 0 blocks
==316==   total heap usage: 5 allocs, 5 frees, 74,784 bytes allocated
==316==
==316== All heap blocks were freed -- no leaks are possible
==316==
==316== For counts of detected and suppressed errors, rerun with: -v
==316== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

问题是什么以及如何解决?

【问题讨论】:

  • 不要在 C++ 中使用 C 风格的数组。不要在 C++ 中使用拥有内存的原始指针。不要在 C++ 中使用显式内存分配。您似乎编写了一个带有一些 C++ cin / cout 的 C 程序。使用标准容器,如果需要,还可以使用智能指针。
  • @bolov 如果可以,我会这样做 - 但不幸的是,这是一项实验室工作,任务中提到了使用 C 风格的数组。
  • 当你遇到这样的错误时,假设你的 code 是错误的,而不是编译器。在任何情况下,您都不应该在 C++ 中使用原始指针或 new。至少使用lint newArr[arrLen] 定义一个数组,或者使用std::vector,当您尝试超出其边界进行写入时,它实际上会磨损您。使用像unique_ptr 这样的智能指针,而不是newdelete
  • @saber-nyan 无论如何,没有什么能阻止您使用std::vector 来查找问题,然后将其替换为 C 样式数组。只是不要用vector提交代码
  • 顺便说一句,您可能想将此链接提供给您的讲师:Stop teaching C. 通过“强迫”您首先学习“C 方式”做事,他会让您经历许多不必要的苦难。 C++ 的重点是让这些东西(手动内存管理等)消失

标签: c++ arrays memory valgrind


【解决方案1】:

我的理解是您尝试构建一个新数组,其中包含原始数组中不存在的值。在您的情况下,您首先构建了 [4,2] 并尝试构建 [5,3,1],因为 [4,2] 中不存在 M=5,m=1 之间的值是 [5,3,1]。

您的问题是您首先将 newArr 构建为长度为 2 的数组(与 arr 的长度相同)。但是您不能将 3 个值放入大小为 2 的数组中。

==316== Invalid write of size 8
==316==    at 0x1093A4: process(long long const*, long long, long long*, long long&, long long, long long) (main.cpp:34)
==316==    by 0x1094E4: main (main.cpp:57)
==316==  Address 0x4d60560 is 0 bytes after a block of size 16 alloc'd
==316==    at 0x483850F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==316==    by 0x1094BA: main (main.cpp:55)

Invalid write of size 8 表示您试图在某个错误的地址写入long long int

Address 0x4d60560 is 0 bytes after a block of size 16 alloc'd 表示 valgrind 检测到您的错误写入位于大小为 16 的内存块的末尾,正好是两个 long long ints 的数组的大小。

【讨论】:

    【解决方案2】:

    问题是你的 newArr 不一定有足够的空间来容纳所有 long long int;这取决于 M 和 m 的值,它们决定了 process(...) 中的 for 循环要经过多少次迭代,以及存储在 arr 中的值。

    要修复它,在此行中:

    • lint *newArr = new lint[arrLen], newArrLen;

    分配 (M - m + 1),而不是 arrLen 的新 lints 数量。

    您还应该确保 M > m

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-29
      • 2011-12-03
      • 2011-08-03
      • 1970-01-01
      • 2010-10-17
      • 2013-09-30
      • 2019-09-20
      相关资源
      最近更新 更多