C 允许;压倒一切;具有内部链接的零初始化对象

C 允许;压倒一切;具有内部链接的零初始化对象,c,static,language-lawyer,extern,linkage,C,Static,Language Lawyer,Extern,Linkage,我正在为单元测试设计一个微框架,希望能够为客户提供定义“测试套件名称”的能力。因此,我有一个名为test\u suite.h的头文件: static const char *const test_suite_name; static inline void run_all_tests(void){ printf("Running "); if(!test_suite_name){ printf("unnamed suite"); } else {

我正在为单元测试设计一个微框架,希望能够为客户提供定义“测试套件名称”的能力。因此,我有一个名为
test\u suite.h
的头文件:

static const char *const test_suite_name;

static inline void run_all_tests(void){
    printf("Running ");
    if(!test_suite_name){
        printf("unnamed suite");
    } else {
        printf("%s suite", test_suite_name);
    }
    //run tests
}
其目的是允许客户端“覆盖”测试套件名称,如下所示:

#include "test_suite.h"

extern const char *const test_suite_name = "suite1";
我认为这种用法的行为定义明确,因为
静态常量char*const test\u suite\u name构成一个暂定定义,然后
extern const char*const test\u suite\u name=“suite1”构成外部定义。由于
6.2.2(p4)
,因此没有链接不一致:

对于使用存储类说明符
extern
在 该标识符的先前声明可见的范围,31)如果 先前的声明指定了内部或外部链接 后面声明中标识符的链接是 与先前声明中指定的链接相同

我做了一些实验:

  • :
  • 打印以下错误消息:

    error: redefinition of 'const char* const suite_name'
     extern const char *const suite_name = "some suite";
    

  • :
  • 工作完全正常,不产生任何警告

  • gcc7.4.0
    在我的机器上
  • 产生警告:

    warning: ‘test_suite_name’ initialized and declared ‘extern’
    
    问题:上述代码的行为是否定义良好

    我非常确定,如果编写以下代码,行为将是未定义的:

    #include "test_suite.h"
    
    const char *const test_suite_name = "suite1"; //without extern
    
    由于
    6.2.2(p5)
    (强调我的):

    如果函数的标识符声明没有 存储类说明符,其链接的确定与 它是用存储类说明符
    extern
    声明的如果 对象标识符的声明具有文件作用域且没有 存储类说明符,其链接是外部的。


    因此,
    static const char*const test_suite_name带有内部链接和
    const char*const test_suite_name=“suite1”带有外部链接。

    使用静态链接

    实际上,您可以使用静态而不是外部。 在Ubuntu下使用gcc进行快速测试:

    #include "test_suite.h"
    
    static const char *const test_suite_name = "huhu";
    
    int main() {
          run_all_tests();
          return 0;
    }
    
    如果我编译时使用:

    gcc -Wall -Wpedantic -Wextra mytest.c -o mytest
    
    gcc mytest2.c -o mytest2
    
    它作为输出给出:

    Running huhu suite
    
    省略静态的

    如果您不小心忘记指定static,那么它应该会给出一个编译时错误。因此,如果我将此行更改为:

    const char *const test_suite_name = "huhu";
    
    试着像这样编译它:

    gcc -Wall -Wpedantic -Wextra mytest2.c -o mytest2
    
    将显示此错误消息:

    mytest2.c:3:19: error: non-static declaration of ‘test_suite_name’ follows static declaration
     const char *const test_suite_name = "huhu";
                       ^~~~~~~~~~~~~~~
    In file included from mytest2.c:1:
    test_suite.h:3:26: note: previous declaration of ‘test_suite_name’ was here
     static const char *const test_suite_name;
    
    由于这是一个错误,如果使用以下方法编译,也会输出该错误:

    gcc -Wall -Wpedantic -Wextra mytest.c -o mytest
    
    gcc mytest2.c -o mytest2
    
    错误消息的屏幕截图


    您可能想看看这里的讨论:@StephanSchlecht-Nice-catch。这个问题正是关于我正在考虑的案例。可能将
    extern
    替换为
    static
    是一个错误workaround@StephanSchlecht但完全使用存储类说明符是UB。有没有办法在这种情况下发出警告<代码>-Wall-Wextra-pedantic
    不会发出它。1。可能需要一个
    -xc
    来告诉编译器源文件是用c.2编写的。ideone似乎没有公开编译器警告,但这确实意味着编译器不会产生警告。@StephanSchlecht是的,
    静态
    应该可以,因为没有链接不一致。没有指定存储类编译时没有警告,即使它是UB(链接不一致)。无论如何,没有指定存储类的情况不是违反约束,因此不需要诊断消息。是的,你说得对。框架的使用者不应该忽略静态,因为由于标准,行为是未定义的,通常应该更好地避免。此处§6.2.2(7)适用:如果在翻译单元内,同一标识符同时出现在内部和外部链接中,则行为未定义。当然也有一些C编译器可以编译它而不会出现错误消息。但这对消费者来说不是个问题吗?