C程序仅在Visual Studio中的调试模式下,以编程方式更改argv会在程序退出后产生堆损坏错误,如何解决?

qvk1mo1f  于 2023-04-29  发布在  其他
关注(0)|答案(5)|浏览(122)

我有一个很大的遗留程序,其中的argv参数是在程序init中以编程方式更改的,后面是解析参数的逻辑。
在释放模式下,程序正常终止。
在调试模式下,程序执行所有必需的计算并给出正确的输出。但退出时会出现堆损坏错误:

错误消息:

Microsoft Visual C++运行时库

调试错误!
程序:...sers\AMD\source\repos\ArgvOverflow\x64\Debug\ArgvOverflow。exe
检测到堆腐 eclipse :在0x 00000259566 FDC 90的CRT块(#62)之后。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;

这并不能解决问题。

ntjbwcob

ntjbwcob1#

你可以修改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字节的内容。

(好奇心:这是非常好的,可以说是最正确的定义argv为VLA的方式,像这样:
int main (int argc, char* argv[argc]),因为argv无论如何都会衰减成char**。)
话虽如此,修改argcargv,虽然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;
}
wnavrhmk

wnavrhmk2#

  • 修改参数字符串的字符 * 是法律的的。C11 5.1.2.2.1p2

参数argcargv以及argv数组指向的字符串应可由程序修改,并在程序启动和程序终止之间保留其最后存储的值。
仍然不允许访问数组的边界,argv将只有**argc + 1个元素(值为argc *,就像在 * main的开头一样),没有你试图填充的那么多。

au9on6nz

au9on6nz3#

你不能像这样摆弄argvargv不是您的,它已在程序启动时根据命令行分配并填充。
如果你像这样调用你的程序:

program a b c

那么argc是4,argv[0]指向"program"argv[1]指向"a",等等。
argv[argc]NULL
但是访问argv[5]和更高的值是未定义的行为,因为您访问的是越界的数组。
这篇文章可能也有帮助:How dangerous is it to access an array out of bounds?
要解决您的问题,请执行以下操作:不要访问越界的数组。

jgovgodb

jgovgodb4#

为了解决这个问题,我创建了一个单独的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;
}
ndh0cuux

ndh0cuux5#

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

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

相关问题