返回一个指向静态局部variables安全的指针?
我正在使用一些广泛使用返回指向静态局部variables的方式的代码。 例如:
char* const GetString() { static char sTest[5]; strcpy(sTest, "Test"); return sTest; }
我是否认为这是安全的?
PS,我知道这是做同样事情的一个更好的方法:
char* const GetString() { return "Test"; }
编辑:道歉,function签名当然应该是:
const char* GetString();
第一个例子:有点安全
char* const GetString() { static char sTest[5]; strcpy(sTest, "Test"); return sTest; }
尽pipe不推荐,但这是安全的,即使函数的作用域结束,静态variables的作用域仍然保持有效。 这个函数根本不是线程安全的。 一个更好的函数会让你传递一个char* buffer
和GetString()
函数来填充maxsize
。
特别是,这个函数不被认为是一个可重入的函数,因为可重入函数除了别的以外不能将地址返回到静态(全局)非常数数据 。 请参阅可重入函数 。
第二个例子:完全不安全
char* const GetString() { return "Test"; }
如果你做了一个const char *
这将是安全的。 你给的是不安全的。 原因是因为string文字可以存储在只读内存段中,并允许它们被修改将导致未定义的结果。
char* const
(const指针)意味着你不能改变指针指向的地址。 const char *
(指向const的指针)意味着你不能改变这个指针指向的元素。
结论:
你应该考虑:
1)如果你有权访问代码,那么修改GetString
来获取一个char* buffer
的参数来填充和使用一个maxsize
。
2)如果你没有访问代码,但你必须调用它,把这个方法包装在另一个受互斥锁保护的函数中。 新方法如1所述。
从根本上说,是的,从价值意义上来说,这是安全的,因为它是静态的。
从一个常量指针指向variables数据,而不是指向常量数据的variables指针,这是不安全的。 调用函数不允许修改数据会更好:
const char *GetString(void) { static char sTest[5]; strncpy(sTest, "Test", sizeof(sTest)-1); sTest[sizeof(sTest)-1] = '\0'; return sTest; }
在所示的简单情况下,几乎不需要担心缓冲区溢出问题,尽pipe我的代码版本不用担心,并且确保了null结束。 另一种方法是使用TR24731函数strcpy_s
代替:
const char *GetString(void) { static char sTest[5]; strcpy_s(sTest, sizeof(sTest), "Test"); return sTest; }
更重要的是,两个变体都会返回一个指向常量数据的(可变)指针,所以用户不应该修改string,并且(可能)会在数组范围之外进行践踏。 (As @strager在注释中指出,返回一个const char *
并不能保证用户不会尝试修改返回的数据,但是他们必须转换返回的指针,使它不是const的,然后修改数据;这调用了未定义的行为,任何事情都可能在这一点上。)
文字回归的一个优点是通常可以通过编译器和操作系统强制执行不写的承诺。 该string将被放置在程序的文本(代码)段中,如果用户尝试修改返回值指向的数据,则操作系统将生成一个错误(在Unix上的分段违例)。
[至less有一个答案指出代码不可重入; 那是对的。 返回文字的版本是可重入的。 如果重入是重要的,那么接口需要被固定,以便调用者提供存储数据的空间。
这取决于你的意思是安全的。 我可以立即看到几个问题:
- 你已经返回了一个
char * const
,这将允许调用者在这个位置改变string。 潜在的缓冲区溢出。 或者你的意思是一个const char *
? - 您可能会遇到再入或并发问题。
要解释第二个,请考虑这一点:
const char * const format_error_message(int err) { static char error_message[MAXLEN_ERROR_MESSAGE]; sprintf(error_message, "Error %#x occurred", err); return error_message; }
如果你这样称呼它:
int a = do_something(); int b = do_something_else(); if (a != 0 && b != 0) { fprintf(stderr, "do_something failed (%s) AND do_something_else failed (%s)\n", format_error_message(a), format_error_message(b)); }
…将要打印什么?
相同的线程。
static
variables(在一个函数中)就像范围化的全局variables。 一般来说,他们应该避免(像全局variables,它们会导致重入问题),但有时有用(一些标准库函数使用它们)。 你可以返回指向全局variables的指针,所以你也可以返回指向static
variables的指针。
是的,这是完全安全的。 本地静态的生命周期是整个程序在C中的执行时间,所以你可以返回一个指针,因为即使在函数返回之后数组仍然是活着的,并且返回的指针可以被有效地解除引用。
这是非常有用的,因为您可以直接使用该函数作为printf参数。 但是,如前所述,在一次调用中对函数进行多次调用会导致一个问题,因为该函数使用相同的存储并调用它两次将覆盖返回的string。 但我testing了这段代码,它似乎工作 – 你可以安全地调用一个函数,其中最多MAX_CALLS次使用givemestring,它将正确行为。
#define MAX_CALLS 3 #define MAX_LEN 30 char *givemestring(int num) { static char buf[MAX_CALLS][MAX_LEN]; static int rotate=0; rotate++; rotate%=sizeof(buf)/sizeof(buf[0]); sprintf(buf[rotate],"%d",num); return buf[rotate]; }
唯一的问题是线程安全,但是这可以用线程局部variables(gcc的__thread关键字)
是的,这经常被用来返回一些查找的文本部分,即将一些错误编号转换成人类友好的string。
明智的做法是:
fprintf(stderr, "Error was %s\n", my_string_to_error(error_code));
如果my_string_to_error()
返回一个已分配的string,那么给定这个函数的上述(非常)常用的用法,程序就会泄漏。
char const *foo_error(...) { return "Mary Poppins"; }
…也行,一些大脑死亡的编译器可能希望你把它施放。
只要看着这样的string,不要归还一本书:)
返回指向局部variables的指针,不论它是否是静态的,都可能是不安全的,因为静态variables的生命期限于函数,它将是一个悬挂指针。