【问题标题】:Is it safe to use the argv pointer globally?全局使用 argv 指针是否安全?
【发布时间】:2015-10-14 19:23:01
【问题描述】:

全局使用argv 指针是否安全?还是有可能失效的情况?

即:这段代码安全吗?

char **largs;
void function_1()
{
    printf("Argument 1: %s\r\n",largs[1]);
}
int main(int argc,char **argv)
{
    largs = argv;
    function_1();
    return 1;
}

【问题讨论】:

  • 不,代码不保存。该程序可以不带参数调用,因此只有 argv[0] 或 args[0] 会包含一个字符串。
  • @manni66:很好的观察,虽然不是问题的重点:但你可以用if (largs[0] && largs[1])之类的东西来解决它。
  • @NateEldredge:是什么让你认为 largs[1] 如果 argc==1 会为空? OP 只需要创建一个全局int largsc; 并在 main() 中分配它largsc = argc,就像他为largs 所做的那样。那么function_1()不仅可以访问程序的argv,还可以访问它的argc
  • @phonetagger: C89 2.1.2.2,就是这样。 (或 C99 5.1.2.2.1。)“argv[argc] 应为空指针。”当然,您也可以按照您的建议进行操作,但利用 argv 保证为空终止这一事实通常很方便。
  • @NateEldredge:哎呀,好痛! :) 我从来不知道。如果我有的话,多年来这可能会简化一些事情。感谢您的信息。

标签: c argv


【解决方案1】:

是的,全局使用argv 是安全的;您可以像在程序中使用任何char** 一样使用它。 C99 标准甚至规定了这一点:

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

C++ 标准中没有类似的段落,但同样是隐含的,没有相反的规则。

请注意,C++ 和 C 是不同的语言,您应该只选择一种来询问您的问题。

【讨论】:

  • 考虑到这两种语言都使用 argv 并且他明确标记了 cc++当他第一次发布问题时:)
  • 老实说,argv 仅在main() 执行期间才真正存在,因此在atexit() 处理程序之类的处理程序中访问argv 可能会出现一些问题。
  • @LeeDanielCrocker 这是真的吗?规范可能很棘手,但在我看来,就像 TartanLlama 引用规范说它一直保留到终止。这向我表明,内存可通过终止获得,而 main() 只是传递了一个指向它的指针。
【解决方案2】:

只要main() 函数不退出就应该是安全的。 main() 退出后可能发生的一些事情示例如下:

  1. 全局变量和静态变量的析构函数
  2. 线程运行时间超过main()

存储的argv 不得用于这些。

The reference 没有说出任何理由来假设 main() 函数的参数的生命周期不同于函数参数生命周期的一般规则。

只要argv 指针本身是有效的,C/C++ 运行时就必须保证这个指针指向的内容是有效的(当然,除非有什么东西破坏了内存)。所以使用指针和长的内容一定是安全的。在main() 返回后,C/C++ 运行时也没有理由保持内容有效。所以上面的推理既适用于指针,也适用于它指向的内容。

【讨论】:

  • 你能提供参考来支持这个吗?
  • @ComicSansMS,不,这只是基于局部变量和参数生命周期的一般规则的常识:直到它们所属的函数退出。
  • @ComicSansMS,我添加了参考链接并编辑了答案。关键是main() 的引用并没有说明其参数的生命周期有什么特别之处,因此可以合理地假设main() 的参数遵循生命周期的一般规则。
  • 是的,在这个问题上很难找到明确的来源,这就是我问的原因。感谢您的尝试,非常感谢。
  • 在某种程度上,您的论点似乎主要适用于 argv 指针本身的生命周期(即,如果您从 &argv 存储了一个 to 指针)而不是内容。
【解决方案3】:

全局使用 argv 指针是否安全

这需要进一步澄清。正如C11 规范在第 5.1.2.2.1 章中所说,程序启动

[..].. 带有两个参数(此处称为argcargv,尽管可以使用任何名称,因为它们是声明它们的函数的本地名称)

这意味着,变量本身的范围仅限于main()。它们本身并不是全球性的

标准又说,

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

也就是说,这些变量的生命周期直到main()完成执行。

因此,如果您使用全局变量来保存来自 main() 的值,则可以安全地使用这些全局变量在任何其他函数中访问相同的值。

【讨论】:

  • “程序终止”是否包含 main 的结尾?还是在那之后?
  • @Yakk 就是在那之后,见atexit()
  • @mikk 当然,这似乎是合理的:但它不同意上述答案,并且我还没有看到明确的标准报价。有吗? Random 在另一个答案中提供了可能是委员会成员关于意图的随意评论。
  • @Yakk main 从程序的初始化部分调用。堆栈的顶部元素是 main 的返回地址,以返回处理其余垂死的例程。 argv 指向的字符串数组一直存在到最后。但是,如果其中的指针被重新分配,您可能会遇到问题。
【解决方案4】:

This thread comp.lang.c.moderated 新闻组从 C 标准的角度详细讨论了这个问题,包括引用显示 argv 数组的内容(而不是 argv 指针本身,例如您获取了地址&argv 并将其存储到“程序终止”之前,并且断言在 atexit 注册的函数正在执行时,程序终止尚未以与此相关的方式发生:

在 atexit-registered 期间程序没有终止 函数处理。我们认为这很明显。

(我不确定 Douglas A. Gwyn 是谁,但听起来“我们”是指 C 标准委员会?)

讨论的上下文主要是关于存储指针argv[0](程序名称)的副本。

相关的C标准文本为5.1.2.2.1:

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

当然,C++ 不是 C,它的标准可能会在这个问题上略有不同或没有解决。

【讨论】:

    【解决方案5】:

    您可以将它们作为参数传递,也可以将它们存储在global variables 中。只要您不从main 返回并尝试在atexit 处理程序或全局范围内变量的析构函数中处理它们,它们仍然存在并且可以从任何范围访问。

    【讨论】:

      【解决方案6】:

      是的,它对 ether C 或 C++ 是安全的,因为 main 完成后没有线程。

      【讨论】:

      • 这是什么保证? C没有例如从main 退出时杀死所有额外线程的任何析构函数。
      猜你喜欢
      • 2010-10-30
      • 2022-07-07
      • 2014-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-02
      相关资源
      最近更新 更多