从本地C语言风格的数组返回指针导致悬空指针问题

49

我对以下代码有点困惑:

#include <iostream>

const char* f()
{
    const char* arr[]={"test"};
    return arr[0];
}

int main()
{
    auto x = f();
    std::cout << x;
}

我认为这段代码应该是未定义的行为(UB)。我们在本地范围内返回一个指向C风格数组元素的指针。事情应该会出错。然而,我用-Wall -Wextra -pedantic在g++和clang上测试了所有编译器,没有一个抱怨。 valgrind也没有抱怨。

上面的代码是有效的还是像人们想象的那样UB?

PS:运行它似乎会产生“正确”的结果,即显示“test”,但这并不表示正确性。


10
就实际效果而言,“test”常量字符串被存储在可执行文件的静态数据区域,因此即使函数返回后该字符串仍然有效。当然,它是否有语言规范的保证则是另一个问题。 - Jeremy Friesner
2
问这些问题没有任何伤害,而且这个问题写得很好。加一。 - Bathsheba
2
@JesperJuhl 我知道什么是未定义行为,也知道由此可能导致月球爆炸。我想问的是这段代码是否真的存在未定义行为。看起来并没有。但很多人认为这段代码存在未定义行为...所以我认为这个问题很有用。 - vsoftco
2
@vsoftco:我本来没想到,但只有像巴里这样的专家才能指出为什么。 - Bathsheba
1
@TypeIA 是的,我看到了。可能我太习惯自动判断问题了,只是因为看到返回自函数指针的问题:/ - Algirdas Preidžius
显示剩余9条评论
2个回答

79
不,这不是未定义行为。
这段内容:
const char* f()
{
    const char* arr[]={"test"};
    return arr[0];
}

可以改写成等价形式:

const char* f()
{
    const char* arr0 = "test";
    return arr0;
}

所以我们只返回一个指向字符串字面量的本地指针。字符串字面量具有静态存储期,没有什么会悬空。这个函数确实与以下函数相同:

const char* f()
{
    return "test";
}

如果你做了类似于这样的事情

const char* f() {
    const char arr[] = "test"; // local array of char, not array of char const*
    return arr;
}

现在那个是未定义行为 - 我们正在返回一个悬空指针。


1
"字符串字面量具有静态存储期" - 我知道所有编译器都这样做,但是它真的在标准中规定了字符串字面量必须具有静态存储期吗? - UKMonkey
@UKMonkey 是的,已添加引用。 - Barry
3
不是要卖弄学识,但这并不完全“等同”。在这个上下文中,从功能上来说,可以这么认为,但数组就是数组就是数组。;) - Lightness Races in Orbit
4
在两种情况下,都有一个指向“const char”的本地指针,两种情况下该本地指针都被初始化为指向文字常量,并返回其值。唯一的区别是,在原始示例中,本地指针是单个元素数组的成员,在替代版本中,它是标量。如果在某些方面这些不是“等价的”,那又怎样呢?重要的是本地变量的确切类型并不重要。关键是函数返回后字符串字面量是否仍然有效。 - Solomon Slow
2
C语言同样保证字符串文字具有静态存储期,详情请参见http://port70.net/~nsz/c/c11/n1570.html#6.4.5p6。 - zwol
显示剩余8条评论

3
数组 arr 具有局部存储期,将在作用域结束时消失。然而,字符串字面量 "test" 是指向静态存储位置的指针。在返回指针之前,将其暂时存储在本地数组 arr 中并不会改变这一点。它始终是一个静态存储位置。
请注意,如果函数返回的是 C++ 样式的字符串类型而不是 C 样式的 const char *,则额外的转换/簿记可能会使您得到一个按照 C++ 临时规则有限生命周期的值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接