代码之家  ›  专栏  ›  技术社区  ›  John Carter

返回指向静态局部变量的指针是否安全?

  •  41
  • John Carter  · 技术社区  · 17 年前

    char* const GetString()
    {
      static char sTest[5];
      strcpy(sTest, "Test");
      return sTest;
    }
    

    我认为这是安全的,对吗?

    char* const GetString()
    {
      return "Test";
    }
    

    编辑:

    const char* GetString();
    
    7 回复  |  直到 17 年前
        1
  •  40
  •   artm    9 年前

    第一个例子:有些安全

    char* const GetString()
    {
      static char sTest[5];
      strcpy(sTest, "Test");
      return sTest;
    }
    

    char* buffer maxsize 对于 GetString()

    特别是,此功能不被视为 因为可重入函数不能, 将地址返回到静态(全局)非常量数据 . 看见 reentrant functions .

    char* const GetString()
    {
      return "Test";
    }
    

    const char * 你所付出的并不安全。原因是字符串文字可以存储在只读内存段中,允许对其进行修改将导致未定义的结果。

    char* const (const pointer)表示无法更改指针指向的地址。 (指向常量的指针)表示无法更改此指针指向的元素。

    你应该考虑一下:

    1) 如果您有权访问代码,请修改 GetString 获取 字符*缓冲区 使用。

        2
  •  10
  •   Roger Lipscombe    17 年前

    这取决于你所说的安全。我可以立即看到几个问题:

    1. 你退回了一张支票 char * const ,这将允许调用方更改此位置的字符串。潜在的缓冲区溢出。或者你是说 const char *
    2. 您可能在重入或并发方面有问题。

    为了解释第二点,考虑一下:

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

    …将要打印什么?

        3
  •  10
  •   strager    17 年前

    static 变量(在函数中)类似于作用域全局变量。一般来说,应该避免使用它们(与全局变量一样,它们会导致重入问题),但有时它们是有用的(一些标准库函数使用它们)。您可以返回指向全局变量的指针,因此可以返回指向全局变量的指针 变量也是如此。

        4
  •  8
  •   Community Mohan Dere    8 年前

    基本上,是的,它是安全的,因为它是静态的,所以该值将无限期地持续。

    从某种意义上讲,您返回的是指向变量数据的常量指针,而不是指向常量数据的变量指针,这是不安全的。最好不允许调用函数修改数据:

    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在评论中指出的,返回一个 const char * 不能保证用户不会尝试修改返回的数据。但是,他们必须强制转换返回的指针,使其为非常量,然后修改数据;这会调用未定义的行为,在这一点上任何事情都是可能的。)

    文本返回的一个优点是,无写承诺通常可以由编译器和操作系统强制执行。字符串将被放置在程序的文本(代码)段中,如果用户试图修改返回值所指向的数据,操作系统将生成错误(Unix上的段冲突)。

        5
  •  3
  •   Johannes Schaub - litb    17 年前

    是的,非常安全。局部静态的生命周期是整个程序在C中执行的生命周期。因此,您可以返回指向它的指针,因为即使在函数返回后,数组仍将处于活动状态,并且返回的指针可以有效地取消引用。

        6
  •  1
  •   Nuclear    13 年前

    它非常有用,因为您可以将函数直接用作printf参数。 但是,正如前面提到的,在单个调用中对函数的多次调用将导致问题,因为函数使用相同的存储,并且调用两次将覆盖返回的字符串。但是我测试了这段代码,它似乎起到了作用——您可以安全地调用函数,其中givemestring在最多MAX_调用次数下使用,并且它将正常工作。

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

    唯一的问题是线程安全,但这可以通过线程局部变量(gcc的_线程关键字)来解决

        7
  •  0
  •   Jonathan Leffler    17 年前

    是的,这经常用于返回某些查找的文本部分,即将某些错误号转换为人性化字符串。

    在以下情况下这样做是明智的:

    fprintf(stderr, "Error was %s\n", my_string_to_error(error_code));
    

    如果 my_string_to_error()

    char const *foo_error(...)
    {
        return "Mary Poppins";
    }
    

    ... 这也没关系,不过有些死气沉沉的编译器可能会希望您使用它。

    用这种方式看琴弦,不要还书:)