【问题标题】:Invalid read of size 8, Invalid write of size 8 (Valgrind)大小为 8 的无效读取,大小为 8 的无效写入 (Valgrind)
【发布时间】:2014-11-08 05:07:40
【问题描述】:

今天晚上我已经用下面的代码玩了几个小时了,我只是摸不着头脑。

当使用函数从标准输入填充数组时,我不断收到“大小为 8 的无效写入”和“大小为 8 的无效读取”。

我们将不胜感激...

void RawScore(unsigned int rawScoreCount, unsigned int numStudents, student studentInfo[],
              unsigned int projectCount, double rawScores[], double scores[], double weights[])
{
    int id;

    for (int i = 0; i < rawScoreCount; i++)
    {
        std::cin >> id;

        for (int j = 0; j < numStudents; j++)
        {
            if (id == studentInfo[j].id)
            {
                for (int k = 0; k < projectCount; k++)
                {
                    std::cin >> rawScores[k];
                    studentInfo[j].score += rawScores[k]/scores[k] * weights[k];
                }
            }
        }
            std::cin.ignore(10000, '\n');
    }
}

Memcheck 的错误如下:

==5793== Memcheck, a memory error detector
==5793== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==5793== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==5793== Command: a.out.app
==5793== 
==5793== Invalid write of size 8
==5793==    at 0x40E54DB: std::__1::istreambuf_iterator<char, std::__1::char_traits<char> > std::__1::num_get<char, std::__1::istreambuf_iterator<char, std::__1::char_traits<char> > >::__do_get_floating_point<double>(std::__1::istreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::istreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::ios_base&, unsigned int&, double&) const (in /usr/lib/i386-linux-gnu/libc++.so.1.0)
==5793==    by 0x40E517E: std::__1::num_get<char, std::__1::istreambuf_iterator<char, std::__1::char_traits<char> > >::do_get(std::__1::istreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::istreambuf_iterator<char, std::__1::char_traits<char> >, std::__1::ios_base&, unsigned int&, double&) const (in /usr/lib/i386-linux-gnu/libc++.so.1.0)
==5793==    by 0x804D0FA: std::__1::basic_istream<char, std::__1::char_traits<char> >::operator>>(double&) (locale:771)
==5793==    by 0x804CECC: RawScore(unsigned int, unsigned int, student*, unsigned int, double*, double*, double*) (input.cpp:44)
==5793==    by 0x804EE6A: main (main.cpp:35)
==5793==  Address 0x445c388 is 0 bytes after a block of size 40 alloc'd
==5793==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5793==    by 0x40BA709: operator new(unsigned int) (in /usr/lib/i386-linux-gnu/libc++.so.1.0)
==5793==    by 0x804EE26: main (main.cpp:32)
==5793== 
==5793== Invalid read of size 8
==5793==    at 0x804CED3: RawScore(unsigned int, unsigned int, student*, unsigned int, double*, double*, double*) (input.cpp:49)
==5793==    by 0x804EE6A: main (main.cpp:35)
==5793==  Address 0x445c388 is 0 bytes after a block of size 40 alloc'd
==5793==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5793==    by 0x40BA709: operator new(unsigned int) (in /usr/lib/i386-linux-gnu/libc++.so.1.0)
==5793==    by 0x804EE26: main (main.cpp:32)
==5793== 
....... output of program here ......
==5793== 
==5793== HEAP SUMMARY:
==5793==     in use at exit: 0 bytes in 0 blocks
==5793==   total heap usage: 9 allocs, 9 frees, 476 bytes allocated
==5793== 
==5793== All heap blocks were freed -- no leaks are possible
==5793== 
==5793== For counts of detected and suppressed errors, rerun with: -v
==5793== ERROR SUMMARY: 20 errors from 2 contexts (suppressed: 0 from 0)

我已将问题缩小到以下两行,10 个写入错误和 10 个读取错误:

std::cin >> rawScores[k];
studentInfo[j].score += rawScores[k]/scores[k] * weights[k];

任何见解将不胜感激!

【问题讨论】:

  • 请提供 main()、输入参数,或者如果可能的话同时提供两者。

标签: c++ c++11 memcheck


【解决方案1】:
std::cin >> rawScores[k];
studentInfo[j].score += rawScores[k]/scores[k] * weights[k];

从您的上述程序中,jk 取决于用户输入,因此它们的值可以超出实际数组的 studentInfo rawScores 索引。

你的程序应该有逻辑,这样你的程序就不会访问数组边界。

你可以监控你的程序

$ valgrind --tool=memcheck --db-attach=yes ./a.out

有关此概念的详细信息以及如何使用它,您可以参考以下帖子:

https://stackoverflow.com/a/22658693/2724703

【讨论】:

  • 输出未显示任何超出范围的内容。我也在 VS 调试器上运行了这个,看不到它越界。
  • 越界访问具有未定义的行为,因此有时它不会立即出现在程序中。但是 valgrind 报告确实表明您的程序已无法访问。您可以参考上面建议的步骤并在错误点实时调试您的程序。您可以在那时验证所有变量和您的源输入状态。
  • 谢谢!你是对的——我玩弄了定义/声明并这样做了: double* rawScores = new double[rawScoreCount+2]; C++ 对我来说是相当新的——如果有什么事情发生了,我总是习惯于一个很好的崩溃!
【解决方案2】:

您是否正确分配 rawScores?

您还需要确保 projectCount 小于 rawScores 大小

【讨论】:

    【解决方案3】:
    1. 检查是否为数组 [rawScores, score, weights, studentInfo] 传递给此函数之前。
    2. 检查用户输入,应该在数组范围内。

    它会很好地解决你的问题

    【讨论】:

      【解决方案4】:

      数组边界溢出是阴险的。在 c++ 中,如果你不损坏你关心的东西,你可能永远不会发现你超越了一些东西......除了你的一些值可能不太正确。 (这是许多病毒攻击的核心“错误”——利用编写不佳的程序来假设缓冲区/数组大小。

      假设你有类似的东西:

         char buffer[50];
         char author[] = "My Name";
      
         cout << author;
         cin >> buffer;
      

      如果我输入一个 20 字符的输入字符串,没有害处,没有犯规。

      如果我输入一个 55 字符的输入字符串,“我的名字”将被部分覆盖,没有人会注意到,除非我尝试重新打印作者。这可能要等到许多(许多)声明之后才会发生。当你看到作者时,可能看起来像“1234ame”,你会问“这是从哪里来的?

      更糟糕的是,如果我输入一个 70 字符的输入字符串,我会丢弃作者,以及它之后的任何内容,可能是内存管理控制块、I/O 缓冲区等,而且可能会“引人注目”(与否)。

      现在,我希望您明白为什么阵列管理错误可能在提交很久之后才会显示(如果有的话)。 “没有失败”可能并不意味着“正确”,因此您对“输出未显示任何超出范围的内容”的评论可能不像您希望的那样令人欣慰。

      正如 Rupesh 之前所说 - 仔细注意您的数组,包括分配和填充。

      如果这不是您需要的答案,那么您需要显示数组的定义以及它们是如何创建的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多