【问题标题】:Can a static c function return a local char array?静态 c 函数可以返回本地 char 数组吗?
【发布时间】:2017-09-30 19:51:32
【问题描述】:

在 zflog 库中我看到了这段代码

static char* lvl_char(const int lvl)
{
    switch (lvl)
    {
    case ZF_LOG_VERBOSE:
        return "VERBOSE\0";
    case ZF_LOG_DEBUG:
        return "DEBUG\0";
    case ZF_LOG_INFO:
        return "INFO\0";
    case ZF_LOG_WARN:
        return "WARN\0";
    case ZF_LOG_ERROR:
        return "ERROR\0";
    case ZF_LOG_FATAL:
        return "FATAL\0";
    default:
        ASSERT_UNREACHABLE("Bad log level");
        return "?\0";
    }
}

这对我来说似乎很奇怪。我们真的可以从静态函数中返回一个本地 c 字符串吗?

【问题讨论】:

  • This 可能有助于理解变量的分配位置。它是在考虑微控制器的情况下编写的,但普遍适用。 (除非在您没有真正 ROM 的 PC 上。)
  • "VERBOSE\0" 很奇怪。只需"VERBOSE" 通常就足够了,因为当编译器为字符串分配空间时,它包含一个 NUL 终止字符。 (除非调用者出于某种疯狂的原因需要 2 个 NUL)。

标签: c string return string-literals storage-duration


【解决方案1】:

函数(这里是静态的)的链接根本不重要。也不返回“字符串”;相反,返回的是一个指向 char 的指针。返回指向字符串文字的第一个字符的指针是完全合法的 - 字符串文字保证在程序的整个持续时间内都存在。 C11 6.4.5p6 声明此处使用的字符串文字用于初始化 *“static 存储持续时间和长度刚好足以包含序列的 [匿名] 数组”。静态存储持续时间意味着它的“生命周期是程序的整个执行过程,其存储的值仅在程序启动之前初始化一次。” (C11 6.2.4p3)。


确实看起来很奇怪的是字符串文字末尾的 \0,因为文字字符串总是以 0 结尾,所以基本上 "VERBOSE\0" 将只是以 2 个零结尾字节而不是通常的字节;该字符串的strlen 将返回7,就像它返回"VERBOSE" 一样,以此类推。

【讨论】:

  • 函数返回后这个char指针不会是悬空指针吗?我的意思是,这些字符串不是本地的吗?
  • @ozgur 1) 它们是字符串“文字” 2) 没有任何本地内容,文字具有静态存储持续时间。
【解决方案2】:

你错了,返回的指针不是指向本地的char array,而是指向一个string literal,也就是进程的static

来自c-standard

6.4.5 字符串字面量

语义学

在翻译阶段 7,一个字节或零值代码被附加到每个多字节 由一个或多个字符串文字产生的字符序列。78) 多字节字符 然后使用序列来初始化一个静态存储持续时间和长度的数组 足以包含序列。[...]

强调我的

【讨论】:

    【解决方案3】:

    静态 c 函数能否返回本地 char 数组?

    TL;DR无论staticextern,函数都不应返回本地数组,因为它不能被有意义地使用。


    现在,要详细说明手头的问题,

    ....静态函数?

    你有它,static 与“函数”的链接相关联,而不是 返回值或类型

    这里,static 存储说明符表示,函数具有内部链接,即只能从翻译单元访问。

    相关C11,第 §6.2.2 章

    如果对象或函数的文件范围标识符的声明包含存储类 说明符static,标识符有内部链接。

    OTOH,return 之类的语句

    return "FATAL\0"; 
    return "DEBUG\0"; ///and so on
    

    实际上返回指向string literal的第一个元素的指针,根据定义具有静态存储持续时间注1,所以返回值为

    • 与返回类型匹配,char * 注意2
    • 在返回语句后有效。 注3

    注1:

    引用C11,第 §6.4.5/P6 章

    在翻译阶段 7,一个字节或零值代码被附加到每个多字节 由一个或多个字符串文字产生的字符序列。78) 多字节字符 然后使用序列初始化静态存储持续时间数组和长度只是 足以包含序列。对于 字符串文字,数组元素有 键入char,并使用多字节字符的各个字节进行初始化 顺序。

    注2:

    引用章节§6.3.2.1/P3,

    除非它是sizeof 运算符、_Alignof 运算符或 一元 & 运算符,或者是用于初始化数组的字符串文字,具有 type ''array of type'' 被转换为一个类型为 ''pointer to type'' 的表达式,它指向 到数组对象的初始元素,并且不是左值。

    注3:

    引用章节§6.2.4/P3

    一个对象,其标识符在没有存储类说明符的情况下声明 _Thread_local,并且可以使用外部或内部链接或 使用存储类 说明符static,具有静态存储持续时间。它的生命周期是整个执行 程序,其存储值仅在程序启动前初始化一次。

    【讨论】:

      【解决方案4】:
      • 静态 c 函数能否返回本地 char 数组?不,这会导致未定义的行为。
      • 您的代码是否返回本地字符数组?不,它返回一个指向字符串字面量的指针,该字面量位于只读内存中。这个内存不是本地的。
      • 你能从任何函数返回一个指向字符串文字的指针吗?是的。
      • 这里的常规函数​​和内部链接static 之间有区别吗?没有。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-01
        • 2011-01-16
        • 1970-01-01
        • 2018-05-09
        • 1970-01-01
        • 2015-08-17
        相关资源
        最近更新 更多