我正在使用一些代码,这些代码广泛使用返回指向静态局部变量的指针的习惯用法.例如:
char* const GetString() { static char sTest[5]; strcpy(sTest, "Test"); return sTest; }
我认为这是安全的吗?
PS,我知道这是做同样事情的更好方法:
char* const GetString() { return "Test"; }
编辑: 道歉,功能签名当然应该是:
const char* GetString();
Brian R. Bon.. 37
第一个例子:有点安全
char* const GetString() { static char sTest[5]; strcpy(sTest, "Test"); return sTest; }
虽然不推荐,但这是安全的,即使函数的范围结束,静态变量的范围仍然存在.这个功能根本不是线程安全的.一个更好的函数会让你传递一个char* buffer
和一个maxsize
for GetString()
函数来填充.
特别是,此函数不被视为可重入函数,因为重入函数不得将地址返回到静态(全局)非常量数据.请参阅可重入函数.
第二个例子:完全不安全
char* const GetString() { return "Test"; }
如果你这样做,这将是安全的const char *
.你给的是不安全的.原因是因为字符串文字可以存储在只读存储器段中并允许它们被修改将导致不确定的结果.
char* const
(const指针)意味着您无法更改指针指向的地址. const char *
(指向const的指针)意味着您无法更改此指针指向的元素.
结论:
你应该考虑:
1)如果您有权访问该代码,则修改GetString
以获取a的参数char* buffer
以填充和maxsize
使用.
2)如果您无法访问代码,但必须调用它,请将此方法包装在受互斥锁保护的另一个函数中.新方法如1所述.
第一个例子:有点安全
char* const GetString() { static char sTest[5]; strcpy(sTest, "Test"); return sTest; }
虽然不推荐,但这是安全的,即使函数的范围结束,静态变量的范围仍然存在.这个功能根本不是线程安全的.一个更好的函数会让你传递一个char* buffer
和一个maxsize
for GetString()
函数来填充.
特别是,此函数不被视为可重入函数,因为重入函数不得将地址返回到静态(全局)非常量数据.请参阅可重入函数.
第二个例子:完全不安全
char* const GetString() { return "Test"; }
如果你这样做,这将是安全的const char *
.你给的是不安全的.原因是因为字符串文字可以存储在只读存储器段中并允许它们被修改将导致不确定的结果.
char* const
(const指针)意味着您无法更改指针指向的地址. const char *
(指向const的指针)意味着您无法更改此指针指向的元素.
结论:
你应该考虑:
1)如果您有权访问该代码,则修改GetString
以获取a的参数char* buffer
以填充和maxsize
使用.
2)如果您无法访问代码,但必须调用它,请将此方法包装在受互斥锁保护的另一个函数中.新方法如1所述.
从根本上说,是的,它是安全的,因为它是静态的,价值会无限期地持续下去.
从某种意义上说,你已经返回一个指向变量数据的常量指针,而不是指向常量数据的变量指针,这是不安全的.如果不允许调用函数修改数据,那就更好了:
const char *GetString(void) { static char sTest[5]; strncpy(sTest, "Test", sizeof(sTest)-1); sTest[sizeof(sTest)-1] = '\0'; return sTest; }
在所示的简单情况下,几乎不必担心缓冲区溢出,尽管我的代码版本确实令人担心,并确保空终止.另一种方法是使用TR24731功能strcpy_s
:
const char *GetString(void) { static char sTest[5]; strcpy_s(sTest, sizeof(sTest), "Test"); return sTest; }
更重要的是,两个变体都返回一个(变量)指针指向常量数据,因此用户不应该修改字符串并且(可能)在数组范围之外进行操作.(正如@strager在注释中指出的那样,返回a const char *
并不能保证用户不会尝试修改返回的数据.但是,他们必须将返回的指针转换为非const,然后修改数据;这会调用未定义的行为,此时可能发生任何事情.)
文字返回的一个优点是通常可以由编译器和操作系统强制执行无写入承诺.该字符串将放置在程序的文本(代码)段中,如果用户尝试修改返回值指向的数据,操作系统将生成错误(Unix上的分段违例).
[至少有一个答案指出代码不是可重入的; 那是正确的.返回文字的版本是可重入的.如果重入是重要的,则需要修复接口,以便调用者提供存储数据的空间.
static
变量(在函数中)就像作用域的全局变量.一般来说,应该避免它们(如全局变量,它们会引起重入问题),但有时很有用(某些标准库函数使用它们).您可以返回指向全局变量的指针,因此您也可以返回指向static
变量的指针.
这取决于你的安全意味着什么.我可以立即看到几个问题:
您已返回a char * const
,这将允许调用者更改此位置的字符串.潜在的缓冲区溢出.或者你的意思是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)); }
......什么会被打印出来?
线程相同.