声明和定义之间的联系的一致性

时间:2014-10-08 13:10:18

标签: c language-lawyer c11

我想知道下面的C片段是否正确,其中f的定义无法重复,fstatic链接,这是正确的:

static int f(int);

int f(int x) { return x; }

Clang不会发出任何警告。我阅读了C11标准的第6.7.1条,但没有找到我的问题的答案。

可以想象更多的问题,例如下面的t1.c和t2.c,如果答案足够普遍适用于其中一些,那将会很好,但我只是真的很关心关于上面的第一个例子。

~ $ cat t1.c
static int f(int);

int f(int);

int f(int x) { return x; }
~ $ clang -c -std=c99 -pedantic t1.c
~ $ nm t1.o
warning: /Applications/Xcode.app/…/bin/nm: no name list
~ $ cat t2.c
int f(int);

static int f(int);

int f(int x) { return x; }
~ $ clang -c -std=c99 -pedantic t2.c
t2.c:3:12: error: static declaration of 'f' follows non-static declaration
static int f(int);
           ^
t2.c:1:5: note: previous declaration is here
int f(int);
    ^
1 error generated.

2 个答案:

答案 0 :(得分:6)

链接规则有点混乱,功能和对象不同。简而言之,规则如下:

  • 第一个声明确定了链接。
  • static表示内部联系。
  • extern表示已声明的链接,如果没有声明,则表示外部。
  • 如果没有给出它们,则它与函数的extern和对象标识符的外部链接(在同一个翻译单元中具有定义)相同。

所以,这是有效的:

static int f(int); // Linkage of f is internal.

int f(int); // Same as next line.

extern int f(int); // Linkage as declared before, thus internal.

int f(int x) { return x; }

另一方面,这是未定义的行为(参见C11(n1570)6.2.2 p7):

int f(int); // Same as if extern was given, no declaration visible,
            // so linkage is external.

static int f(int); // UB, already declared with external linkage.

int f(int x) { return x; } // Would be fine if either of the above
                           // declarations was removed.

C11 6.2.2涵盖了大部分内容。从N1570草案:

  

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

     

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

     

(5)如果函数的标识符声明没有存储类说明符,则确定其链接与使用存储类说明符extern声明的完全相同。如果对象的标识符声明具有文件范围而没有存储类说明符,则其链接是外部的。

     

30)函数声明只有在文件范围内才能包含static-class说明符static;见6.7.1。
   31)如6.2.1所述,后面的声明可能会隐藏先前的声明。

答案 1 :(得分:5)

根据 C11,6.2.2,7 ,它们都是未定义的行为

  

如果在翻译单元中,同时出现两个标识符   内部和外部联系,行为未定义。

一个函数也是一个标识符,默认情况下一个函数(没有像 static 这样的限定符)有外部链接。

C11,6.2.1标识符范围

  

1标识符可以表示对象;功能;标签或结构,联合或枚举的成员;一个   typedef名称;标签名称;一个宏名;或宏参数。该   相同的标识符可以表示不同点的不同实体   该程序。枚举的成员称为枚举   不变。宏名称和宏参数不再考虑   这里,因为在程序翻译的语义阶段之前   源文件中出现的宏名称被替换为   预处理构成其宏定义的令牌序列。