Какая разница между VC++ 2010 Express и Borland C++ 3.1 в компиляции простого файла кода C++?

Я уже не знаю, что думать и что делать. Код Next компилируется нормально в обеих IDE, но в случае VC++ он вызывает странные сообщения о повреждении кучи, такие как:
"Windows сработала точка останова в Lab4.exe.

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

Это также может быть связано с тем, что пользователь нажал F12, когда Lab4.exe имеет фокус.

В окне вывода может быть больше диагностической информации."

Это происходит при выполнении функции Task1_DeleteMaxElement и я оставляю там комментарии.
Ничего подобного не происходит, если компилируется в Borland C++ 3.1 и все такое работать, как ожидалось.

Итак... что не так с моим кодом или VC++?

#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <memory.h>

void PrintArray(int *arr, int arr_length);
int Task1_DeleteMaxElement(int *arr, int arr_length);

int main()
{
    int *arr = NULL;
    int arr_length = 0;

    printf("Input the array size: ");
    scanf("%i", &arr_length);

    arr = (int*)realloc(NULL, arr_length * sizeof(int));

    srand(time(NULL));

    for (int i = 0; i < arr_length; i++)
        arr[i] = rand() % 100 - 50;

    PrintArray(arr, arr_length);

    arr_length = Task1_DeleteMaxElement(arr, arr_length);

    PrintArray(arr, arr_length);

    getch();

    return 0;
}

void PrintArray(int *arr, int arr_length)
{
    printf("Printing array elements\n");

    for (int i = 0; i < arr_length; i++)
        printf("%i\t", arr[i]);

    printf("\n");
}

int Task1_DeleteMaxElement(int *arr, int arr_length)
{
    printf("Looking for max element for deletion...");

    int current_max = arr[0];

    for (int i = 0; i < arr_length; i++)
        if (arr[i] > current_max)
            current_max = arr[i];

    int *temp_arr = NULL;
    int temp_arr_length = 0;

    for (int j = 0; j < arr_length; j++)
        if (arr[j] < current_max)
        {
            temp_arr = (int*)realloc(temp_arr, temp_arr_length + 1 * sizeof(int)); //if initial array size more then 4, breakpoint activates here
            temp_arr[temp_arr_length] = arr[j];
            temp_arr_length++;
        }


    arr = (int*)realloc(arr, temp_arr_length * sizeof(int));
    memcpy(arr, temp_arr, temp_arr_length);
    realloc(temp_arr, 0); //if initial array size is less or 4, breakpoint activates at this line execution

    return temp_arr_length;
}

person Kosmo零    schedule 18.05.2012    source источник
comment
Это Windows напоминает вам, что у вас есть ошибки с повреждением кучи, а не VS. BC3 использует свой собственный распределитель кучи, поэтому Windows не видит неправильного поведения вашего кода. Не замечать эти ошибки раньше довольно примечательно, но не совсем невозможно.   -  person Hans Passant    schedule 18.05.2012
comment
@Hans Passant: Вы уверены в Windows? Я думал, что это среда выполнения C/C++ обнаружила эти ошибки... Мммм... Думаю, вы правы, но тогда это означает, что обновление версии Windows вполне может привести к сбою, которого раньше не было в приложение... С другой стороны (мне нужно это проверить), у меня через malloc и new были свои собственные распределители, и это было причиной того, что при компиляции с /MT вы не могли освободить указатель в DLL, когда она была размещена в другой DLL.   -  person paercebal    schedule 18.05.2012


Ответы (3)


Я предполагаю, что VC++2010 правильно обнаруживает повреждение памяти, которое игнорируется Borland C++ 3.1.

Как это работает?

Например, при выделении памяти для вас, realloc VC++2010 вполне может "пометить" память вокруг него каким-то специальным значением. Если вы перепишете эти значения, realloc обнаружит повреждение, а затем произойдет сбой.

Тот факт, что он работает с Borland C++ 3.1, — чистая удача. Это очень-очень старый компилятор (20 лет!), и поэтому он будет более терпимым/неосведомленным о таком повреждении памяти (до тех пор, пока в вашем приложении не произойдет какой-то случайный, явно не связанный с ним сбой).

В чем проблема с вашим кодом?

Источник вашей ошибки:

temp_arr = (int*)realloc(temp_arr, temp_arr_length + 1 * sizeof(int))

Для следующих значений temp_arr_length в 32-битном распределении будет:

  • 0: 4 байта = 1 int, когда вы ожидаете 1 (ОК)
  • 1: 5 байт = 1,25 int, когда вы ожидаете 2 (Ошибка!)
  • 2: 6 байт = 1,5 int, когда вы ожидаете 3 (Ошибка!)

Вы ошиблись в своих приоритетах. Как вы видете:

temp_arr_length + 1 * sizeof(int)

вместо этого должно быть

(temp_arr_length + 1) * sizeof(int)

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

Изменить (2012-05-18)

Ханс Пассант прокомментировал диагностику распределителя. Я позволил себе скопировать их здесь, пока он не напишет свой собственный ответ (я уже видел, как комментарии исчезают на SO):

Это Windows напоминает вам, что у вас есть ошибки с повреждением кучи, а не VS. BC3 использует свой собственный распределитель кучи, поэтому Windows не видит неправильного поведения вашего кода. Не замечать эти ошибки раньше довольно примечательно, но не совсем невозможно.

[...] Эта функция недоступна в XP и более ранних версиях. И, конечно же, это одна из причин, по которой все недовольны Vista. Обвинение ОС в том, что на самом деле были ошибки в программе. Win7 воспринимается как «лучшая» ОС в немалой степени потому, что Vista заставляла программистов исправлять свои ошибки. И нет, Microsoft CRT уже давно реализует malloc/new с HeapAlloc. У Borland была история написания собственных программ, которые на какое-то время обыграли Microsoft, пока Windows не догнала их.

[...] CRT использует распределитель отладки, как вы описываете, но генерирует другую диагностику. Грубо говоря, аллокатор отладки ловит мелкие ошибки, Windows ловит грубые.

Я нашел следующие ссылки, объясняющие, что делается с памятью распределителями Windows/CRT до и после выделения/освобождения:

Последняя ссылка содержит таблицу, которую я распечатал и всегда имею под рукой на работе (именно эту таблицу я искал, когда нашел первые две ссылки... :- ...).

person paercebal    schedule 18.05.2012
comment
Да, CRT использует распределитель отладки, как вы описываете, но он генерирует другую диагностику. Грубо говоря, аллокатор отладки ловит мелкие ошибки, Windows ловит грубые. - person Hans Passant; 18.05.2012
comment
@Hans Passant: Пожалуйста, не могли бы вы предоставить полный ответ, содержащий ваши комментарии? Я скопирую и вставлю их в свой ответ для информационных целей, но это кажется неправильным, поскольку вы должны пожинать плоды этих знаний, а не я. На данный момент я скопирую ваши ответы в свой комментарий, и как только я увижу ваш ответ, я заменю комментарии, которые я скопировал / вставил, ссылкой на ваш ответ. Спасибо! - person paercebal; 18.05.2012

Если он дает сбой в realloc, то вы перешагиваете, бухгалтерия хранит память malloc & free.

Неправильный код, как показано ниже:

temp_arr = (int*)realloc(temp_arr, temp_arr_length + 1 * sizeof(int));

должно быть

temp_arr = (int*)realloc(temp_arr, (temp_arr_length + 1) * sizeof(int));

Из-за приоритета оператора * над + при следующем запуске цикла, когда вы ожидаете, что realloc передаст 8 байтов, он может передать только 5 байтов. Итак, во второй итерации вы будете записывать 3 байта в чужую память, что приводит к повреждению памяти и возможному сбою.

person Jay    schedule 18.05.2012

Также

memcpy(arr, temp_arr, temp_arr_length);

должно быть

memcpy(arr, temp_arr, temp_arr_length * sizeof(int) );
person tomato    schedule 18.05.2012