Программа на C Изменение argv программно выдает ошибку повреждения кучи после выхода из программы только в режиме отладки в Visual Studio. Как решить? - PullRequest
0 голосов
/ 28 марта 2019

У меня есть большая унаследованная программа, в которой программно параметр argv программно изменяется в init программы с последующей логикой, которая анализирует параметры.

В режиме Release программа корректно завершается.

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

enter image description here

Сообщение об ошибке:


Библиотека времени выполнения Microsoft Visual C ++

Ошибка отладки!

Программа: ... sers \ AMD \ source \ repos \ ArgvOverflow \ x64 \ Debug \ ArgvOverflow.exe

ОБНАРУЖЕНА КОРРУПЦИЯ КАРТЫ: после блока CRT (# 62) в 0x00000259566FDC90.CRT обнаружил, что приложение записало в память после завершения буфера кучи.

Память, выделенная в minkernel \ crts \ ucrt \ src \ appcrt \ startup \ argv_parsing.cpp (285).

(НажмитеПовторите попытку отладки приложения)


Прервать попытку Повторить Игнорировать


Код:

#include <stdio.h>
int main(int argc, char **argv)
{
       argc = 12;
       argv[1] = "str1";
       argv[2] = "str2";
       argv[3] = "str3";
       argv[4] = "str4";
       argv[5] = "str5";
       argv[6] = "str6";
       argv[7] = "str7";
       argv[8] = "str8";
       argv[9] = "str9";
       argv[10] = "str10";
       argv[11] = "str11";
       printf("Hello world\n");
       return 0;
}

Я прочитал несколько сообщений об изменении argvгде они утверждают, что такие изменения являются законными в соответствии со стандартами C.Я также попробовал предложение иметь строку

argv[argc] = NULL;

Это не решает проблему.

Ответы [ 5 ]

2 голосов
/ 28 марта 2019

Допустимо изменить символы строк аргумента . C11 5.1.2.2.1p2

Параметры argc и argv и строки, на которые указывает массив argv, должны изменяться программой и сохранять свои последние сохраненные значения между запуском программы и ее завершением.

По-прежнему не разрешен доступ к массиву за пределами, argv будет иметь только argc + 1 элементов, не так много, как вы пытаетесь вставить туда.

1 голос
/ 28 марта 2019

Вам разрешено изменять argc и argv, но это не означает, что C внезапно обрабатывает (пере) распределение этих переменных для вас.argv будет массивом типа char* argv[argc];.Он будет содержать столько указателей, сколько argc говорит, что содержит, не больше, не меньше.Точно так же длина каждой строки, на которую указывает argv[i], равна длине, которую передал вызывающий.

Пример:

myprog.exe foo 
  • Это означает, что argc == 2 и argv будет иметь длину 2. Это не может быть изменено вашей программой.
  • argv[0] будет указывать на изменяемую строку "myprog.exe", размер 10 + 1 = 11 байтов.Вы можете изменить содержимое, но не хранить там ничего длиннее 11 байт.
  • argv[1] будет указывать на строку "foo", размер 3 + 1 = 4 байта.Вы можете изменить содержимое, но не хранить там ничего длиннее 4 байт.

(Любопытство: это совершенно нормально и, пожалуй, самый правильный способ определить argv как VLA, например:
int main (int argc, char* argv[argc]), потому что argv распадается на char** в любом случае.)


То, что все сказанное, изменение argc и argv, хотя и разрешено стандартом C, ужасноплохая практика.Не делай этого.Вместо этого вы должны использовать локальную переменную и позволить ей ссылаться на argv, где это необходимо.Пример:

int main (int argc, char* argv[])
{
  const char* argstr [12] = 
  {
    "str0",
    "str1",
    "str2",
    "str3",
    "str4",
    "str5",
    "str6",
    "str7",
    "str8",
    "str9",
    "str10",
    "str11",
  };

  for(int i=0; i<argc; i++)
  {
    argstr[i] = argv[i];
  }

  /* always use argstr here, never argv */

  return 0;
}
1 голос
/ 28 марта 2019

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

Если вы вызываете вашу программу так:

program a b c

затем argc равно 4, argv[0] указывает на "program", argv[1] указывает на "a" и т. Д.

argv[argc] является NULL

Но доступ к argv[5] и выше - неопределенное поведение, потому что вы обращаетесь к массиву за пределами.

Эта статья ТАК тоже может помочь: Насколько опасно обращаться к массиву за пределами? .

Чтобы решить вашу проблему: не обращайтесь к массивам за пределами.

0 голосов
/ 31 мая 2019

Совершенно правдоподобно иметь такую ​​настройку при выполнении отладки.Одна из работ, которая работала для меня, состояла в том, чтобы создать фиктивный список аргументов при вызове программы, а затем заново заполнить argv требуемыми данными, как только вы попадете в main.

myprog.exe dummy dummy dummy dummy dummy dummy dummy dummydummy dummydummy dummy 

и внутри вашего кода

    int main (int argc , char* argv[]){

       argc = 12;
       argv[1] = "str1";
       argv[2] = "str2";
       argv[3] = "str3";
       argv[4] = "str4";
       argv[5] = "str5";
       argv[6] = "str6";
       argv[7] = "str7";
       argv[8] = "str8";
       argv[9] = "str9";
       argv[10] = "str10";
       argv[11] = "str11";
       printf("Hello world\n");
       return 0;
}
0 голосов
/ 28 марта 2019

Чтобы решить эту проблему, я создал отдельную переменную char ** и использовал эту переменную в коде для решения проблемы.

Вот как выглядел новый код:

#include <stdio.h>
int main(int argc, char **argv)
{
    int nargc = 12;
    char **nargv;
    nargv = malloc(sizeof(char*)*nargc);

    nargv[0] = malloc(1 + strlen(argv[0]));
    strcpy(nargv[0], argv[0]);

    nargv[1] = malloc(1 + strlen("srt1"));
    strcpy(nargv[1], "srt1");

    nargv[2] = malloc(1 + strlen("srt2"));
    strcpy(nargv[2], "srt2");

    nargv[3] = malloc(1 + strlen("srt3"));
    strcpy(nargv[3], "srt3");

    nargv[4] = malloc(1 + strlen("srt4"));
    strcpy(nargv[4], "srt4");

    nargv[5] = malloc(1 + strlen("srt5"));
    strcpy(nargv[5], "srt5");

    nargv[6] = malloc(1 + strlen("srt6"));
    strcpy(nargv[6], "srt6");

    nargv[7] = malloc(1 + strlen("srt7"));
    strcpy(nargv[7], "srt7");

    nargv[8] = malloc(1 + strlen("srt8"));
    strcpy(nargv[8], "srt8");

    nargv[9] = malloc(1 + strlen("srt9"));
    strcpy(nargv[9], "srt9");

    nargv[10] = malloc(1 + strlen("srt10"));
    strcpy(nargv[10], "srt10");

    nargv[11] = malloc(1 + strlen("srt11"));
    strcpy(nargv[11], "srt11");

    /* Useful code */

    free(nargv[11]);
    free(nargv[10]);
    free(nargv[9]);
    free(nargv[8]);
    free(nargv[7]);
    free(nargv[6]);
    free(nargv[5]);
    free(nargv[4]);
    free(nargv[3]);
    free(nargv[2]);
    free(nargv[1]);
    free(nargv[0]);


    free(nargv);
    printf("Hello world\n");
    return 0;
}
...