putenv(char *string);
调用似乎存在致命缺陷。
是的,它确实存在致命缺陷。在POSIX(1988)中保留了它,因为那是先前的技术。稍后出现了setenv()
机制。更正: POSIX 1990标准在§B.4.6.1中说“考虑了附加函数putenv()和clearenv(),但被拒绝了”。1997年的Single Unix Specification (SUS)版本2列出了putenv()
但没有列出setenv()
或unsetenv()
。下一次修订(2004年)确实定义了setenv()
和unsetenv()
。
因为它不会复制传递的字符串,所以不能使用局部变量调用它,并且无法保证堆分配的字符串不会被覆盖或意外删除。
你说得对,将局部变量传递给
putenv()
几乎总是不明智的选择——异常情况几乎不存在。如果字符串在堆上分配(使用
malloc()
等),必须确保您的代码不会修改它。如果它被修改了,同时也更改了环境变量。
此外(虽然我没有测试过),由于环境变量的一个用途是将值传递给子进程的环境,因此如果子进程调用
exec*()
函数,则似乎毫无意义。我错了吗?
exec*()
函数会复制环境并将其传递给执行的进程。这里没有问题。
Linux的手册页面指出glibc 2.0-2.1.1放弃了上述行为,并开始复制字符串,但这导致了内存泄漏,后来在glibc 2.1.2中修复了该问题。对我来说不清楚这个内存泄漏是什么或者如何解决它。
内存泄漏会出现的原因是一旦使用
putenv()
函数调用字符串后,就不能再使用该字符串,因为无法确定它是否仍在使用中,尽管您可以通过覆盖它来修改其值(如果将名称更改为在环境中的另一个位置找到的环境变量,则结果不确定)。 因此,如果您分配了空间,则经典的
putenv()
在再次更改变量时会泄漏该空间。 当
putenv()
开始复制数据时,分配的变量变得无引用,因为
putenv()
不再保留对参数的引用,但用户期望环境将引用它,因此内存已泄漏。 我不确定修复方法是什么 - 我 3/4 希望回归到旧行为。
setenv()
函数会复制字符串,但我不知道具体如何工作。 进程加载时会分配环境的空间,但它是固定的。
原始环境空间是固定的; 当您开始修改它时,规则会发生变化。 即使使用
putenv()
,也会修改原始环境,并且可能会因添加新变量或将现有变量更改为具有更长值而增长。
这里是否有一些(任意的?)惯例在起作用? 例如,在 env 字符串指针数组中分配更多插槽,以根据需要向下移动空闲指针?
那就是
setenv()
机制的作用。全局变量
environ
指向指针数组的环境变量起始位置。如果它在不同时间指向不同的内存块,则环境会立即切换。
“新字符串的内存是否分配在环境本身的地址空间中,如果太大而无法适应,您只会收到ENOMEM错误吗?”
嗯,是的,你可能会收到ENOMEM错误,但你必须非常努力才能这样做。如果将环境增大过多,则可能无法正确执行其他程序 - 环境将被截断或执行操作将失败。
考虑到上述问题,有没有理由更喜欢putenv()而不是setenv()?
- 在新代码中使用
setenv()
。
- 更新旧代码以使用
setenv()
,但不要把它作为最重要的任务。
- 不要在新代码中使用
putenv()
。
putenv()
,则应优先将其替换为setenv()
。 - Yeow_Mengexec()
(或者不太可能的情况下,存在exit()
)。例如,https://github.com/apk/c-utils/blob/dffc690ffec471087059bbdb7f6af453690aa835/aenv.c#L185 是可以的,并且在这里使用putenv
是最好的选择,因为你已经有了NAME=value
字符串。 - Andreas Kreysetenv()
后跟unsetenv()
会造成内存泄漏。使用相同键的setenv()
后跟setenv()
也可能会泄漏,具体取决于实现的质量。在重写之前手动free()
的putenv()
可在不泄漏的情况下使用,并在unsetenv()
时进行,在这种情况下,您需要跟踪哪些键值对实际上是堆分配的,使用外部数据结构,否则就会面临未定义行为等问题。-这很混乱。 :-) - Arne Vogelsetenv()
和unsetenv()
函数不应该引起内存泄漏。在* env()函数包的内部,有许多不愉快的细节需要管理,正如您所暗示的那样,管理它们需要比仅通过environ
公开的指针数组更多的信息。该公开变量打开了一个后门(尽管修改它会引发未定义的行为)。它成为了QoI(实现质量)问题。简单但质量低劣的实现容易出现内存泄漏。高质量的实现避免大多数泄漏,但可能被不良程序迫使泄漏。 - Jonathan Lefflerputenv()
的基本原理:“标准开发人员指出,putenv()
是唯一可用于添加到环境中而不允许内存泄漏的函数。”;虽然自2013年版以来已经没有了这些含糊其辞的话语-请参见ERN 1086,但ERN根本没有解决setenv()
可能会泄漏的原始声明。 - Arne Vogel