【问题标题】:Reallocation of argv重新分配 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]应该是空指针,所以可以让它指向到别的东西。

标签: c main argv


【解决方案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.
猜你喜欢
  • 2014-11-09
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 2020-11-28
  • 1970-01-01
  • 1970-01-01
  • 2016-06-26
  • 1970-01-01
相关资源
最近更新 更多