【问题标题】:Why was getenv standardised but not setenv?为什么 getenv 是标准化的而不是 setenv?
【发布时间】:2018-02-06 21:08:08
【问题描述】:

this question 上的答案和cmets,我了解到getenv 是由C++ 标准定义的,但setenv 不是。确实,下面的程序

#include <cstdlib>
#include <iostream>

int main ( int argc, char **argv )
{
    std::cout << std::getenv("PATH") << std::endl;  // no errors

    std::setenv("PATH", "/home/phydeaux/.local/bin:...", true);  // error
}

无法为我编译(clang 3.9)。

为什么这些看似互补的功能之一被标准化,而另一个却没有?

【问题讨论】:

  • @tambre Doubtful,std::getenv 从 C++11 开始是线程安全的(根据 cppreference)
  • 推测,但我想说的是因为std::getenv有一个相对简单的定义可以标准化。串入,串出。补充不是那么直接,并且非常依赖于实现。
  • C standard mirrors this。有7.22.4.6 getenv 函数,但没有对应的setenv()。因此,无论从 C++ 中省略它的逻辑是否存在,它可能也适用于 C。因此我添加了 C 标记。 (还有语言律师标签……)
  • 我同意@StoryTeller 的推测,我想没有明确的答案,因此我投票以“主要基于意见”的方式结束,尽管这个问题确实很有趣......
  • 我投票决定重新打开 - 并不是说​​它有很大的不同 - 因为事实证明有一份文件给出了省略一个非常相关的功能的理由。

标签: c++ c language-lawyer


【解决方案1】:

C90标准包括getenv();因此,C++98 标准也是如此。

最初创建C标准时,环境设置的先例是putenv()setenv() 函数直到后来才被设计出来。标准委员会尽可能避免创建新功能,但也尽可能避免标准化有问题的功能(是的,localeconv()gets() 是反例)。 putenv() 的行为是有问题的。你必须传递它不是自动持续时间的内存,但你不知道你是否可以再次使用它。这就像一个强制的内存泄漏。 putenv() 没有标准化是一件好事。

C 标准的rationale 明确表示(§7.20.4.5,p163):

标准中省略了相应的putenv 函数,因为它在多进程环境之外的实用性值得怀疑,而且它的定义正确地属于操作系统标准。

特定于平台的 API 介入并以适合它们的方式提供缺失的功能。


POSIX 标准的第一版(1988 年试用;1990 年)不包括 setenv()putenv()。 X/Open 可移植性指南 (XPG) 第 1 期确实包含 putenv(),这是基于它在 SVID (System V Interface Definition) 中的出现 — 不包括 setenv()。 XPG 第 6 版添加了 setenv()unsetenv()(有关链接到的 URL 中的功能,请参见历史部分)。奇怪的是,在运行 macOS Sierra 10.12.6 的 Mac 上,man 3 setenv 有一个历史记录部分,它标识:

函数 setenv() 和 unsetenv() 出现在版本 7 AT&T UNIX 中。 putenv() 函数出现在 4.3BSD-Reno 中。

这是出乎意料的并且可能是错误的,因为 UNIX Programmer's Manual Vol 1 (1979) 不包括 putenv()setenv()unsetenv() 中的任何一个。 putenv() 函数在 80 年代的某个阶段被添加到 Unix 的 AT&T 变体中;它在 SVID 中并在 1990 年 SVR4 发布时记录在案,并且可能是 System III 的一部分。我认为他们几乎把平台颠倒了。 4.3BSD-Reno 于 1990 年 6 月发布,在第一个 C 和 POSIX 标准发布之后。

在 cmets 中有一些关于 Random832 的讨论,现已删除,其中提到 TUHS – The Unix Heritage Society 作为有关古代 Unix 版本的信息来源。这个链条包括我的观察:如果没有别的,这个讨论强调了为什么标准委员会在避免“设置环境”方面做得很好!与我的记忆相反,putenv() 似乎不在第 7 版 UNIX 中。我相当肯定它在我从 1983 年开始使用的系统中可用,这是很多第 7 版,其中一些材料来自 System III,一些来自 PWB。它是 SVR4 的一部分(我有一本手册),并在某些版本的 SVID 中定义(可能在 SVR4 之前)。

C 的基本原理还提到了对gets() 的担忧,但尽管存在这些担忧,但还是将其包括在内;当然,它(非常明智地)从 C11 中删除(但 POSIX 仍然指的是 C99,而不是 C11)。

【讨论】:

【解决方案2】:

setenv 在 C 定义的某些原始环境中是不可能的。

getenv 允许您查看您的环境。使用 exec[lv][p][e] 创建新进程允许您创建具有继承或新环境的子进程。

但是,setenv 会修改调用进程的状态,这并不总是可能的。

我猜是因为它增加了调用者的可写接口,本来是不需要的,现在是安全风险。

【讨论】:

  • "修改调用进程的状态" - 真的吗? C 可以指定此类平台(如果存在)在其 getenv()/putenv() 实现中提供足够的隔离,以产生每个进程环境的效果。
  • @TobySpeight:他的意思是,处理对getenv 的调用。
  • 啊,我明白了;我误解为父进程。感谢您指出这一点,@Cheersandhth.-Alf
  • @TobySpeight 即使考虑到您的想法,对于自然提供全局环境或根本不提供环境的系统而言,提供隔离环境可能已被视为负担过重(符合标准的 getenv 可以总是返回 null)。
猜你喜欢
  • 1970-01-01
  • 2015-08-26
  • 2010-09-19
  • 2012-02-14
  • 2012-11-15
  • 1970-01-01
  • 2011-11-22
相关资源
最近更新 更多