Ошибка в ./thrash: free(): неверный указатель

avatar
user8768055
7 апреля 2018 в 21:57
375
1
1

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

char** pgs =(char**) malloc(sizeof(char *) * pages);
if(pgs == NULL){
    printf("Failed to allocate memory");
    exit(0);
}
int i;
for(i = 0; i < pages; i++){
    pgs[i] = malloc(4096);
    /*if(pgs[i] == NULL){
    printf("Failed to allocate memory");
    exit(0);
    }*/
*pgs[i] = "\0";
/*if(pgs[i] == NULL){
    printf("Failed to allocate memory");
    exit(0);
}*/
}

В середине программы к элементам этого массива обращаются и изменяют случайным образом, чтобы вызвать пробуксовку (как часть лабораторной работы):

while(time(NULL) - startTime < seconds){
    long rando = rand() % pages;
    if(modify > 0){
        *pgs[rando]++;
    }
    else{
        long temp = *pgs[rando];
    }

В конце программы я пытаюсь освободить эту память:

for(i = 0; i < pages; i++){
    free(pgs[i]);
}


free(pgs);

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

Источник
Jonathan Leffler
7 апреля 2018 в 22:01
1

Пожалуйста, вводите код точно. Кроме того, не включайте закомментированный код и не включайте две копии закомментированного кода. Это поможет легче понять, о чем вы спрашиваете.

Jonathan Leffler
7 апреля 2018 в 22:02
1

Вы не получаете предупреждения компилятора для *pgs[i] = "\0";? Не публикуйте код, на который жалуется компилятор, если только ваш вопрос не звучит так: «Почему компилятор жалуется на этот код?»

Jonathan Leffler
7 апреля 2018 в 22:06
0

Ваша проблема в том, что ваш модифицирующий код *pgs[rando]++; эквивалентен *(pgd[rando]++), а не (*pgs[rando])++;, как вы думали. Вы увеличиваете указатели, а не данные, на которые они указывают. Итак, когда вы освобождаете его, это не указатель, который был выделен, поэтому вы получаете сообщения.

Jonathan Leffler
7 апреля 2018 в 22:08
0

Также принято использовать exit(1); или exit(EXIT_FAILURE);, чтобы указать, что программа не удалась; exit(0); или exit(EXIT_SUCESS); указывает, что программа выполнена успешно. —— И обратите внимание, что ваш модифицирующий код изменяет только один байт из 4096 байтов, выделенных на страницу, поэтому на самом деле он не очень сильно перегружает управление памятью (по крайней мере, большинство кэшей кэшируют менее 4096 байт за раз).

Pablo
7 апреля 2018 в 22:54
0

*pgs[i] = "\0"; должно быть *pgs[i] = '\0'; Обратите внимание, что 'x' не совпадает с "x"

Ответы (1)

avatar
Jonathan Leffler
7 апреля 2018 в 22:53
1

В показанных вами фрагментах программы обнаружено множество проблем, некоторые из которых были указаны в комментариях:

  • Программы должны сообщать об ошибках при стандартной ошибке, а не при стандартном выводе.
  • Программы должны завершаться с ненулевым статусом в случае сбоя.
  • Программы должны компилироваться без предупреждений.
  • Сообщения в целом и сообщения об ошибках в частности должны заканчиваться новой строкой.
  • Программа пытается изменить только один байт каждой страницы.

Однако основная проблема заключается в том, что код в вопросе использует *pgs[rando]++, предназначенный для изменения выделенной памяти. Это эквивалентно *(pgs[rando]++), который увеличивает указатель, а затем считывает значение и отбрасывает его, а не эквивалентен (*pgs[rando])++, который изменяет байт pgs[rando][0]. Код в вопросе должен генерировать предупреждение о value computed is not used (или ошибку, если вы убедитесь, что компилируете все предупреждения как ошибки). Поскольку ваш код увеличивает указатели, значения, возвращаемые в систему выделения памяти с помощью free(), как правило, не совпадают с теми, которые возвращала вам система распределения памяти, поэтому вы действительно передаете недопустимые указатели на free().

Этот код позволяет избежать проблем, описанных выше. Он выполняет фиксированное количество итераций и не использует time(). Он печатает sum, чтобы оптимизатор не мог оптимизировать доступ для чтения к памяти.

/* SO 4971-2352 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum { PAGESIZE = 4096 };

int main(void)
{
    int pages = PAGESIZE;
    char **pgs = (char **)malloc(sizeof(char *) * pages);
    if (pgs == NULL)
    {
        fprintf(stderr, "Failed to allocate memory\n");
        exit(EXIT_FAILURE);
    }
    for (int i = 0; i < pages; i++)
    {
        pgs[i] = malloc(PAGESIZE);
        if (pgs[i] == NULL)
        {
            fprintf(stderr, "Failed to allocate memory\n");
            exit(EXIT_FAILURE);
        }
        memset(pgs[i], '\0', PAGESIZE);     // Or use calloc()!
    }

    size_t sum = 0;
    for (int i = 0; i < PAGESIZE * PAGESIZE; i++)
    {
        int pagenum = rand() % pages;
        int offset = rand() % PAGESIZE;
        int modify = i & 2;
        if (modify != 0)
        {
            pgs[pagenum][offset]++;
        }
        else
        {
            sum += pgs[pagenum][offset];
        }
    }

    printf("Sum: 0x%.8zX\n", sum);

    for (int i = 0; i < pages; i++)
        free(pgs[i]);
    free(pgs);

    return 0;
}

Я назвал этот код thrash31.c и скомпилировал его в thrash31, используя:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror thrash31.c -o thrash31
$

При запуске с программой синхронизации я получил вывод:

$ timecmd -u -- thrash31
2018-04-07 15:48:58.546809 [PID 9178] thrash31
Sum: 0x001FE976
2018-04-07 15:48:59.355508 [PID 9178; status 0x0000]  -  0.808699s
$

Итак, запуск занял около 0,8 секунды. Сумма, которую он генерирует, каждый раз одинакова, потому что код не задает генератор случайных чисел.

user8768055
11 апреля 2018 в 00:52
0

Хотел ответить раньше, спасибо за помощь. Этот код работает и решил мои проблемы.