C ++新建/删除错误?

时间:2016-12-18 00:02:33

标签: c++ heap valgrind new-operator delete-operator

我目前正在用C ++实现素数筛,我设计了代码,以便我能够控制筛子的大小(通过main函数中的整数文字),但是我得到了一些奇怪的错误一切都在。我在几年内没有使用过C ++,但我记得使用newdelete运算符来管理堆内存。

我的问题有点如下......

该程序适用于某些筛子尺寸,但不适用于其他尺寸,并且效果不是严格线性的。例如,如果我制作了大小为10,000的筛子,程序运行正常,但是如果我的大小为1,000,那么程序会因为一个相当神秘的(我的经验值)消息而崩溃,这些消息总是引导我到关于C malloc的页面断言错误(通过谷歌)。此外,该程序适用于10,000 with 删除语句,如果我通过valgrind运行另一个无用的(对我来说)消息,则会崩溃。结果,我甚至无法在我自己的DX上调试该死的东西

任何智慧的话语?

特别是,为什么程序会从较小的输入中崩溃?理论上,这可能 less 可能导致堆错误,不是吗?此外,为什么valgrind无法运行程序,而它可以自行运行?我怀疑(再次)这与新/删除操作符的错误或丑陋使用有关,但我不完全确定。对我来说特别令人困惑的部分(约valgrind)是错误提到我在new上使用unsigned long运算符,据我所知 - 不是我做的(至少没有具体)。我认为这是一些与我不熟悉的编译器相关的细节,但我并不完全确定。

代码(带有删除语句;工作时大小为10,000,但不是valgrind):

using namespace std;

#include <iostream>
#include <cstdlib>
#include <map>
#include <string>

long count_primes(bool* sieve,int size)
{
    long count = 0;
    for (int a=0;a<size;a++)
    {
        if (sieve[a]) 
        {
            count++;
        }
    }
    return count;
}

int next_prime(bool* primes_so_far,int current_prime,int size)
{
    for (int a=current_prime+1;a<size;a++)
    {
        if (primes_so_far[a]) return a;
    }
    return -2;
}

int prime_number_sieve(int* primes,int size)
{
    if (size<4) return -2;

    bool* sieve = new bool[size];

    for (int a=0;a<size;a++)
    sieve[a] = true;

    sieve[0] = false;
    sieve[1] = false;

    int a=2;
    do
    {
        int b = a;
        if (b*a < size) sieve[b*a] = false;
        do
        {
            b++;
            sieve[b*a] = false;
        } while (b*a < size);
        a = next_prime(sieve,a,size);
    } while (a*a < size);

    long prime_list_size = (long) count_primes(sieve,size);
    int* prime_list = new int[prime_list_size];

    for (int b=0,c=0;b<size;b++)
    {
        if (sieve[b]) prime_list[c++] = b;
    }   

    srand(time(NULL));
    int rand_int1 = rand();
    int rand_int2 = rand();
    long rand_num1 = (((long) rand_int1) * prime_list_size)/RAND_MAX;
    long rand_num2 = (((long) rand_int2) * prime_list_size)/RAND_MAX;

    primes[0] = prime_list[rand_num1];
    primes[1] = prime_list[rand_num2];

    delete[] sieve;
    delete[] prime_list;

    return 0;
}

int main()
{
    int* prime = new int[2];

    prime_number_sieve(prime,10000);

    cout << "\n";
    cout << prime[0];
    cout << "\n";
    cout << prime[1];
    cout << "\n";

    return 0;
}
上面的

valgrind错误:

==23738== Invalid write of size 1
==23738==    at 0x400A4B: prime_number_sieve(int*, int) (in /home/botch/Downloads/unsorted/bre_final/a.out)
==23738==    by 0x400C31: main (in /home/botch/Downloads/unsorted/bre_final/a.out)
==23738==  Address 0x5ab83e0 is 0 bytes after a block of size 10,000 alloc'd
==23738==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==23738==    by 0x4009CD: prime_number_sieve(int*, int) (in /home/botch/Downloads/unsorted/bre_final/a.out)
==23738==    by 0x400C31: main (in /home/botch/Downloads/unsorted/bre_final/a.out)
==23738== 

valgrind: m_mallocfree.c:303 (get_bszB_as_is): Assertion 'bszB_lo == bszB_hi' failed.
valgrind: Heap block lo/hi size mismatch: lo = 4063233, hi = 0.
This is probably caused by your program erroneously writing past the
end of a heap block and corrupting heap metadata.  If you fix any
invalid writes reported by Memcheck, this assertion failure will
probably go away.  Please try that before reporting this as a bug.

将尺寸更改为1,000时出错:

a.out: malloc.c:2392: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Aborted (core dumped)

修改

我非常感谢所有(实际)的帮助!感谢下面的答案,程序现在将运行大多数输入(当然不是大)但我仍然得到Valgrind错误。我并不介意那个,但我很想知道发生了什么。我尝试了下面的建议,将随机素数的选择机制改为(((long) rand_int1) % prime_list_size);而不是(((long) rand_int1) * prime_list_size)/RAND_MAX,但我在Valgrind方面没有明显的结果。我仍然无法确切地知道无效堆写入的来源。我将修改代码以查看是否是我的删除,并将报告回来。

2 个答案:

答案 0 :(得分:3)

循环

 do
    {
        b++;
        sieve[b*a] = false;
    } while (b*a < size);

在检查sieve[b*a]之前是否已分配给b*a < size。这允许在数组末尾之外分配元素。这是此循环的最后一次迭代的未定义行为。

最好使用std::vector<bool> [注意它有一些其他向量没有的限制]或std::bitset,而不是手动使用运算符new和{{ 1}}。请记住,仍然需要确保您的代码不会脱离标准容器的末尾。

答案 1 :(得分:1)

我可以在你的代码中看到两个问题。

第一个在这里:

   do
   {
       b++;
       sieve[b*a] = false;
   } while (b*a < size);

这不能阻止写入超出限制size,因为在分配筛子后发生检查 [b * a] = false;

   while (b*a < size)
   {
       sieve[b*a] = false;
       b++;
   }

我看到的另一个问题是:

  

long rand_num1 = (((long) rand_int1) * prime_list_size)/RAND_MAX;

     

long rand_num2 = (((long) rand_int2) * prime_list_size)/RAND_MAX;

乘法中的项可能导致溢出。我想你想得到一个小于size的随机索引?你可以这样做:

long rand_num1 = (((long) rand_int1) % prime_list_size);
long rand_num2 = (((long) rand_int2) % prime_list_size);

当然,这不会产生完美的均匀分布,但它会起作用。为了更好地分发,请查看std库的数字随机生成器及其vector,这对于您正在做的事情来说是一个非常好的工具。