C - BitArray 分段错误

C - BitArray Segmentation Fault

我目前正在尝试使用 BitSet 在 C 中实现埃拉托色尼筛法,但是当我尝试筛选高达 1,000,000(100 万)- 100,000(100,000)个素数时出现分段错误虽然仍在工作,但我不明白为什么会出现段错误。

这是我使用的代码(我标记了发生错误的行):

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>

void eSieve(uint64_t upperLimit);
int main(int argc, char *argv[]) {
  uint64_t upperLimit;

  if (argc == 2) {
    upperLimit = (uint64_t) atoll(argv[1]);
    printf("Using custom limit: %" PRIu64 "\n", upperLimit);
  } else {
    upperLimit = 1000;
    printf("Using default limit: %" PRIu64 "\n", upperLimit);
  }

  eSieve(upperLimit);

  return 0;
}

typedef uint32_t prime_t;

void eSieve(uint64_t upperLimit) {
  if (upperLimit < 2) {
    printf("FAILURE: Bad upper limit.\n");
    return;
  }

  prime_t *sieve = calloc(1, (upperLimit + sizeof(prime_t) - 1)/sizeof(prime_t));

  if (!sieve) {
    printf("FAILURE: Could not initialize sieve.\n");
    return;
  }

  sieve[0] |= 3;    // Set first and second bit (representing 0 and 1)

  uint64_t prime, number;
  for (prime = 2; prime * prime < upperLimit; ) {
    for (number = prime * prime; number < upperLimit; number += prime) {
      // Segmentation fault for prime = 2 and number = 258048
      sieve[number/sizeof(prime_t)] |= (((prime_t) 1) << (number % sizeof(prime_t)));
    }

    while ((sieve[++prime/sizeof(prime_t)] & (prime_t)1 << (prime % sizeof(prime_t))) != 0)
      ;
  }

  number = upperLimit;
  while ((sieve[--number/sizeof(prime_t)] & (((prime_t)1) << (number % sizeof(prime_t)))) != 0)
    ;

  printf("Greatest prime-number below %" PRIu64 ": %" PRIu64 "\n", 
      upperLimit, number);
}

有人知道为什么会出现这个错误吗?我猜现在已经分配了足够的 space(不知何故),但我现在看不出这是怎么可能的...

你用 calloc 分配 X bytes,将总数除以 sizeof(prime_t),但表现得好像你有空间给 X prime_t 稍后的元素。

编辑:或者实际上,您正在分配一个大小为 X 的 1 元素数组。

如果你想像现在这样使用它,你应该这样做:

calloc(X, sizeof(prime_t)) 代替。

编辑:代码中的另一个主要问题是您使用的是字节级索引而不是位级索引。

请注意 prime_t 中有 sizeof(prime_t) * 8 位,因此在每个字节中您都设置了 1 位,为真。索引时除以 sizeof(prime_t) 而不是 (sizeof(prime_t) * 8)

您没有得到正确的位数:

sieve[number/sizeof(prime_t)] |= (((prime_t) 1) << (number % sizeof(prime_t)));

做除法和mod时,需要divide/mod位数,而不是位数字节:

sieve[number/(sizeof(prime_t)*8)] |= (((prime_t) 1) << (number % (sizeof(prime_t)*8)));

同样地:

while ((sieve[++prime/(sizeof(prime_t)*8)] & (prime_t)1 << (prime % (sizeof(prime_t)*8))) != 0)

...

while ((sieve[--number/(sizeof(prime_t)*8)] & (((prime_t)1) << (number % (sizeof(prime_t)*8)))) != 0)

编辑:

您也没有分配正确的内存量。您需要的字节数等于限制 除以位数 ,加上 1 sizeof(prime_t) 以向上舍入。

prime_t *sieve = calloc(1, (upperLimit / 8) + sizeof(prime_t));

就像现在一样,您分配的字节数是您需要的两倍。

另外,如果你想防范一个字节多于或少于 8 位的情况,请在上面的代码中使用 CHAR_BIT 代替 8sizeof(uint64_t) 的计算结果无关紧要,因为您仍然可以获得所需的正确位数。