free и двумерный динамический массив (экономим память)

Модератор: Модераторы разделов

Аватара пользователя
ЭЖД
Сообщения: 332
Статус: openSuSE Member
ОС: openSuSE

free и двумерный динамический массив

Сообщение ЭЖД »

начал делать задачку № 5.13 из незабвенной книги K&R и столкнулся с проблемой аварийного завершения программы при обращении к функции free()
собственно код программы:

Код:

/* * 5.13.c * * Created on: 24.10.2009 * Author: egd */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAXLINE 100 #define FREE_MEM \ {\ for (i = n - j; i > 0; --i)\ free(mass[i]);\ free(mass);\ exit(EXIT_FAILURE);\ } int main(int argc, char *argv[]) { int n, i, j = -1; char **mass, *del, buff[MAXLINE + 1]; if (argc != 2) n = 10; else if (argv[1][0] == '-' && isdigit(argv[1][1])) n = atoi(&argv[1][1]); if ((mass = malloc(n * sizeof(char *))) == NULL) { perror("malloc to mass"); exit(EXIT_FAILURE); } memset(mass, 0, sizeof(mass)); do { if ((buff = fgets(buff, MAXLINE, stdin)) == NULL && ferror(stdin)) { perror("fgets"); FREE_MEM } else ++j; if (j == n) { free(mass[0]); for (i = 0; i < n - 1; ++i) mass[i] = mass[i + 1]; j = n - 1; } if ((mass[j] = malloc(strlen(buff) * sizeof(char))) == NULL) { perror("calloc"); FREE_MEM } if (buff) strcpy(mass[j], buff); } while (!feof(stdin)); for (i = 0; i < n; ++i) { printf("%s", mass[i]); free(mass[i]); } free(mass); exit(EXIT_SUCCESS); }

в кратце что она делает:
читает из stdin построчно и запносит в массив из argv[1] строк или 10 по умолчанию.
нужно вывести последние argv[1] строк (примерно тоже самое делает программа tail).
для экономии памяти, как и было написано в задании, я для каждой строки использовал минимум памяти.
если мы считали больше строк, чем может поместится в массиве - самую первую строку выталкиваем и сдвигаем все строки вверх, на освободившееся место помещаем новую.
и так далее пока не встретим EOF.
выводим что у нас получилось.

но при удалении первой строки получаем аварийное завершение с бектрейсом следующего содержания:
> ./5.13 -3 < ../5.13.c
*** glibc detected *** ./5.13: free(): invalid pointer: 0x0804b020 ***
======= Backtrace: =========
/lib/libc.so.6[0xb779e50b]
/lib/libc.so.6(cfree+0xd9)[0xb77a3049]
./5.13[0x804892c]
/lib/libc.so.6(__libc_start_main+0xfe)[0xb7747ace]
./5.13[0x8048681]
======= Memory map: ========
...
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
Аварийный останов


а вот работающий код. отличие лишь в том, что для строк сразу задаем всю память, причем фиксированного размера MAXLINE

Код:

#include <stdio.h> #include <stdlib.h> #define MAXLINE 100 int main() { int n = 3; // Выделяем память char **buffers = malloc(sizeof(char*) * n); int i=0; for(i=0; i < n; ++i) { buffers[i] = malloc(sizeof(char)*MAXLINE); } // Читаем строки for(i=0; i < n; ++i) { fgets(buffers[i], MAXLINE, stdin); } // Выводим строки for(i=0; i < n; ++i) { printf("%s", buffers[i]); } // Освобождаем память обратно for(i=0; i < n; ++i) { free(buffers[i]); } free(buffers); }
«Когда истинный гений появляется в этом низком мире, его можно узнать по тому знаку, что все глупцы объединяются против него»
Спасибо сказали:
Аватара пользователя
ЭЖД
Сообщения: 332
Статус: openSuSE Member
ОС: openSuSE

Re: free и двумерный динамический массив

Сообщение ЭЖД »

полагаясь на второй, работоспособный вариант, переписал код. но это же очень расточительно! есть идея использовать связанный список, но задача поставлена на использовании массивов.

Код:

/* * 5.13.c * * Created on: 24.10.2009 * Author: egd */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAXLINE 100 #define FREE_MEM \ {\ for (; n > 0; --n)\ free(mass[n]);\ free(mass);\ exit(EXIT_FAILURE);\ } int main(int argc, char *argv[]) { int n = 10, i, j; char **mass, *buff = malloc(MAXLINE * sizeof(char)); // 99 chars + '\0' if (argc == 2 && argv[1][0] == '-' && isdigit(argv[1][1])) if ((n = atoi(&argv[1][1])) == 0) exit(EXIT_SUCCESS); if ((mass = malloc(n * sizeof(char *))) == NULL) { perror("malloc to mass"); exit(EXIT_FAILURE); } memset(mass, 0, sizeof(mass)); for (j = 0; j < n; ++j) if ((mass[j] = malloc(MAXLINE * sizeof(char))) == NULL) { perror("calloc"); FREE_MEM } j = 0; do { if ((buff = fgets(buff, MAXLINE - 1, stdin)) == NULL && ferror(stdin)) { // 99 chars + '\0' perror("fgets"); FREE_MEM } if (++j == n) { if (buff) { for (i = 0; i != n - 1; ++i) strcpy(mass[i], mass[i + 1]); strcpy(mass[i], buff); j = n - 1; } else break; } else { strcpy(mass[j - 1], buff); } } while (!feof(stdin)); for (j = 0; j < n; ++j) { printf("%s", mass[j]); free(mass[j]); } free(mass); exit(EXIT_SUCCESS); }
«Когда истинный гений появляется в этом низком мире, его можно узнать по тому знаку, что все глупцы объединяются против него»
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: free и двумерный динамический массив

Сообщение NickLion »

Вот, с коментариями:

Код: Выделить всё

/*
* 5.13.c
*
*  Created on: 24.10.2009
*      Author: egd
*/

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

#define MAXLINE 100
#define FREE_MEM \
    {\
    for (i = n - j; i > 0; --i)\
    free(mass[i]);\
    free(mass);\
    exit(EXIT_FAILURE);\
    }

int main(int argc, char *argv[]) {
    int n, i, j = -1;
    char **mass, *del, buff[MAXLINE + 1];

    if (argc != 2)
        n = 10;
    else if (argv[1][0] == '-' && isdigit(argv[1][1]))
        n = atoi(&argv[1][1]);
    if ((mass = malloc(n * sizeof(char *))) == NULL) {
        perror("malloc to mass");
        exit(EXIT_FAILURE);
    }
    memset(mass, 0, sizeof(mass[0]) * n); // корректное обнуление массива
    do {
        *buff = 0; // для последней строчки
        // а зачем присваивание? с ним просто не компилится, ведь в константный указатель пытаемся запихнуть другой указатель
        if ((/*buff = */fgets(buff, MAXLINE, stdin)) == NULL && ferror(stdin)) {
            perror("fgets");
            FREE_MEM
        } else
            ++j;
        if (j == n) {
            free(mass[0]);
            for (i = 0; i < n - 1; ++i)
                mass[i] = mass[i + 1];
            j = n - 1;
        }
        // а вот здесь и ошибка была - место под завершающий нулевой символ забыли, ай-ай-ай :)
        if ((mass[j] = malloc(( strlen(buff) + 1 ) * sizeof(char))) == NULL) {
            perror("calloc");
            FREE_MEM
        }
        //if (buff) // а зачем? buff - всегда не NULL
            strcpy(mass[j], buff);
    } while (!feof(stdin));

    for (i = 0; i < n; ++i) {
        printf("%s", mass[i]);
        free(mass[i]);
    }
    printf("\n"); // для последней строчки
    free(mass);
    exit(EXIT_SUCCESS);
}
Спасибо сказали:
Аватара пользователя
ЭЖД
Сообщения: 332
Статус: openSuSE Member
ОС: openSuSE

Re: free и двумерный динамический массив

Сообщение ЭЖД »

NickLion
// корректное обнуление массива

возможно вы имели ввиду... memset(mass, 0, sizeof(&mass[0]) * n);
// а зачем присваивание? с ним просто не компилится, ведь в константный указатель пытаемся запихнуть другой указатель

я ошибся, нужно char *buff=malloc((MAXLINE + 1)*sizeof(char));
тогда присваивание от fgets работает. это нужно для корректной работы когда мы сдвигаем элементы в массиве mass и записывает туда новый.

// а вот здесь и ошибка была - место под завершающий нулевой символ забыли, ай-ай-ай :)

каюсь, но sizeof погоды не меняет.
«Когда истинный гений появляется в этом низком мире, его можно узнать по тому знаку, что все глупцы объединяются против него»
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: free и двумерный динамический массив

Сообщение NickLion »

ЭЖД писал(а):
26.10.2009 13:17
я ошибся, нужно char *buff=malloc((MAXLINE + 1)*sizeof(char));
тогда присваивание от fgets работает. это нужно для корректной работы когда мы сдвигаем элементы в массиве mass и записывает туда новый.
не, зачем... Вы же копируете содержимое через strcpy, не нужно больше ничего.

ЭЖД писал(а):
26.10.2009 13:17
// а вот здесь и ошибка была - место под завершающий нулевой символ забыли, ай-ай-ай :)

каюсь, но sizeof погоды не меняет.

у Вас было strlen(buff) * sizeof(char), а я сделал ( strlen(buff) + 1 ) * sizeof(char). А sizeof... ну пусть будет, на всякий случай.
Спасибо сказали:
Аватара пользователя
ЭЖД
Сообщения: 332
Статус: openSuSE Member
ОС: openSuSE

Re: free и двумерный динамический массив

Сообщение ЭЖД »

NickLion
все учел, код подчистил, но все равно вываливается на free()

Код:

/* * 5.13.c * * Created on: 26.10.2009 * Author: egd */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAXLINE 100 #define FREE_MEM \ {\ for (i = n - j; i > 0; --i)\ free(mass[i]);\ free(mass);\ exit(EXIT_FAILURE);\ } int main(int argc, char *argv[]) { int n, i, j = -1; char **mass, buff[MAXLINE + 1]; if (argc == 2 && argv[1][0] == '-' && isdigit(argv[1][1])) if ((n = atoi(&argv[1][1])) == 0) exit(EXIT_SUCCESS); if ((mass = malloc(n * sizeof(char *))) == NULL) { perror("malloc to mass"); exit(EXIT_FAILURE); } memset(mass, 0, sizeof(mass)); do { if (fgets(buff, MAXLINE - 1, stdin) == NULL && ferror(stdin)) { perror("fgets"); FREE_MEM if (++j == n) { free(mass[0]); for (i = 0; i < n - 1; ++i) mass[i] = mass[i + 1]; j = n - 1; } if ((mass[j] = malloc(sizeof(buff) * sizeof(char))) == NULL) { perror("calloc"); FREE_MEM } strcpy(mass[j], buff); } } while (!feof(stdin)); for (i = 0; i < n; ++i) { printf("%s", mass[i]); free(mass[i]); } free(mass); exit(EXIT_SUCCESS); }
«Когда истинный гений появляется в этом низком мире, его можно узнать по тому знаку, что все глупцы объединяются против него»
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: free и двумерный динамический массив

Сообщение NickLion »

Ой, что-то последний Ваш код - совсем не то...

PS ведь в том коде, что я привел - все работает. и поменял всего пару строчек, отталкивайтесь от него.
Спасибо сказали:
alexotmp
Сообщения: 96
ОС: OpenSUSE 11.2

Re: free и двумерный динамический массив

Сообщение alexotmp »

ЭЖД писал(а):
25.10.2009 18:11
начал делать задачку № 5.13 из незабвенной книги K&R и столкнулся с проблемой аварийного завершения программы при обращении к функции free()
собственно код программы:

Код:

/* * 5.13.c * * Created on: 24.10.2009 * Author: egd */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAXLINE 100 #define FREE_MEM \ {\ for (i = n - j; i > 0; --i)\ free(mass[i]);\ free(mass);\ exit(EXIT_FAILURE);\ } int main(int argc, char *argv[]) { int n, i, j = -1; char **mass, *del, buff[MAXLINE + 1]; if (argc != 2) n = 10; else if (argv[1][0] == '-' && isdigit(argv[1][1])) n = atoi(&argv[1][1]); if ((mass = malloc(n * sizeof(char *))) == NULL) { perror("malloc to mass"); exit(EXIT_FAILURE); } memset(mass, 0, sizeof(mass)); do { if ((buff = fgets(buff, MAXLINE, stdin)) == NULL && ferror(stdin)) { perror("fgets"); FREE_MEM } else ++j; if (j == n) { free(mass[0]); for (i = 0; i < n - 1; ++i) mass[i] = mass[i + 1]; j = n - 1; } if ((mass[j] = malloc(strlen(buff) * sizeof(char))) == NULL) { perror("calloc"); FREE_MEM } if (buff) strcpy(mass[j], buff); } while (!feof(stdin)); for (i = 0; i < n; ++i) { printf("%s", mass[i]); free(mass[i]); } free(mass); exit(EXIT_SUCCESS); }


запусти эту прогу вот так:
MALLOC_CHECK_=0 ./a.out
у меня работает.
man 3 free
Спасибо сказали:
Аватара пользователя
ЭЖД
Сообщения: 332
Статус: openSuSE Member
ОС: openSuSE

Re: free и двумерный динамический массив

Сообщение ЭЖД »

NickLion
пытаюсь понять, почему работает/неработает
«Когда истинный гений появляется в этом низком мире, его можно узнать по тому знаку, что все глупцы объединяются против него»
Спасибо сказали:
NickLion
Сообщения: 3408
Статус: аватар-невидимка
ОС: openSUSE Tumbleweed x86_64

Re: free и двумерный динамический массив

Сообщение NickLion »

alexotmp писал(а):
26.10.2009 17:55
запусти эту прогу вот так:
MALLOC_CHECK_=0 ./a.out
у меня работает.
man 3 free

Плохой совет. Это просто игнорирование ошибок (таких как выход за пределы динамической памяти, разрушение кучи). Это не решение проблемы.
Спасибо сказали: