在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字符串吗?
答案 0 :(得分:6)
功能(此处为静态)的链接根本不重要。也不是"字符串"回;而是返回的是指向char的指针。将指针返回到字符串文字的第一个字符是完全合法的 - 字符串文字保证在程序的整个持续时间内存在。 C11 6.4.5p6声明此处使用的字符串文字用于初始化*" 静态存储持续时间和长度的[匿名]数组足以包含序列&#34 ;.静态存储持续时间意味着其"生命周期是程序的整个执行,其存储值仅在程序启动之前初始化一次。" (C11 6.2.4p3)
看起来奇怪的是字符串文字末尾的\0
,因为文字字符串总是以0结尾,所以基本上"VERBOSE\0"
只是用2个零字节而不是通常的1个字节终止;该字符串的strlen
将返回7
,就像返回"VERBOSE"
一样,依此类推。
答案 1 :(得分:5)
你错了,返回的指针没有指向本地char array
,而是指向string literal
,即static
进程。
6.4.5字符串文字
<强>语义学对强>
在转换阶段7中,将值为零的字节或代码附加到每个多字节 由字符串文字或文字产生的字符序列.78)多字节字符 然后,序列用于初始化静态存储持续时间和长度的数组 足以包含序列。[...]
Emèphasismine
答案 2 :(得分:3)
静态c函数可以返回一个本地字符数组吗?
TL; DR 无论static
还是extern
,函数都不应返回本地数组,因为它无法有效使用。
现在,详细说明问题,
....静态函数?
你在那里,static
与“功能”的链接相关联,而不是返回值或类型。
此处,static
存储说明符表示该函数具有内部链接,即只能从翻译单元访问。
相关C11
,章节§6.2.2
如果对象或函数的文件范围标识符的声明包含存储类 说明符
static
,标识符具有内部链接。
OTOH,return
语句如
return "FATAL\0";
return "DEBUG\0"; ///and so on
实际上将指针返回到string literal的第一个元素,根据定义,它具有静态存储持续时间注1 ,因此返回值为
char *
注2 注1:
引用C11
,章节§6.4.5/ P6
在转换阶段7中,将值为零的字节或代码附加到每个多字节 由字符串文字或文字产生的字符序列。 78)多字节字符 然后序列用于初始化静态存储持续时间数组和长度 足以包含序列。对于字符串文字,数组元素具有 键入
char
,并使用多字节字符的各个字节进行初始化 序列
注2:
引用章节§6.3.2.1/ P3,
除非它是
sizeof
运算符,_Alignof
运算符或者&
运算符的操作数。 一元static
运算符,或者是用于初始化数组的字符串文字,一个表达式 type ''类型''的数组被转换为类型''指向类型''指针的表达式 到数组对象的初始元素并且不是左值。
注3:
引用章节§6.2.4/ P3
在没有存储类说明符的情况下声明其标识符的对象 _Thread_local,与外部或内部链接或与存储类 说明符
id | key | quantity | product name 1 | menu1 | 2 | Burgers 2 | menu1 | 2 | Drinks 3 | menu1 | 1 | adds on 4 | menu2 | 3 | Burgers 5 | menu2 | 3 | Drinks 6 | menu2 | 2 | adds on
具有静态存储持续时间。它的一生就是整个执行 程序,其存储值仅在程序启动前初始化一次。
答案 3 :(得分:2)
static
常规函数之间是否存在差异?否。