【问题标题】:C - BitArray Segmentation FaultC - BitArray 分段错误
【发布时间】:2016-11-01 05:50:31
【问题描述】:

我目前正在尝试使用 BitSet 在 C 中实现 Eratosthenes 筛选,但是当我尝试筛选高达 1,000,000(100 万)的素数时出现分段错误 - 100,000(100000)仍在工作不过,我不知道为什么会出现段错误。

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

#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);
}

有人知道错误发生的原因吗?我猜现在分配了足够的空间(不知何故),但我现在看不出这怎么可能......

【问题讨论】:

  • 在您的代码中哪里获得 SIGSEGV?
  • @AndrewHenle 我写了一条评论。
  • 您说您使用的是 bit 数组,但实际上您似乎使用的是 byte 数组。可能是。您的代码有点难以理解。如果您确实打算使用位数组,那么最好将您的测试和设置操作分解为宏。
  • prime * prime 显然是比upperLimit 更大的值,就这么简单?使用调试器并检查。
  • 我打赌prime * prime 会溢出 64 位。

标签: c segmentation-fault dynamic-memory-allocation bit-fields sieve-of-eratosthenes


【解决方案1】:

你没有得到正确的位数:

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

做除法和取模时,需要除/取bits的个数,而不是bytes的个数:

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 代替 8。无论sizeof(uint64_t) 计算结果如何都无关紧要,因为您仍然可以获得所需的正确位数。

【讨论】:

  • @Matthias,不,您分配的字节数比您需要的多出 CHAR_BIT(通常为 8)倍。
  • @JohnBollinger 非常感谢!
  • @Lundin 你知道一行可读的代码来做一些事情吗(不浪费 8 倍内存)?
  • @JohnBollinger 快速问题:在 CHAR_BIT 不是 8 的奇异机器上会发生什么? sizeof(uint64_t) 返回什么?因为它不能返回像 8 这样的偶数......更好的分配方式是什么(number/(sizeof(prime_t)*8)#define BITS_IN_PRIME_T 64 然后number/BITS_IN_PRIME_T
  • sizeof(uint64_t) 会返回什么CHAR_BIT != 8?
【解决方案2】:

您用calloc 分配X 个字节,将总数除以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)

【讨论】:

  • 因为我只需要每个素数一位而不是整个prime_t
  • 您当前的实现使用 1 个字节,而不是一位。
  • 它在哪里使用一个字节?我用sieve[number/sizeof(prime_t)] |= (((prime_t) 1) &lt;&lt; (number % sizeof(prime_t))); 设置单个位
  • sizeof 返回字节,而不是位
  • 好的,所以在每个字节中您设置了 1 位,是的。索引时除以sizeof(prime_t) 而不是(sizeof(prime_t) * 8)
猜你喜欢
  • 2017-08-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多