C 多个相同的原型合法吗?
以下代码在C 多个相同的原型合法吗?,c,prototype,language-lawyer,declaration,c99,C,Prototype,Language Lawyer,Declaration,C99,以下代码在Linux x64上使用gcc和clang编译时不会发出任何警告: #include <stdio.h> #include <stdlib.h> void foo(void); void foo(void); void foo(void); int main(void) { return 0; } #包括 #包括 void foo(void); void foo(void); void foo(void); 内部主(空) { 返回0; } 依
Linux x64
上使用gcc
和clang
编译时不会发出任何警告:
#include <stdio.h>
#include <stdlib.h>
void foo(void);
void foo(void);
void foo(void);
int main(void)
{
return 0;
}
#包括
#包括
void foo(void);
void foo(void);
void foo(void);
内部主(空)
{
返回0;
}
依我看,根据C99的以下片段,这是合法的:
引用同一对象或函数的所有声明应具有
兼容型;否则,行为是未定义的
(……)
双功能
要兼容的类型,两者都应指定兼容的返回类型
(……)
此外,参数类型列表(如果两者都存在)应在
参数数量和省略号终止符的使用情况;相应的
参数应具有兼容类型
(……)
如果两种类型的类型相同,则它们具有兼容的类型
我说得对吗?我想确保它不是UB,并且我的理解是正确的。多个相同的原型是合法的,事实上是常见的,因为在现代C语言中,函数定义通常包含该函数的原型,并且在包含头文件的范围内也包含该函数的原型。也就是说 foo.h:
void foo(int x);
#include "foo.h"
void foo(int x) {
printf("%d\n", x);
}
/* ... */
extern int global;
#include "global.h"
int global = 42;
foo.c:
void foo(int x);
#include "foo.h"
void foo(int x) {
printf("%d\n", x);
}
/* ... */
extern int global;
#include "global.h"
int global = 42;
在函数体foo
的定义和整个文件的其余部分中,有两个相同的foo()
原型。这很好
同一对象或函数的多个声明不完全相同也是可以的,只要它们是兼容的。例如
声明
void foo();
将foo
声明为接受未指定参数但不返回任何内容的函数。此声明与foo.c
和foo.h
中已经存在的声明兼容,并且可以将其添加到这些文件中的一个或两个,而不会产生任何附加效果
这也适用于对象(变量),其中一些应用程序非常常见。例如,如果要声明从多个文件访问的全局变量,则通常在头文件中声明该变量。包含该变量的C源文件和该变量的定义(也是一个声明)通常包含头,产生两个声明:
global.h:
void foo(int x);
#include "foo.h"
void foo(int x) {
printf("%d\n", x);
}
/* ... */
extern int global;
#include "global.h"
int global = 42;
global.c:
void foo(int x);
#include "foo.h"
void foo(int x) {
printf("%d\n", x);
}
/* ... */
extern int global;
#include "global.h"
int global = 42;
或者存在向前声明复合数据类型的情况:
struct one;
struct two {
struct one *my_one;
struct two *next;
};
struct one {
struct two *my_two;
}
请注意
struct one
的多个兼容但不完全相同的声明。如果不对其中一种类型进行多次声明,则无法声明这组特定的数据结构。C标准为C11。不要使用标准的无效过时版本进行讨论。如果您真的使用c99如果您真的使用c99
,您可以将其标记为c99而不是c-我听说大多数编译器仍然没有实现所有的c99
功能,更不用说C11
,所以我想知道“真的使用c99”是什么意思只要声明是等价的,就可以重复声明。例如,给定typedef int int32_2代码>,都是int函数(int,int)代码>和int32_t函数(int,int32_t)代码>是等效的。@Weather Vane,这在编译单元中是如何实现的?链接时会分配多个冲突的存储位置?关于定义中的重复原型,这一点很好-我甚至没有想到这一点!还有一件事-如果您有时间,您能否确认我对C99
或C11
标准中哪些部分描述了该行为是正确的,或者我应该在标准的另一部分中寻找解释?最近我开始喜欢阅读官方标准,一开始它们似乎很难理解,你读得越多你就理解得越多。我认为作为函数定义的void foo(){…}
与void foo(void){…}
相同,而声明void foo()
与void foo()不同(void)
(C99)。你的想法或这是否超出了本文的范围?@chux,在C99和c211中,foo()
是一个带有空[参数]标识符列表的函数声明器,而foo(void)
是一个带有单元素参数类型列表的函数声明器(其中一个元素是匿名的)。当这些参数作为函数定义的一部分出现时,它们对于函数的参数具有相同的含义,但只有包含参数类型列表的声明符可用作原型(C99 6.9.1/7;C2011 6.9.1/7;以及其他地方),无论是否在函数定义中。当然,在函数定义之外,它们有不同的含义。@chux,这也意味着采用参数的函数的K&R风格的定义不能作为该函数的原型。实际上,我在这方面的回答措辞相当谨慎,以便准确,而不必深入研究t他提出了这个问题。