C++ New/Delete 错误?

C++ New/Delete Error?

我目前正在用 C++ 实现质数筛,我设计的代码应该能够控制筛的大小(通过主函数中的整数文字),但我得到了一些奇怪的错误贯穿始终。我有几年没用过 C++了,但我记得使用 newdelete 运算符来管理堆内存。

我的问题如下...

该程序适用于某些筛网尺寸,但不适用于其他筛网尺寸,并且效果不是严格线性的。例如,如果我将筛子设置为 10,000,程序 运行 没问题,但如果我将其设置为 1,000,程序就会崩溃,并显示一条相当神秘(就我的经验而言)的消息,该消息总是引导我到有关 C malloc 断言错误的页面(通过 google)。此外,该程序在 10,000 个 删除语句的情况下工作正常,但如果我 运行 通过 valgrind 和另一个无用的(对我来说)消息,它将崩溃。结果,我什至无法在自己的 DX 上调试该死的东西

有什么妙语吗?

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

代码(带有 delete 语句;适用于大小 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 错误。我不太介意 that,但我很想知道发生了什么。我尝试了下面的建议,将随机素数的选择机制更改为 (((long) rand_int1) % prime_list_size); 而不是 (((long) rand_int1) * prime_list_size)/RAND_MAX,但我在 Valgrind 方面没有明显的结果。我仍然无法准确判断无效堆写入的来源。我将修改代码以查看是否是我的删除导致的并会报告回来。

循环

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

在检查 b*a < size 之前对 sieve[b*a] 进行赋值。这允许分配一个元素超过数组的末尾。这是此循环最后一次迭代的未定义行为。

你最好使用 std::vector<bool> [注意它有一些其他向量没有的限制] 或 std::bitset,而不是手动使用运算符 newdelete。请记住,仍然有必要确保您的代码不会从标准容器的末端掉落。

我在您的代码中发现了两个问题。

第一个来了:

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

这不能防止写入超出限制 size,因为检查发生在 赋值 `sieve[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,这对于您正在做的事情来说是一个非常好的工具。