C程序在Visual Studio中仅在调试模式下退出程序后,以编程方式更改argv会导致堆损坏错误。怎么解决?

问题描述 投票:-1回答:5

我有一个大型的遗留程序,其中以编程方式在程序初始化中以编程方式更改argv参数,然后是解析参数的逻辑。

在发布模式下,程序正常终止。

在调试模式下,程序执行所有必需的计算并提供正确的输出。但退出时会出现Heap损坏错误:

enter image description here

错误信息:


Microsoft Visual C ++运行时库

调试错误!

程序:... sers \ AMD \ source \ repos \ ArgvOverflow \ x64 \ Debug \ ArgvOverflow.exe

HEAP CORRUPTION DETECTED:在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;

这不是解决问题的方法。

c argv heap-corruption
5个回答
2
投票

修改参数字符串的字符是合法的。 C11 5.1.2.2.1p2

参数argcargv以及argv数组指向的字符串应该可由程序修改,并在程序启动和程序终止之间保留它们最后存储的值。

它仍然不允许访问数组越界,argv将只有argc + 1元素,而不是你尝试在那里的东西。


1
投票

你不能像这样乱用argvargv不是你的,它在程序启动期间根据命令行分配和填充。

如果你这样调用你的程序:

program a b c

然后argc是4,argv[0]指向"program"argv[1]指向"a"等。

argv[argc]NULL

但访问argv[5]及更高版本是未定义的行为,因为您访问数组越界。

这篇SO文章也可能有所帮助:How dangerous is it to access an array out of bounds?

解决您的问题:不要访问超出界限的数组。


1
投票

您可以修改argcargv,但这并不意味着C突然为您处理(重新)这些变量的分配。 argv将是char* argv[argc];类型的阵列。它将包含与argc一样多的指针,表示它包含,不多也不少。类似地,argv[i]指向的每个字符串的长度与调用者通过的长度一样长。

例:

myprog.exe foo 
  • 这意味着argc == 2argv的长度为2.这个程序无法更改。
  • argv[0]将指向一个可修改的字符串"myprog.exe",大小10 + 1 = 11个字节。您可以更改内容,但不能存储超过11个字节的内容。
  • argv[1]将指向字符串"foo",大小3 + 1 = 4字节。您可以更改内容,但不能存储超过4个字节的内容。

(好奇心:它是完全正确的,可以说是将q​​azxswpoi定义为VLA的最正确方法,如下所示: argv,因为无论如何int main (int argc, char* argv[argc])衰变成argv。)


尽管如此,修改char**argc虽然是C标准所允许的,但却是非常糟糕的做法。不要这样做。相反,你应该使用一个局部变量,让它在需要的地方引用argv。例:

argv

0
投票

为了解决这个问题,我创建了一个单独的char **变量,并在代码中使用该变量来解决问题。

以下是新代码的样子:

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;
}

0
投票

在进行调试时进行这种设置是完全合理的。为我工作的一个工作是在调​​用程序时设置一个伪参数列表,然后在进入main后用所需数据重新填充argv。

#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;
}

在你的代码里面

myprog.exe dummy dummy dummy dummy dummy dummy dummy dummydummy dummydummy dummy 
© www.soinside.com 2019 - 2024. All rights reserved.