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

Pas*_*uoq 4 c language-lawyer c11

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

static int f(int);

int f(int x) { return x; }
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

maf*_*fso 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; }
Run Code Online (Sandbox Code Playgroud)

另一方面,这是未定义的行为(参见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.
Run Code Online (Sandbox Code Playgroud)

其中大部分内容包含在C11 6.2.2中.从N1570草案:

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

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

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

30)函数声明只有在文件范围内时才能包含存储类说明符static; 见6.7.1.
31)如6.2.1中所述,后面的声明可能隐藏先前的声明.

  • 我不明白为什么第二个片段需要诊断.与@BlueMoon引用的相反,似乎这个"唯一"是UB. (3认同)

P.P*_*.P. 5

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

如果在翻译单元内,同一标识符同时出现内部和外部链接,则行为未定义.

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

C11,6.2.1标识符的范围

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