Другое

Исправление ошибки Run-Time Check Failure #2 повреждение стека в C Windows API

Узнайте, как исправить ошибку Run-Time Check Failure #2 повреждение стека в программировании C Windows API. Понимание ошибок указатель vs адрес указателя и правильные методы выделения памяти.

Почему я получаю ошибку Run-Time Check Failure #2 - Stack around the variable ‘pMyStringAdress’ was corrupted в моей программе на C с использованием Windows API?

Я новичок в программировании на C и Windows API. После компиляции моего кода в Visual Studio я столкнулся с ошибкой: ‘Run-Time Check Failure #2 - Stack around the variable ‘pMyStringAdress’ was corrupted.’

Вот мой код:

c
#include <stdio.h>
#include <Windows.h>
#include <string.h>

char MyStrings[] = "ich bin ein text";

int main() {    
    SIZE_T SizeMyStrings = sizeof(MyStrings);

    PVOID pMyStringAdress = VirtualAlloc(
        NULL,
        SizeMyStrings,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE
    );

    if (pMyStringAdress == NULL) {
        printf("VirtualAlloc fehlgeschlagen mit fehlercode %d", GetLastError());
    }
    else {
        printf("Speicher erfolgreich allokiert sitzt bei: %p\n", &pMyStringAdress);
    }

    printf("Probiere in den Speicher zu schreiben.");
    memcpy(&pMyStringAdress, MyStrings, SizeMyStrings);
    return 0;
}

Что вызывает эту ошибку повреждения стека и как я могу исправить это в моей программе на C?

Ошибка “Run-Time Check Failure #2 - Stack around the variable ‘pMyStringAdress’ was corrupted” возникает из-за того, что вы копируете данные по адресу самой переменной-указателя (в стеке), а не в память, на которую указывает этот указатель (память, выделенную в куче).

Краткий ответ

Проблема заключается в вызове memcpy, где вы используете &pMyStringAdress (адрес переменной-указателя) вместо pMyStringAdress (указатель на выделенную в куче память). Это приводит к записи данных за пределами переменной-указателя в стеке, повреждая стек. Вам следует использовать memcpy(pMyStringAdress, MyStrings, SizeMyStrings) для записи в выделенную в куче память.

Содержание


Понимание ошибки

Сообщение об ошибке “Run-Time Check Failure #2 - Stack around the variable was corrupted” от Visual Studio указывает на то, что ваша программа записала данные за пределы переменной в стеке, повредив соседние области стека. Это функция безопасности среды выполнения Visual Studio, которая помогает обнаруживать переполнение буфера и ошибки повреждения памяти на ранней стадии.

Согласно исследованиям Stack Overflow, это обычно происходит при:

  • переполнении буфера, когда запись выходит за выделенный стековый объем
  • неправильных операциях с указателями, которые перезаписывают соседние переменные в стеке
  • операциях с памятью, когда путают указатели с адресами указателей

Основная причина: Указатель против адреса указателя

Критическая проблема в вашем коде находится в этой строке:

c
memcpy(&pMyStringAdress, MyStrings, SizeMyStrings);

Давайте разберемся, что происходит:

Что дает &pMyStringAdress:

  • Адрес самой переменной-указателя
  • Это расположение в памяти в стеке, где хранится значение указателя
  • На 64-битной системе это обычно 8 байт

Что дает pMyStringAdress:

  • Значение, хранящееся в указателе (адрес, возвращенный VirtualAlloc)
  • Это указывает на вашу выделенную в куче память
  • Именно сюда вы хотите записать данные

Функция memcpy копирует фактические байты памяти, которые вы указали. Когда вы передаете ей &pMyStringAdress, она копирует значение указателя (адрес), а не память, на которую он указывает.

Исправленный код и объяснение

Вот исправленная версия вашей программы:

c
#include <stdio.h>
#include <Windows.h>
#include <string.h>

char MyStrings[] = "ich bin ein text";

int main() {    
    SIZE_T SizeMyStrings = sizeof(MyStrings);

    PVOID pMyStringAdress = VirtualAlloc(
        NULL,
        SizeMyStrings,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE
    );

    if (pMyStringAdress == NULL) {
        printf("VirtualAlloc не удалось с кодом ошибки %d", GetLastError());
    }
    else {
        printf("Память успешно выделена по адресу: %p\n", pMyStringAdress);
    }

    printf("Попытка записи в память.");
    // ПРАВИЛЬНО: Используем pMyStringAdress (выделенную память), а не &pMyStringAdress
    memcpy(pMyStringAdress, MyStrings, SizeMyStrings);
    
    // Опционально: Проверяем, что копирование прошло успешно
    printf("Скопированное содержимое: %s\n", (char*)pMyStringAdress);
    
    // Не забудьте освободить память!
    VirtualFree(pMyStringAdress, 0, MEM_RELEASE);
    
    return 0;
}

Ключевые изменения:

  1. printf("Память успешно выделена по адресу: %p\n", pMyStringAdress); - изменено с &pMyStringAdress на pMyStringAdress
  2. memcpy(pMyStringAdress, MyStrings, SizeMyStrings); - изменено с &pMyStringAdress на pMyStringAdress
  3. Добавлен VirtualFree(pMyStringAdress, 0, MEM_RELEASE); для правильного освобождения выделенной памяти

Лучшие практики управления памятью

При работе с функциями памяти Windows API придерживайтесь этих лучших практик:

  1. Всегда проверяйте возвращаемые значения функций выделения памяти, таких как VirtualAlloc
  2. Используйте правильный указатель: Помните, что &variable дает вам адрес переменной, а variable (когда это указатель) дает значение, на которое он указывает
  3. Всегда освобождайте выделенную память, чтобы избежать утечек памяти
  4. Используйте подходящие размеры: Заметьте, что sizeof(MyStrings) включает нуль-терминатор, что обычно требуется для строк
  5. Инициализируйте указатели значением NULL при объявлении, чтобы избежать неопределенного поведения

Вот сравнение распространенных операций с указателями:

Операция Значение Расположение в памяти
pMyStringAdress Значение указателя (адрес в куче) Куча
&pMyStringAdress Адрес переменной-указателя Стек
*pMyStringAdress Разыменованный указатель (первый байт данных в куче) Куча

Дополнительные советы по отладке

Если вы столкнетесь с подобными проблемами в будущем:

  1. Используйте отладчик для проверки адресов памяти до и после операций
  2. Проверяйте границы при копировании данных - убедитесь, что у назначения достаточно места
  3. Проверяйте значения указателей - убедитесь, что они не равны NULL перед разыменованием
  4. Следите за ошибками на единицу - особенно при операциях со строками
  5. Рассмотрите безопасные альтернативы, такие как memcpy_s, когда они доступны

Согласно документации Microsoft, проверки времени выполнения Visual Studio помогают обнаруживать эти проблемы, которые в противном случае могли бы привести к скрытым ошибкам или уязвимостям безопасности.

Полный рабочий пример

Вот полный, надежный пример с правильной обработкой ошибок:

c
#include <stdio.h>
#include <Windows.h>
#include <string.h>

int main() {
    const char* sourceText = "Hello from allocated memory!";
    SIZE_T textSize = strlen(sourceText) + 1; // +1 для нуль-терминатора
    
    // Выделение памяти
    PVOID allocatedMemory = VirtualAlloc(
        NULL,
        textSize,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE
    );

    if (allocatedMemory == NULL) {
        DWORD error = GetLastError();
        printf("VirtualAlloc не удалось с кодом ошибки: %d\n", error);
        return 1;
    }

    printf("Память выделена по адресу: %p\n", allocatedMemory);
    
    // Копирование данных в выделенную память
    memcpy(allocatedMemory, sourceText, textSize);
    
    // Проверка копирования
    printf("Скопированное содержимое: %s\n", (char*)allocatedMemory);
    
    // Очистка
    if (!VirtualFree(allocatedMemory, 0, MEM_RELEASE)) {
        DWORD error = GetLastError();
        printf("VirtualFree не удалось с кодом ошибки: %d\n", error);
        return 1;
    }
    
    printf("Память успешно освобождена.\n");
    return 0;
}

Источники

  1. Stack Overflow - Объяснение ошибки Run-Time Check Failure #2
  2. Microsoft MSDN - Обсуждение Runtime Check Failure
  3. cppreference - Документация функции memcpy
  4. GeeksforGeeks - Использование memcpy в C
  5. Tutorialspoint - Функция memcpy в библиотеке C

Заключение

  • Ошибка возникает из-за записи в стековую позицию указателя вместо кучи, на которую он указывает
  • Исправьте, изменив &pMyStringAdress на pMyStringAdress в обоих вызовах printf и memcpy
  • Всегда проверяйте возвращаемые значения функций выделения памяти и правильно освобождайте выделенную память
  • Понимание разницы между указателем и адресом указателя критически важно для программирования с использованием Windows API
  • Проверки времени выполнения Visual Studio помогают обнаруживать повреждение стека, которое может привести к уязвимостям безопасности

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

Авторы
Проверено модерацией
Модерация