malloc и бесплатная проблема - PullRequest
4 голосов
/ 02 января 2012

Итак, у меня есть этот кусок кода, я запускаю его пару раз:

void svnViewStatus()
{
    FILE *file;
    int i, j, k, q=0, ok;
    char mystring[100000], *vect[100000], *var, last[12], first[12];
    file = fopen("db_svnViewStatus.txt", "r");
    if(file == NULL)
    {
        printf("error");
    }
    else
    {
        i=1;
        while(fgets(mystring, 100000, file))
        {
            if( i > 1) 
            {
                j=1;
                var = strtok(mystring, ";");
                while(var != NULL)
                {
                    if(j==3)
                    {
                        if(i == 2)
                        {
                            strcpy(first, var);
                        }
                        strcpy(last, var);
                    }
                    if(j == 2)
                    {
                        if(q != 0)
                        {
                            ok=1;
                            for(k=0; k<q; k=k+2)
                            {
                                if(strcmp(vect[k], var) == 0)
                                {
                                    *vect[k+1]++;
                                    ok=0;
                                }
                            }
                            if(ok == 1)
                            {
                                vect[q] = malloc(strlen(var)+1);
                                strcpy(vect[q], var);
                                vect[q+1] = malloc(sizeof(int));
                                *vect[q+1] = 1;
                                q = q+2;
                            }
                        }
                        else
                        {
                            vect[q] = malloc(strlen(var)+1);
                            strcpy(vect[q], var);
                            vect[q+1] = malloc(sizeof(int));
                            *vect[q+1] = 1;
                            q = q+2;
                        }
                    }
                    j++;
                    var = strtok(NULL, ";");
                }
            }
            i++;
        }
    }
    fclose(file);
    printf("nr: %d \n", i-2);
    printf("first: %s \n", first);
    printf("last: %s \n", last);
    for(i=0; i<q; i = i+2)
    {
        printf("User %s: %d \n", *vect[i], *vect[i+1]);
    }
    for(i=0; i<q; i=i+1)
    {
         free(vect[i]);
    }

}

и в db_svnViewStatus.db у меня есть:

NumeRepo:CitateWoodyAllen;DataCreat:12 Nov 2011;Detinator:Ioana;Descriere:Citate ale faimosului regizor Woody Allen
1;Ioana;12 Nov 2011;Woody Allen;What if everything is an illusion and nothing exists? In that case, I definitely overpaid for my carpet.
2;Mihai;12 Nov 2011;Woody Allen;The lion and the calf shall lie down together but the calf won't get much sleep
3;Mihai;13 Nov 2011;Woody Allen;Eighty percent of success is showing up
4;Cristi;23 Nov 2011;Woody Allen;It is impossible to travel faster than the speed of light, and certainly not desirable, as one's hat keeps blowing off
5;Ioana;25 Nov 2011;Woody Allen;I had a terrible education. I attended a school for emotionally disturbed teachers.
6;Cristi;25 Nov 2011;Woody Allen;I will not eat oysters. I want my food dead. Not sick. Not wounded. Dead.

Но я получаю это:

"ОБНАРУЖЕНА КОРРУПЦИЯ КАРТЫ: после блока Normal (# 54) в 0x000032E90. CRT обнаружил, что приложение записало в память после завершения буфера кучи."

Любая помощь?

Кроме того, я должен использовать свободный после того, как я выделю память?Зачем?

Ответы [ 6 ]

6 голосов
/ 02 января 2012

Вы должны выделить память для завершения нуля, например:

vect[q] = malloc(strlen(var)+1);

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

4 голосов
/ 02 января 2012

Есть 2 проблемы. Во-первых, вы не выделяете достаточно памяти для строки. Вам нужен дополнительный байт для нулевого терминатора.

Другой способ заключается в том, что вы устанавливаете vect [q + 1] для указателя, затем вы перезаписываете этот указатель значением 1. Когда вы пытаетесь освободить его, вы пытаетесь освободить память в памяти. Местоположение 1. Вам нужно изменить:

vect[q+1] = 1;

до

*(vect [ q + 1 ]) = 1;
1 голос
/ 02 января 2012

Этот код корректно компилируется и работает под valgrind.

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

static void svnViewStatus(void)
{
    FILE *file;
    int i, j, k, q=0, ok;
    char mystring[100000], *vect[100000], *var, last[12], first[12];
    file = fopen("db_svnViewStatus.txt", "r");
    if (file == NULL)
    {
        printf("error");
        return;
    }

    for (i = 1; fgets(mystring, sizeof(mystring), file) != 0; i++)
    {
        if (i == 1) /* Skip heading */
            continue;
        char *space = mystring;
        for (j = 1; (var = strtok(space, ";")) != NULL; space = NULL, j++)
        {
            if (j == 3)
            {
                if (i == 2)
                {
                    strcpy(first, var);
                }
                strcpy(last, var);
            }
            if (j == 2)
            {
                if (q != 0)
                {
                    ok = 1;
                    for (k = 0; k<q; k += 2)
                    {
                        if (strcmp(vect[k], var) == 0)
                        {
                            (*vect[k+1])++;
                            ok=0;
                        }
                    }
                    if (ok == 1)
                    {
                        vect[q] = malloc(strlen(var)+1);
                        strcpy(vect[q], var);
                        vect[q+1] = malloc(sizeof(int));
                        *vect[q+1] = 1;
                        q += 2;
                    }
                }
                else
                {
                    vect[q] = malloc(strlen(var)+1);
                    strcpy(vect[q], var);
                    vect[q+1] = malloc(sizeof(int));
                    *vect[q+1] = 1;
                    q += 2;
                }
            }
        }
    }

    fclose(file);
    printf("nr: %d \n", i-2);
    printf("first: %s \n", first);
    printf("last: %s \n", last);
    for (i=0; i<q; i = i+2)
    {
        printf("User %s: %d \n", vect[i], *vect[i+1]);
    }
    for (i=0; i<q; i=i+1)
    {
        free(vect[i]);
    }
}

int main(void)
{
    svnViewStatus();
    return 0;
}

Имеет несколько уровней меньше отступов, чем оригинал. Код, идущий с правой стороны экрана, чаще всего указывает на наличие проблем. Как отмечено в комментариях, основная проблема заключалась в выражении, которое предназначалось для увеличения целого числа посредством указателя, но фактически увеличивало указатель, а не целое число. Есть пара петель for, заменяющих петли while, так что элемент управления цикла находится в верхней части цикла. Зачастую таким способом легче управлять циклом.

Я все еще недоволен кодом. Вы выделяете int и обрабатываете char * как int *, что означает, что на 64-битной машине у вас есть 8-байтовый указатель, указывающий на 4 байта данных (и два вызова malloc()). Если вы использовали структуру, такую ​​как:

struct data
{
    char *string;
    int   count;
};

struct data vect[5000];

При этом вы только выделите (продублируете) строку в следующем элементе структуры. Это было бы более компактно, и там меньше опасности ошибиться. (Вы написали бы vect[i].count++;, и он сделал бы то, что вы хотите, без суеты, без суеты.) И вам не нужно было бы возиться с q += 2; (или q = q + 2;).

Этот код лучше, используя структуру. Он также проверяет распределение памяти и гарантирует, что имена, скопированные в first и last, не переполняются (и обнуляются). Он по-прежнему не выполняет проверку границ массива vect, чтобы убедиться, что он не перезаписывает конец. Если при чтении файла произошла ошибка, произошла утечка памяти; очистка потребует некоторого внимания (и функции для этого).

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

struct data
{
    char *string;
    int   count;
};

static void svnViewStatus(void)
{
    FILE *file;
    int i, q = 0;
    char mystring[100000], last[12], first[12];
    struct data vect[50000];
    file = fopen("db_svnViewStatus.txt", "r");
    if (file == NULL)
    {
        printf("file open error\n");
        return;
    }

    for (i = 1; fgets(mystring, sizeof(mystring), file) != 0; i++)
    {
        if (i == 1) /* Skip heading */
            continue;
        char *space = mystring;
        char *var;
        for (int j = 1; (var = strtok(space, ";")) != NULL; space = NULL, j++)
        {
            if (j == 3)
            {
                if (i == 2)
                {
                    strncpy(first, var, sizeof(first)-1);
                    first[sizeof(first)-1] = '\0';
                }
                strncpy(last, var, sizeof(last)-1);
                last[sizeof(last)-1] = '\0';
            }
            if (j == 2)
            {
                int found = 0;
                for (int k = 0; k < q; k++)
                {
                    if (strcmp(vect[k].string, var) == 0)
                    {
                        vect[k].count++;
                        found = 1;
                    }
                }
                if (found == 0)
                {
                    vect[q].string = strdup(var);
                    if (vect[q].string == 0)
                    {
                        printf("Memory allocation error\n");
                        return;
                    }
                    vect[q].count = 1;
                    q++;
                }
            }
        }
    }

    fclose(file);

    printf("nr: %d\n", i-1);
    printf("first: %s\n", first);
    printf("last: %s\n", last);
    for (i = 0; i < q; i++)
    {
        printf("User %s: %d\n", vect[i].string, vect[i].count);
    }

    for (i = 0; i < q; i++)
    {
        free(vect[i].string);
    }
}

int main(void)
{
    svnViewStatus();
    return 0;
}
1 голос
/ 02 января 2012

Вы не выделяете здесь достаточно памяти:

vect[q] = malloc(strlen(var));

strlen(3) сообщает только о длине содержимого строки и пропускает завершающий символ ASCII NUL, который C использует для завершения строк. Каждый раз, когда вы видите: malloc(strlen(foo)); это почти наверняка ошибка . Всегда пишите malloc(strlen(foo) + 1);. Сделайте это + 1 очевидным, потому что каждый раз, когда вы не видите, что + 1, вы, вероятно, находите ошибку.

strcpy(vect[q], var);

Поскольку vect[q] не имеет достаточно памяти для этой памяти, эта строка перезаписала несвязанный байт с символом ASCII NUL.

Что касается free(3) памяти, большинство программистов считают очень полезным писать malloc(3) и free(3) вызовы одновременно с одинаковой регистрацией исходного кода, поэтому эти функции могут быть добавлены или удалены чисто и легко. Существует ли простая и соответствующая функция «разрыва», которая соответствует только что написанной функции? Функции ..._init() часто имеют функции ..._final(), функции ..._open() часто имеют функции ..._close() и так далее. Эти пары идеально подходят для управления памятью.

0 голосов
/ 27 февраля 2012

Отвечая на ваш последний вопрос:

Кроме того, мне следует использовать free после выделения памяти? Зачем?

Да, вы должны использовать free (); Если вы не используете free (), вы рискуете столкнуться с проблемой утечки памяти.

Вы должны вызывать free () прямо в конце функции, перед оператором возврата, если он у вас есть.

Однако вызывающая функция в вашем случае main также может вызывать free (), когда ваша функция svnViewStatus () завершена и возвращает управление main. Это потому, что можно использовать другую переменную-указатель с free (); вам просто нужно убедиться, что новый указатель хранит тот же адрес. Я имею в виду адрес первого байта выделенного блока памяти malloc.

И, как упоминал dasblinkenlight, вам нужно выделить память для завершения нуля.

Надеюсь, это поможет.

0 голосов
/ 02 января 2012

Во-первых, в этом выражении выражения:

*vect[k+1]++;

у вас есть проблема с приоритетом оператора. Вы, вероятно, хотели сделать это вместо:

(*vect[k+1])++;

Тогда в этом вызове функции:

printf("User %s: %d \n", *vect[i], *vect[i+1]);

тип vect[i] является указателем на символ. Чтобы напечатать строку, вы не хотите разыменовывать ее и, вероятно, хотите вместо этого:

printf("User %s: %d \n", vect[i], *vect[i+1]);
...