重新分配 argv

Posted

技术标签:

【中文标题】重新分配 argv【英文标题】:Reallocation of argv 【发布时间】:2015-11-08 16:45:03 【问题描述】:

当我在 main 函数(第 78 行)中看到这段代码时,我正在查看 GNU coreutils 包的代码,特别是 the program 'yes':

if (argc <= optind)

  optind = argc;
  argv[argc++] = bad_cast ("y");

数组argv怎么可以这样展开呢?显然,将任何代码 sn-p 脱离上下文是一个非常糟糕的主意,所以我查看了 argv 是否事先被修改过,除了对 initialize_main (&amp;argc, &amp;argv) 的调用之外似乎没有,这似乎不需要为“新大小”或类似的东西争论(但在 C 中,就像任何语言一样,事情并不总是它们看起来的样子)。

我决定写一个简单的程序来测试我是否可以在argv上调用realloc()

char** new_argv = realloc(argv, ++argc * sizeof*argv);

它有效(在 Windows 10 上使用 VS2013)。它返回一个指向已分配内存的指针。当然,如果它是未定义的行为,那实际上并不意味着 任何东西

所以,长话短说,我的问题是,argv 是如何分配的?重新分配argv真的安全吗?

【问题讨论】:

我很确定它是未指定的,因此是 UB;你最好 malloc + memcpy。考虑一下 coreutils 是为特定平台编写的,其中一些行为是已知的。 argv 是指向char(程序拥有)的指针数组,最后一个,即argv[argc]应该是空指针,所以可以让它指向到别的东西。 【参考方案1】:
argv[argc++] = bad_cast ("y");

这不会扩展argv 数组。它只是为argv[argc] 赋值,然后递增argc。这确实打破了argv[argc] == NULL 的初始保证,但只要代码不依赖于它是有效的。

标准保证:

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

它没有明确保证argv 指向的数组中的char* 指针是可修改的,但这是一个合理的假设。 (严格来说argv指向数组的第一个元素,而不是数组本身,但那句话已经够长了。)

char** new_argv = realloc(argv, ++argc * sizeof*argv);

这具有未定义的行为。 realloc 的第一个参数必须是空指针或指向由 malloccallocrealloc 或等效项分配的内存的指针。 argv 指向的内存在进入main 之前以某种未指定的方式分配。您可以制作数组的副本,但不能合法地释放它,这是realloc 所做的一部分。如果realloc 调用对您来说表现“正确”,那么您就是不走运。 (如果你幸运,你的程序就会崩溃,这会告诉你有问题。)

【讨论】:

我更喜欢这个答案,因为它更深入、更直接地回答了我的问题。 优秀的答案!尤其是UB和倒霉/幸运的部分。【参考方案2】:

首先,argv[argc] 被定义为NULL

其次,argc++ 递增 argc 但返回其旧值。

因此,argv[argc++] = ... 不会调用未定义的行为;它只是为以前的NULL 指针分配一个新值。

【讨论】:

argv[argc] 是否保证为NULL @ThePcLuddite 它是,从标准 5.1.2.2.1 程序启动 - 2 argv[argc] shall be a null pointer.

以上是关于重新分配 argv的主要内容,如果未能解决你的问题,请参考以下文章

如何重新分配磁盘空间?

重新分配内存并在C中重新分配的内存空间添加一个字符串

当 React 功能组件重新渲染时,它会重新分配分配的值和功能吗?

为啥 C++ 分配器中没有重新分配功能?

std::vector- 他将分配多少内存(在重新分配期间)

问题重新分配分配的对象