Обнаружено повреждение кучи после вызова удаления в визуальном С++?

Я пытаюсь запустить свой код С++, введение которого в этот новый код привело к повреждению памяти, может ли кто-нибудь помочь мне понять, что может быть причиной этого. После вызова удаления я столкнулся с этой проблемой. Я также пробовал размещать newArgs[SZ] = '\0'; после каждого вызова strncpy.

Ошибка говорит:

ПОВРЕЖДЕНИЕ КУЧИ ОБНАРУЖЕНО после нормального блока (# 274) по адресу 0X00C09600 и т. д.

int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* argc, int nShowCmd){

    MyClass *obj;
    char args[] = " hello world";
    int SZ = strlen(args);
    int argsLength = 0;

    if(argc != NULL)
        argsLength=strlen(argc);
    SZ+=argsLength;
    char *newArgs = new char[SZ];
    strncpy(newArgs, "",SZ);

    if(argc != NULL)
        strncpy(newArgs, argc,argsLength);

    StrCat(newArgs,args);

    obj = new MyClass(newArgs);
    delete[] newArgs;

    return 0; 
}

person Juseeth    schedule 14.11.2014    source источник
comment
Что такое SIZE? Он используется, но никогда не объявлялся.   -  person Igor Tandetnik    schedule 14.11.2014
comment
Вы добавляете argsLength к SIZE, а не к SZ, поэтому newArgs будет недостаточно большим.   -  person The Dark    schedule 14.11.2014
comment
int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* argc, int nShowCmd) Что это?   -  person drescherjm    schedule 14.11.2014
comment
{std::string newArgs = args; newArgs += argc; } Вероятно, это то, к чему сводится весь этот код.   -  person PaulMcKenzie    schedule 14.11.2014
comment
Вероятно, он хотел написать SZ вместо SIZE, но проблема в том, что SZ должен быть еще одним для завершающего нулевого символа.   -  person ElefEnt    schedule 14.11.2014
comment
@drescherjm Скорее всего, WinMain переименован в main через переключатель /ENTRY по неясным причинам.   -  person Igor Tandetnik    schedule 14.11.2014


Ответы (2)


Вы получаете длину строки argc. Это без завершающего нулевого байта:

argsLength=strlen(argc);

Позже вы копируете argc в свой буфер. Но вы копируете только символы argsLength, то есть строку без конечного нулевого байта. Тогда strncpy скопирует только содержимое строки и не добавит нулевой байт в конце (см. man strncpy).

strncpy(newArgs, argc,argsLength);

Сразу после strncpy вы добавляете еще одну строку в свой newArgs.

StrCat(newArgs,args);

В зависимости от содержимого newArgs после выделения (которое может быть случайным, при отладке оно, вероятно, будет заполнено специальным шаблоном), функция strcat fill не найдет замыкающий нулевой байт в newArgs и, таким образом, будет считываться за пределы выделенного буфера (пока не будет находит нулевой байт) и добавит туда ваши строковые аргументы — где-то в куче и за пределами выделенной памяти. Это куча коррупции.

Кроме:

  1. При выделении памяти вам понадобится еще один байт для завершающего нулевого байта.
  2. Какая польза от strncpy(..., "", SZ)?
  3. Используйте std::string, а не строки C, подверженные ошибкам.
  4. Вы пропускаете выделенный объект MyClass.
  5. В С++ строковые литералы являются константами.

Я бы переписал вашу программу так (не компилировал и не тестировал):

int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* argc, int nShowCmd)
{
    const char args[] = " hello world";
    std::string newArgs;
    if(argc != NULL)
        newArgs = argc;
    newArgs += args;

    // Use the line that you prefer
    MyClass obj1(newArgs.c_str());
    std::unique_ptr<MyClass> obj2(new MyClass(newArgs.c_str()));

    return 0; 
}
person Werner Henze    schedule 14.11.2014
comment
как мне получить длину argc, и использование этого strncpy(...,, SZ) заключается в инициализации массива с пустыми символами изначально, поскольку по умолчанию он заполняет мусор. - person Juseeth; 14.11.2014
comment
длина argc: символы strlen(argc) плюс один завершающий нулевой байт. Таким образом, при объединении строк str1 и str2 ваш целевой буфер должен иметь длину strlen(str1)+strlen(str2)+1 байт. strncpy(..., , SZ) не скопирует байт (если SZ==0) или только завершающий нулевой байт (если SZ›0). Если вы хотите заполнить массив нулевыми байтами (что не обязательно, потому что вы перезапишете их позже), вам нужно использовать memset или std::fill. - person Werner Henze; 14.11.2014
comment
но вы сказали, что argc не имеет завершающего нулевого символа, поэтому определяет ли strlen его длину? - person Juseeth; 14.11.2014
comment
@suj: Нет, я этого не говорил. Если argc — это я, то у вас есть три байта: «m», «e» и завершающий нулевой байт «\0», поэтому память, на которую указывает argc, имеет длину 3 байта. strlen(argc) возвращает 2, потому что в строке два символа. Вам действительно следует погуглить/узнать, как строки представлены в C :-). - person Werner Henze; 14.11.2014

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

Я настоятельно рекомендую вам прекратить new использовать char* в C++ и использовать strings.

person Jonathan Mee    schedule 14.11.2014