【问题标题】:Returning reference to static local variable in C++在 C++ 中返回对静态局部变量的引用
【发布时间】:2009-06-06 15:39:19
【问题描述】:

这个问题只是为了让我更好地理解 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 ) ;
}

【问题讨论】:

  • 只是让您知道这些是指针,而不是引用。

标签: c++


【解决方案1】:

这两个函数本身并不违法。首先,在这两种情况下,您都返回一个指针的副本,该指针指向一个具有静态存储持续时间的对象:字符串文字将在整个程序持续时间内存在。

但是您的 main 函数完全是关于未定义的行为。您不允许写入字符串文字的内存 :) 您的 main 函数所做的可以缩减为等效行为

"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 *

【讨论】:

    【解决方案2】:

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

    静态在这种情况下几乎没有什么区别,这真的是一样的:

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

    【讨论】:

    • 是的,你展示了这个是好事,因为大多数人没有意识到你可以做到这一点。
    【解决方案3】:

    当您像这样定义和初始化指向 char 的指针时:

    char * word = "hello";
    

    您实际上是在告诉编译器将固定字符串“hello”放入某个固定的存储区,然后创建word 指针变量指向它。

    虽然您将 word 变量更改为指向其他内容,并且如果它确实指向某个可变存储,您可以通过 * 和 [] 运算符更改它指向的内容,但不允许更改固定字符串“你好”通过它。

    C++ 允许将固定字符串分配给指向非常量 char 的指针,纯粹是为了向后兼容。最好只将这些字符串分配给指向const char 的指针。例如

    const char * word = "hello";
    

    这样可以防止通过编译时类型检查导致非法运行时行为。

    编辑:

    在您的示例中,将局部变量声明为静态与非静态之间基本上没有外部可见的区别。这会影响每个函数中指针变量的生命周期。它不会影响指针变量指向的固定字符串的生命周期。由于函数返回指针变量的 (在 C++ 中返回总是按值),所以函数中的指针变量是否在函数结束时被销毁并不特别重要.由于字符串文字具有静态存储持续时间,因此字符串本身将始终超出函数的范围。

    【讨论】:

      【解决方案4】:

      你可能想要的是:

      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 ;
      }
      

      注意:在这里,您正在创建一个字符数组“单词”并复制“为什么我不合法?”进入数组。您可以对阵列进行更改。

      另外,由于语言处理数组的方式,它们会在脱帽时退化为指针,当您返回数组(或将其作为参数传递)时,它会自动转换为指针。

      【讨论】:

        【解决方案5】:

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

        如果你有 Ollydbg 或者如果你阅读了反汇编输出,你可以看到字符串在 RDATA 部分(只读数据部分)。 如果是堆上存储的常规字符串,就没有问题

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

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

        【讨论】:

          【解决方案6】:

          您的 static 是一个 const 字符串文字。你不应该修改它。一些编译器可能允许您这样做。如果相反,您的静态是 std::string,那么您将能够从调用者修改它。

          【讨论】:

            【解决方案7】:

            来自wikipedia

            在 C 编程语言及其 后代,术语静态变量 至少有两个特定的和 本质上不相关的意思,每个 与C的static的语义有关 关键词:

            1. 静态局部变量,通常作用域,但具有静态 存储时间(相对于 声明的自动局部变量 使用 auto 关键字)

            2. 静态全局变量,具有通常的静态存储持续时间,但 范围为它们所在的文件 被定义(相对于外部 用 extern 声明的变量 关键字)

            所以。您声明的静态变量的作用域正常 - 它们的作用域仍在其各自的函数内,并且在这些函数之外不可用。

            你仍然可以返回指针,但它们没有任何意义。

            编辑:

            同样来自this page

            全局函数中的静态局部变量可以 被认为是全局变量, 因为它们的价值留在记忆中 终身计划。1 唯一的区别是它们只是 可访问(即范围)到一个 功能。

            【讨论】:

              【解决方案8】:

              适合我...?

              #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&,您只会得到“字符串”的第一个字母。

              【讨论】:

                猜你喜欢
                • 2022-08-14
                • 2012-11-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多