按照

2014-10-08 44 views
4

我想知道如果下面的C代码段,其中的f定义不能重复fstatic联动的,是正确的声明和定义之间的联动:按照

static int f(int); 

int f(int x) { return x; } 

锵不排放任何警告它。我读了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. 

回答

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;见6.7.1。
31)按照6.2.1的规定,后面的声明可能会隐藏事先声明。

+3

我不明白为什么第二个片段需要诊断。与@BlueMoon引用的相反,这个“唯一”似乎是UB。 – 2014-10-08 14:20:02

+0

@JensGustedt:谢谢,我想知道,如果这是违反约束的话,UB在蓝月的答案中提到的UB应该如何发生,这就是答案。我的理解是否正确,通常,拒绝在UB上编译(在语句中)意味着编译器必须能够确定所讨论的代码是否可达,但这不适用于此,因为我们正在处理(文件范围)声明? – mafso 2014-10-08 14:40:11

+1

是的,这里是没有执行的代码,所以可达性没有多大意义:)这里的UB意味着整个程序从一开始就具有UB。可能基本上是因为标识符的链接可能出错了。尽管如此,将UB作为UB是有点跛脚的,这在编译时很容易检测到,所以我认为违反约束条件是有条件的。 – 2014-10-08 15:01:43

5

C11,6.2.2,7他们都是不确定的行为,

如果在翻译单元中出现同一标识符,并且内部和外部链接均为 ,则行为未定义。

甲函数也是标识符和默认(如静态没有任何限定词)的函数的具有外部连接。

C11,标识符

1的标识符可以表示一个对象的6.2.1作用域;功能;标签或结构的成员,工会或枚举;一个 typedef名称;标签名称;一个宏名称;或宏参数。 相同的标识符可以表示程序中不同点处的不同实体。枚举的成员被称为枚举 常量。此处不考虑宏名称和宏参数,因为在程序翻译的语义阶段之前,源文件中宏名称的出现被构成其宏定义的预处理标记序列替换。

+0

对不起,我正在将“已接受”标记移动到另一个更符合Clang行为的答案。我仍然很感谢你迅速指出,解释见6.2.2。 – 2014-10-08 14:06:44

+0

np,这是你的选择。我确实赞同Mafso的更好,因为当声明的顺序很重要时(和Mafso的+1),它会覆盖更多的差异。 – 2014-10-08 14:16:47