Как realloc может не работать, а malloc может?

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

Затем я memcpy массив указателей в новый выделенный массив, но обнаружил, что это сломало его, указатели, такие как 0x10 и 0x2b, были помещены в массив. Есть настоящие указатели, но если я заменю memcpy на цикл for, это исправит ситуацию. Почему memcpy сделал это? Должен ли я не использовать memcpy в своем коде?

Код:

float * resizeArray_by(float *array, uint size)
{
    float *tmpArray = NULL;
    if (!array)
    {
        tmpArray = (float *)malloc(size);
    }
    else
    {
        tmpArray = (float *)realloc((void *)array, size);
    }

    if (!tmpArray)
    {
        tmpArray = (float *)malloc(size);
        if (tmpArray)
        {
            //memcpy(tmpArray, array, size - 1);
            for (int k = 0; k < size - 1; k++)
            {
                ((float**)tmpArray)[k] = ((float **)array)[k];
            }
            free(array);
        }
    }

    return tmpArray;
}

void incrementArray_andPosition(float **& array, uint &total, uint &position)
{
    uint prevTotal = total;
    float *tmpArray = NULL;
    position++;
    if (position >= total)
    {
        total = position;
        float *tmpArray = resizeArray_by((float *)array, total);
        if (tmpArray)
        {
            array = (float **)tmpArray;

            array[position - 1] = NULL;
        }
        else
        {
            position--;
            total = prevTotal;
        }
    }
}

void addArray_toArray_atPosition(float *add, uint size, float **& array, uint &total, uint &position)
{
    uint prevPosition = position;
    incrementArray_andPosition(array, total, position);

    if (position != prevPosition)
    {
        float *tmpArray = NULL;
        if (!array[position - 1] || mHasLengthChanged)
        {
            tmpArray = resizeArray_by(array[position - 1], size);
        }

        if (tmpArray)
        {
            memcpy(tmpArray, add, size);
            array[position - 1] = tmpArray;
        }
    }
}

После всех моих исправлений код, вероятно, инициализируется. Интересно то, что после сортировки массивов я выделяю с помощью malloc огромный массив, чтобы переупорядочить массивы в один массив, который будет использоваться как GL_ARRAY_BUFFER. Если realloc не выделяет из-за нехватки места, то почему не выделяет?

Наконец, это все равно приводит к сбою в конце. После прохождения функции рендеринга один раз происходит сбой. Если бы я удалил все свои исправления и просто поймал, когда realloc не выделяет, это сработало бы нормально. Напрашивается вопрос, что плохого в распределении моего массива вместо перераспределения, чтобы вызвать проблемы в будущем?

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


person NebulaFox    schedule 10.05.2011    source источник
comment
Трудно понять, в чем проблема, не видя кода. Не могли бы вы включить некоторый контекст об использовании вами memcpy? Фактические вызовы realloc и malloc?   -  person David Thornley    schedule 10.05.2011
comment
Похоже, здесь работает ошибка. Либо в вашем коде, либо в ЭЛТ (менее вероятно).   -  person Paul Groke    schedule 10.05.2011
comment
Вы путаете значение переменной «размер»? Это размер массивов в байтах или количество поплавков в массиве?   -  person James    schedule 10.05.2011
comment
Почему ваш вопрос помечен как C и C++ одновременно?   -  person AnT    schedule 10.05.2011
comment
@AndreyT, потому что я пишу C на C++ из-за android-ndk   -  person NebulaFox    schedule 10.05.2011


Ответы (3)


Вы путаете size и типы указателей. При выделении памяти size — это количество байтов, и вы преобразуете тип указателя в float *, по существу создавая массив float размера size / sizeof(float). В коде, эквивалентном memcpy, вы обрабатываете массив как float ** и копируете size из них. Это уничтожит кучу, предполагая, что sizeof(float *) > 1, и, вероятно, является источником более поздних проблем.

Более того, если вы копируете, скажем, массив размером 100 в массив размером 200, вам нужно скопировать более 100 элементов, а не 200. Копирование за конец массива (что вы и делаете) может привести к программировать сбои.

Динамически выделяемый массив указателей на floats будет иметь тип float **, а не float *, и уж точно не смесь этих двух типов. Размер массива — это количество байтов для malloc и друзей, а также количество элементов во всех операциях с массивом.

memcpy будет точно копировать байты, предполагая, что исходный и целевой блоки не перекрываются (и отдельно выделенные блоки памяти не перекрываются). Однако вы указали size - 1 для количества скопированных байтов, тогда как скопированное число должно быть точным размером байтов старого массива. (В любом случае, где вы получаете неправильные значения указателя? Если он находится в расширенной части массива, вы все равно копируете туда мусор.) Если memcpy дает вам ерунду, она становится ерундой с самого начала, и это не ваша проблема.

person David Thornley    schedule 10.05.2011
comment
Когда я печатаю значения массива memcpy memcpy(tmpArray, array, size - 1), я получаю неверные указатели. - person NebulaFox; 10.05.2011

Судя по разным битам информации (realloc не находит память, memcpy ведет себя неожиданно, аварийно завершает работу), это очень похоже на повреждение кучи. Без некоторых примеров кода того, что именно вы делаете, трудно сказать наверняка, но похоже, что в какой-то момент вы неправильно управляете памятью, в результате чего куча переходит в недопустимое состояние.

Можете ли вы скомпилировать свой код на альтернативной платформе, такой как Linux (возможно, вам придется заглушить некоторые API-интерфейсы для Android)? Если это так, вы можете посмотреть, что происходит на этой платформе, и/или использовать valgrind, чтобы найти ее.

Наконец, если у вас есть этот помеченный C++, почему вы используете malloc/realloc вместо, например, vector (или другого стандартного контейнера) или new?

person Mark B    schedule 10.05.2011
comment
На android-ndk стандартной библиотеки не существует. Изначально я использовал вектор. А так как в C++ нет realloc, я пишу на C. - person NebulaFox; 10.05.2011

И кстати, вам не нужно проверять, является ли array NULL

Вы можете заменить

if (!array)
{
    tmpArray = (float *)malloc(size);
}
else
{
    tmpArray = (float *)realloc((void *)array, size);
}

by

tmpArray = realloc(array, size*sizeof (float));

realloc действует как malloc при наличии указателя NULL.

Еще одна вещь, будьте осторожны, чтобы размер не был равен 0, так как realloc с 0, поскольку размер такой же, как free.

В-третьих, не приводить типы к указателям, когда в этом нет крайней необходимости. Вы типизировали возврат функций распределения, это считается плохой практикой со времен ANSI-C. Это обязательно в С++, но поскольку вы используете выделение C, вы, очевидно, не в С++ (в этом случае вы должны использовать new/delete). Приведение переменной массива к (void *) также необязательно, так как это может скрыть некоторые предупреждения, если ваш параметр был ложно объявлен (это может быть int или указатель на указатель, и путем приведения вы бы подавили предупреждение).

person Patrick Schlüter    schedule 10.05.2011