Почему `realloc ()` не работает на некоторых загадочных входах? - PullRequest
0 голосов
/ 08 октября 2018

Я уже часами пытался выяснить, почему моя программа не работает на этом входе, но для меня все еще остается загадкой.Во-первых, вот соответствующие подробности, чтобы воспроизвести эту ошибку.

Используя файлы, перечисленные ниже в том же каталоге, скомпилируйте с gcc -O0 -g main.c ArrayList.c (примечание: gcc --version output 7.3.0).После этого запустите ./a.out $((10**9)).Вы должны получить следующую ошибку:

a.out: malloc.c:2868: mremap_chunk: Assertion `((size + offset) & (GLRO (dl_pagesize) - 1)) == 0' failed.
Aborted (core dumped)

Я уже пробовал отлаживать через это, и проблема, похоже, не в моем коде, т.е. ошибка, похоже, в коде realloc,но я честно не знаю.Если я использую ./a.out $((10**10)), программа не выйдет из строя, что для меня загадочно.Кажется, проблема заключается в следующей строке:

arraylist->data = realloc(arraylist->data, sizeof(uint64_t) * (arraylist->capacity));

Я прочитал справочные страницы для подсказок, правильно ли я вызываю realloc, но мне ничего не выпало.Все, что пытается сделать программа, - это просеять не простые числа меньше, чем sqrt (n), используя модифицированное сито эратосфена.Может кто-нибудь помочь мне?Спасибо!


main.c:

// main.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include "ArrayList.h"

// Segment addressing.
#define BYTE_IDX(i) i >> 4
#define BIT_IDX(i) (i >> 1) % 8

// Bit manipulation.
#define IS_PRIME(i) ~(S[BYTE_IDX(i)] >> BIT_IDX(i)) & 1U
#define SET_BIT(i) S[BYTE_IDX(i)] |= (1U << BIT_IDX(i))

uint64_t primepi(uint64_t n)
{
    uint64_t sqrtn = (uint64_t)sqrt((double)n);
    uint8_t *S = calloc((sqrtn + 1) / 16, sizeof(uint8_t));

    ArrayList arraylist;
    arraylist_init(&arraylist);

    for (uint64_t i = 3; i * i <= n; i += 2)
        if (IS_PRIME(i))
        {
            arraylist_append(&arraylist, i);
            for (uint64_t j = i * i; j * j <= n; j += 2 * i)
                SET_BIT(j);

        }

    free(S);
    arraylist_free(&arraylist);
    return (uint64_t)0;
}

int main(int argc, char **argv)
{
    uint64_t n = primepi(atoll(argv[1]));
    printf("n = %lu\n", n);
    return 0;
}

ArrayList.h:

/**
 * ArrayList.h
 *
 * Summary:
 *  Provides a specification of the ArrayList data structure.
 */

#define ARRAYLIST_INITIAL_CAPACITY 128

typedef struct {
    uint64_t size;
    uint64_t capacity;
    uint64_t *data;
} ArrayList;

void arraylist_init(ArrayList *arraylist);

void arraylist_append(ArrayList *arraylist, uint64_t value);

uint64_t arraylist_get(ArrayList *arraylist, uint64_t index);

void arraylist_double_capacity_if_full(ArrayList *arraylist);

void arraylist_free(ArrayList *arraylist);

ArrayList.c:

/**
 * ArrayList.c
 *
 * Summary:
 *  Provides an implementation of the ArrayList data structure.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "ArrayList.h"

void arraylist_init(ArrayList *arraylist)
{
    // Initialize size and capacity.
    arraylist->size = (uint64_t)0;
    arraylist->capacity = ARRAYLIST_INITIAL_CAPACITY;

    // Allocate memory of the arraylist->data.
    arraylist->data = calloc(arraylist->capacity, sizeof(uint64_t));
}

void arraylist_append(ArrayList *arraylist, uint64_t value)
{
    // Double ArrayList if it is full.
    arraylist_double_capacity_if_full(arraylist);

    // Append the value and increment the size.
    arraylist->data[arraylist->size++] = value;
}

uint64_t arraylist_get(ArrayList *arraylist, uint64_t index)
{
    if (index >= arraylist->size || index < (uint64_t)0)
    {
        printf("Index %lu out of bounds for ArrayList of size %lu\n", index, arraylist->size);
        exit(1);
    }
    return arraylist->data[index];
}

void arraylist_double_capacity_if_full(ArrayList *arraylist)
{
    if (arraylist->size >= arraylist->capacity)
    {
        arraylist->capacity *= (uint64_t)2;
        arraylist->data = realloc(arraylist->data, sizeof(uint64_t) * (arraylist->capacity));
    }
}

void arraylist_free(ArrayList *arraylist)
{
    free(arraylist->data);
}

Редактировать:

Выход из режима работы valgrind --tool=memcheck ./a.out $((10**9)):

==31666== Memcheck, a memory error detector
==31666== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31666== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31666== Command: ./a.out 1000000000
==31666== 
==31666== Invalid read of size 1
==31666==    at 0x1089E7: primepi (main.c:29)
==31666==    by 0x108AA7: main (main.c:40)
==31666==  Address 0x55cb7f8 is 0 bytes after a block of size 1,976 alloc'd
==31666==    at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==31666==    by 0x108952: primepi (main.c:19)
==31666==    by 0x108AA7: main (main.c:40)
==31666== 
==31666== Invalid write of size 1
==31666==    at 0x108A17: primepi (main.c:29)
==31666==    by 0x108AA7: main (main.c:40)
==31666==  Address 0x55cb7f8 is 0 bytes after a block of size 1,976 alloc'd
==31666==    at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==31666==    by 0x108952: primepi (main.c:19)
==31666==    by 0x108AA7: main (main.c:40)
==31666== 
==31666== Invalid read of size 1
==31666==    at 0x108982: primepi (main.c:25)
==31666==    by 0x108AA7: main (main.c:40)
==31666==  Address 0x55cb7f8 is 0 bytes after a block of size 1,976 alloc'd
==31666==    at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==31666==    by 0x108952: primepi (main.c:19)
==31666==    by 0x108AA7: main (main.c:40)
==31666== 
n = 0
==31666== 
==31666== HEAP SUMMARY:
==31666==     in use at exit: 0 bytes in 0 blocks
==31666==   total heap usage: 8 allocs, 8 frees, 70,584 bytes allocated
==31666== 
==31666== All heap blocks were freed -- no leaks are possible
==31666==
==31666== For counts of detected and suppressed errors, rerun with: -v
==31666== ERROR SUMMARY: 9 errors from 3 contexts (suppressed: 0 from 0)

1 Ответ

0 голосов
/ 08 октября 2018

Проблема заключается в вашем макросе SET_BIT:

#define SET_BIT(i) S[BYTE_IDX(i)] |= (1U << BIT_IDX(i))

или, возможно, в вашем макросе BYTE_IDX:

#define BYTE_IDX(i) i >> 4

или, возможно, в этом цикле:

for (uint64_t j = i * i; j * j <= n; j += 2 * i)
            SET_BIT(j);

он выходит за пределы массива S.

Когда:

  • argv [1] = "1000000000"
  • n = 1000000000
  • sqrtn = 31622
  • DYNAMIC_ARRAY_SIZE (S) = (sqrtn + 1) / 16 * sizoef (uint8_t) = 1976

, максимальный индекс для S - 1975. Настройка макроса SET_BITto:

#define SET_BIT(i)  do{ \
    size_t _a = BYTE_IDX(i); \
    if (_a > 1975)  \
        fprintf(stderr, "Setting byte %ld\n", _a); \
    S[_a] |= (1U << BIT_IDX(i)); \
}while(0)

, которые мы можем видеть в выводе:

Setting byte 1976 

Вы записываете вне пределов для массива S, который перезаписывает * alloc data - он выбрасывает утверждение.

Живой код доступен на onlinedbg .

К вашему коду:

  • Использование любого аргумента вне макроса плохо.Если вы используете S внутри макроса, передайте его в качестве аргумента, возможно, с аргументом размера, таким образом вы можете написать утверждение.Вы только что обнаружили, почему макросы IS_PRIME и SET_BIT плохие.Мне очень нравится ваша идея индексации битовых полей очень очень очень - просто напишите соответствующую библиотеку для этого, как для arraylist.
  • size_t - это тип для хранения размеров.Используйте size_t для ArrayList.size и ArrayList.capacity.Используйте uint64_t arraylist_get(ArrayList *arraylist, size_t index);.Также было бы неплохо, если вы работаете с большими числами, чтобы проверить переполнение умножения, выражения arraylist->capacity *= (uint64_t)2; и sizeof(uint64_t) * (arraylist->capacity) могут переполниться.
...