在C++中返回静态局部变量的引用

10

这个问题是为了更好地理解C++中的静态变量。

我曾经认为,如果在C ++中声明一个变量为静态变量,那么我可以返回对局部变量的引用,因为该变量应该在函数返回后继续存在。为什么这样做不起作用呢?

#include <stdio.h>
char* illegal()
{
  char * word = "hello" ;
  return word ;
}

char* alsoNotLegal()
{
  static char * word = "why am I not legal?" ;
  return word ;
}


int main()
{
  // I know this is illegal
  //char * ill = illegal();
  //ill[ 0 ] = '5' ;
  //puts( ill ) ;

  // but why is this? I thought the static variable should "live on" forever -
  char * leg = alsoNotLegal() ;
  leg[ 0 ] = '5' ;
  puts( leg ) ;
}

4
知道这些是指针而不是引用。 - Zifre
8个回答

20
这两个函数本身并不违法。首先,在这两种情况下,你都返回一个指向具有静态存储期的对象的指针副本:字符串字面值将在整个程序运行期间保持存在。
但是你的主函数涉及未定义的行为。你不能写入一个字符串字面值的内存 :) 你的主函数所做的可以被简化为等效的行为。
"hello"[0] = '5';
"why am I not legal?"[0] = '5';

两者都是未定义行为,而且在某些平台上会崩溃(很好!)。

编辑:请注意,在C++中,字符串字面量具有const类型(C语言中不是这样):char const[N]。将其分配给非const字符指针的操作会触发不推荐使用的转换(任何良好的实现都会发出警告)。因为上述对该常量数组的写入不会触发该转换,所以代码会编译错误。事实上,你的代码正在做这件事:

((char*)"hello")[0] = '5';
((char*)"why am I not legal?")[0] = '5';

阅读C++ strings: [] vs *


4

只有指针是静态的,它指向一个常量字符串。执行 leg[ 0 ] = '5' 是不可以的,因为它会修改常量字符串。

在这种情况下,static 没有太大的区别,实际上是相同的:

char* alsoNotLegal()
{
     return "why am I not legal?";
}

没错,你展示这个很好,因为大多数人都不知道你可以这样做。 - toto

3

当您定义并初始化一个指向char的指针时,例如:

char * word = "hello";

你实际上是告诉编译器将固定的字符串“hello”放入某个固定的存储空间中,然后创建一个名为word的指针变量指向它。

虽然你可以更改word变量以指向其他内容,如果它指向了一些可变存储空间,你可以通过*和[]运算符更改它所指向的内容,但你不能通过它来更改固定的字符串“hello”。

C++允许将固定字符串赋值给非const char指针,这仅是为了向后兼容。更好的做法是只将这些字符串分配给const char指针。例如:

const char * word = "hello";

这样可以通过编译时的类型检查来防止在运行时出现非法行为。
编辑:
在您的示例中,局部变量是否声明为静态基本上没有外部可见区别。这会影响每个函数中指针变量的生命周期,但不会影响指针变量所指向的固定字符串的生命周期。由于函数返回指针变量的值(在C++中,返回始终按值传递),因此函数中的指针变量是否在函数结束时被销毁并不特别重要。字符串本身将始终超出函数范围,因为字符串字面量具有静态存储期。

2
您可能想要的是:

您期望的可能是:

char* alsoNotLegal()
{
    static char[] word = "why am I not legal?" ;
 // static char*        x = "X"; <- Not good.
 // static const char*  y = "Y"; <- Good. As the compiler will warn you about
 //                                       Illegal attempts to modify it.

    return word ;
}

注意:这里您正在创建一个字符数组'word',并将"why am I not legal?"复制到该数组中。您可以对数组进行更改。
另外,由于语言处理数组的方式,当您返回一个数组(或将其作为参数传递)时,它会自动转换为指针。因此,它们很容易在某些情况下退化为指针。

1

这些“即时”字符串存储在Windows PE的只读“r”部分中。这就是为什么当您尝试写入该位置时会出现操作系统异常的原因。

如果您有Ollydbg或者阅读反汇编输出,您可以看到这些字符串位于RDATA部分(只读数据部分)。 如果它是存储在堆上的常规字符串,那么就不会有问题。

char* alsoNotLegal()
{
  static char word[] = "why am I not legal?" ;
  return word ;
}

这将有效,因为字符串将存储在“堆”上,即可执行映像的读/写部分。

0

对我有效吗...?

#include<iostream>
using namespace std;

char* legal() {
    char* word = "helllo";
    return word;
}

char* alsoLegal() {
    static char* word = "hello!";
    return word;
}

int main(){

    cout << legal() << endl;
    cout << alsoLegal() << endl;
    return 0;
}

但是正如在您的问题的评论中已经指出的那样,我们返回的是指针而不是引用,如果它只是一个char&,那么您只会得到“字符串”的第一个字母。


0

你的静态变量是一个常量字符串字面值。你不应该修改它。一些编译器可能会允许你这样做。如果你的静态变量是一个std::string,那么你就可以从调用者处修改它。


0

来自wikipedia

在C语言及其后代中,术语“静态变量”至少有两个具体且本质上无关的含义,它们各自与C语言中的静态关键字的语义相关:

  1. 静态局部变量,它们的作用域通常是正常范围,但具有静态存储期(而不是使用auto关键字声明的自动局部变量)

  2. 静态全局变量,它们具有通常的静态存储期,但作用域仅限于定义它们所在的文件(而不是使用extern关键字声明的外部变量)

因此,您声明的静态变量的作用域仍然在其各自的功能内部,并且在那些功能之外不可用。

您仍然可以返回指针,但它们实际上没有任何意义。

编辑:

还来自this page

全局函数中的静态局部变量可以被视为全局变量,因为它们的值在程序的生命周期内保留在内存中。唯一的区别是它们只能被一个函数访问(即作用域仅限于该函数)。1

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