使用getpwnam()时静态分配的变量

时间:2016-05-05 05:34:30

标签: c unix

我正在研究系统编程。

有很多函数可以返回静态分配的变量。

其中一个,有getpwnam()

所以,在书中我正在阅读的内容:

printf("%ld %ld\n", (long)(getpwnam("tsr")->pw_uid), (long)(getpwnam("avr")->pw_uid));

两者都返回相同的uid,尽管在/etc/passwd中,他们的uid是不同的。 并解释原因。

这就是问题。

但是,我认为这是因为静态分配的变量。但是,结果是输出不同(例如,如3001,3005)。

为什么输出不同?

根据这本书,输出应该是相同的。

并且,在函数中返回静态分配的变量,如局部变量(自动变量)?

例如:

char* func(const char** name)
{
    char str[256];
    strcpy(str,name);
    return str;
}

2 个答案:

答案 0 :(得分:4)

编译器可以随意调用getpwname,然后按照自己喜欢的顺序访问pw_uid字段。

例如,它可能会生成如下代码:

struct passwd *pw1 = getpwname("tsr");
struct passwd *pw2 = getpwname("avr");
long uid1 = pw1->pw_uid;
long uid2 = pw2->pw_uid;
printf("%ld %ld\n", uid1, uid2);

将打印相同的数字,因为对getpwname的两次调用都返回指向同一static变量的指针,而第二次调用会在我们有机会之前覆盖第一个写入的内容。获取我们需要的数据。这就是本书预期的事情。

另一方面,编译器也可以选择执行类似

的操作
struct passwd *pw1 = getpwname("tsr");
long uid1 = pw1->pw_uid;
struct passwd *pw2 = getpwname("avr");
long uid2 = pw2->pw_uid;
printf("%ld %ld\n", uid1, uid2);

在这种情况下,我们需要的信息(UID)在第二次调用getpwname之前保存在另一个变量中。

重要的是要强调两种生成代码的方式对于编译器来说都是合法的(从技术上讲,函数的参数之间没有序列点,所以副作用可能以任何顺序发生),所以你应该编写你的代码明确地在第二个例子中,以确保它将始终正常工作。

(顺便说一下,通常最好通过支持这些函数的可重入版本来避免这些意外 - 例如,getpwnam_r不会返回指向下次调用时被覆盖的静态数据的指针,但填写你提供的结构)

  

并且,在函数中返回静态分配的变量,如局部变量(自动变量)?

不完全;返回指向局部变量的指针是非法的,因为当函数返回时,该变量“逻辑上不再存在”,因此您将返回一个指向您不再拥有的内存的指针;实际上,每当它在堆栈上的位置被重用时(例如,在下一个函数调用时,或者在函数的后面),这些内存都会被不相关的数据无法自动覆盖。

返回一个指向静态分配的变量的指针(即全局或static本地 - 实际上只是一个伪装的全局)而不是合法的 - 内存是你的,不会去任何地方 - 但是从那以后它在所有对该函数的调用之间共享,它必然会在下次调用相关函数时被覆盖。这使得编写在嵌套调用中使用此类函数的代码变得复杂,并且线程安全或异步安全(如:在信号处理程序中可用)代码完全不可能。

答案 1 :(得分:1)

根据文档,getpwnam会返回指向静态存储的指针,并且可能会被后续调用getpwentgetpwnamgetpwuid覆盖。这就是你看到不一致结果的原因。作为建议,您可以使用这些函数的reentrent变体getpwnam_rgetpwuid_r,将信息返回给用户提供的缓冲区,而不是静态缓冲区。

  

并且,在函数中返回静态分配的变量,如局部变量(自动变量)?

不同之处在于静态存储数据初始化一次并存在于程序的整个生命周期中,自动变量仅存在于定义的块中。因此,在您的示例中,str数组在函数func返回时不再存在,并且使用它是未定义的行为。在静态的情况下,它是可以的。

char* func(const char** name)
{
    static char str[256];
    strcpy(str,name);
    return str;
}

但请记住,使用静态数据通常会导致函数无法重复。