Программа вызывает повреждение памяти при перераспределении памяти

У меня есть программа с двумерным массивом. Сначала я выделяю массив следующим образом:

char **crossword;
crossword = (char **) malloc(n* sizeof(*crossword));
for (i = 0; i < n; i++)
    crossword[i] = (char *)malloc(n);

Где n = 50.

Затем у меня есть функция, которая читает строки из стандартного ввода. Проблема в том, что я не знаю, сколько и какой длины будут эти строки.

void read(char **p,int *n)
{
char tmp = 0,prevtmp = 0;
int i = 0, j = 0,x;

while (1)
{
    prevtmp = tmp;
    tmp = getchar();
    if ((tmp == '\n' && prevtmp == '\n') || feof (stdin))
    {
        *n = i;
        break;
    }
    if (tmp == '\n')
    {
        p[i][j] = '\0';
        i++;
        if (i == *n)
        {
            p = (char **) realloc(p, 2*i); // if there is more strings than space for them, allocate more memory.
            for (x = i; x < 2*i; x++)
                p[x] = (char *) malloc (*n);

            *n *= 2;
        }
        j = 0;
        continue;
    }
    p[i][j] = tmp;
    j++;
    if (j == *n)
        p[i] = (char *)realloc(p[i], 2*j); //same as above
}
}

Функция вызывается так:

read(crossword,&n);

Эта функция прекрасно работает, когда нет необходимости в realloc (менее 50 строк, каждая меньше 50 символов). Но для больших входов это не удается с

*** glibc detected *** ./a.out: malloc(): memory corruption (fast): 0x00000000014282f0 *** error.

Я думаю, что моя проблема в той части, где я перераспределяю больше памяти, вот вывод valgrind:

==8885== Invalid write of size 1
==8885==    at 0x40084C: read (in /home/xerw/Dropbox/CVUT/PROGTEST/du6/a.out)
==8885==    by 0x40177F: main (in /home/xerw/Dropbox/CVUT/PROGTEST/du6/a.out)
==8885==  Address 0x51f2b88 is not stack'd, malloc'd or (recently) free'd
==8885== 
==8885== Invalid write of size 8
==8885==    at 0x4008A6: read (in /home/xerw/Dropbox/CVUT/PROGTEST/du6/a.out)
==8885==    by 0x40177F: main (in /home/xerw/Dropbox/CVUT/PROGTEST/du6/a.out)
==8885==  Address 0x51f4f00 is not stack'd, malloc'd or (recently) free'd

valgrind: m_mallocfree.c:266 (mk_plain_bszB): Assertion 'bszB != 0' failed.
valgrind: This is probably caused by your program erroneously writing past the
end of a heap block and corrupting heap metadata.  If you fix any
invalid writes reported by Memcheck, this assertion failure will
probably go away.  Please try that before reporting this as a bug.

==8885==    at 0x3804C6CF: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==8885==    by 0x3804C812: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==8885==    by 0x38000883: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==8885==    by 0x380574EA: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==8885==    by 0x38057E03: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==8885==    by 0x380212DC: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==8885==    by 0x3802146A: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==8885==    by 0x3808F656: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)
==8885==    by 0x3809E68C: ??? (in /usr/lib/valgrind/memcheck-amd64-linux)

sched status:
  running_tid=1

Thread 1: status = VgTs_Runnable
==8885==    at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8885==    by 0x4008A5: read (in /home/xerw/Dropbox/CVUT/PROGTEST/du6/a.out)
==8885==    by 0x40177F: main (in /home/xerw/Dropbox/CVUT/PROGTEST/du6/a.out)

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

Что я делаю не так?


person isklenar    schedule 24.11.2012    source источник
comment
что за кроссворд?   -  person piokuc    schedule 25.11.2012
comment
char **кроссворд, отредактировал свой пост.   -  person isklenar    schedule 25.11.2012
comment
Вы делаете это p = (char **) realloc(p, 2*i); в своем коде. p указывает на кроссворд? Если это так, то вы выделили кроссворд размером n * sizeof(*crossword). Так 2*i правильно? Кстати, плохо вызывать realloc и присваивать его результат указателю, который вы перераспределяете напрямую - если realloc() не работает, происходит утечка памяти, потому что ваш исходный указатель теперь потерян.   -  person Nik Bougalis    schedule 25.11.2012
comment
2D-массивы и двойные указатели — это не одно и то же. Использование [][] для двойного указателя всегда плохо. Прочтите это: ibiblio.org/pub/languages/fortran/append- c.html.   -  person linuxuser27    schedule 25.11.2012
comment
@linuxuser27 linuxuser27 Почему stringArray[i][j] плохо получать символ с индексом j в строке, заканчивающейся 0, на которую указывает stringArray[i]? Я нахожу это намного яснее, чем *(*(stringArray + i) + j) или *(stringArray[i] + j) или (*(stringArray + i))[j].   -  person Daniel Fischer    schedule 25.11.2012
comment
"This function works fine, when there is no need for realloc (there is less than 50 strings, each smaller than 50 characters). But for large inputs, this fails" дай угадаю. Вы используете crossword после возвращения read? realloc не изменяет адрес, хранящийся в переменной crossword в вызывающем объекте, поэтому, когда realloc меняет местоположение внутри read, вызывающий объект остается с оборванным указателем. Запись через него может легко повредить жизненно важную память, а любое его использование вызывает неопределенное поведение.   -  person Daniel Fischer    schedule 25.11.2012
comment
@DanielFisher, так что, если я правильно вас понял, realloc действителен только для функции, в которой он был вызван?   -  person isklenar    schedule 25.11.2012


Ответы (1)


Проблема, похоже, связана со следующей строкой:

p = (char **) realloc(p, 2*i);

Я думаю, это должно быть:

p = (char **) realloc(p, (2*i)*sizeof(char*));

то есть вы хотите увеличить массив, чтобы он содержал 2*i элементов, поэтому вам нужно выделить 2*i раз больше размера char*.

person Raam    schedule 24.11.2012
comment
Спасибо, исправил, но моя программа все равно вылетает, на этот раз с другой ошибкой. Я пишу с телефона прямо сейчас, и я не помню точную ошибку, посмотрю завтра утром. - person isklenar; 25.11.2012