我正在为我大学的编译/语言课程编写一个玩具C编译器。
我正在尝试充实C中符号解析的语义,并提出了这个测试用例,我尝试对常规编译器clang& GCC。
void foo() { }
int main() { foo(5); } // foo has extraneous arguments
大多数编制者似乎只是警告无关的论点。
问题:这背后的根本原因是什么?
对于我的符号表生成/解析阶段,我正在考虑一个函数是一个带有返回类型的符号,以及几个参数化的参数(基于语法),每个参数都有一个相应的类型。
感谢。
答案 0 :(得分:32)
原型中带有 no 列出的参数的函数被认为具有不确定编号,而不是零。
如果你真的想要零参数,它应该是:
void foo (void);
空列表变体是古代C的延续,甚至在ANSI获得它之前,你就有这样的东西:
add_one(val)
int val;
{
return val + 1;
}
(int
是声明符之外指定的默认返回类型和参数类型。)
如果你正在做一个玩具编译器并且你并不担心每个微小的C99的符合性,我只是抛出那个选项并需要某种参数列表
它会让你的生活变得更加轻松,我怀疑人们是否需要使用这个“功能”。
答案 1 :(得分:17)
它是为了向后兼容古代 C编译器。在地球冷却之前,所有C函数声明看起来大致如下:
int foo();
long bar();
等等。这告诉编译器该名称是指一个函数,但不指定了有关参数数量或类型的任何内容。原始(1989)C标准中最大的变化可能是添加了“函数原型”,它允许声明参数的数量和类型,因此编译器可以检查调用函数时传递的内容。为了保持现有代码的兼容性,他们决定一个空参数列表将保留其现有含义,如果你想声明一个不带参数的函数,你必须添加void
来代替参数列表:int f(void);
。
请注意,在C ++中同样是 not true - C ++消除旧样式函数声明,并要求指定所有参数的数量和类型 1 。如果你声明没有参数的函数,这意味着它不接受任何参数,如果你试图传递任何参数,编译器会抱怨(除非你也重载了函数,所以有另一个函数与同名)可以参数)。
1 虽然您仍然可以使用省略号来获取带有变量参数列表的函数 - 但是当/如果这样做,您只能将POD类型作为参数传递。
答案 2 :(得分:5)
您尚未提供foo
函数的原型,因此编译器无法强制执行。
如果你写了:
void foo(void) {}
然后你将提供一个不带参数的函数原型。
gcc的-Wstrict-prototypes
会抓住这个。如果出现错误,请使用-Werror=strict-prototypes
。标准从不指定某些内容应该是警告还是错误。
答案 3 :(得分:4)
为什么这在C中合法?
首先澄清C标准不使用 legal 这个词。
在C语言中,此程序不严格符合:
void foo() { }
int main() { foo(5); } // foo has extraneous arguments
编译此程序时,由于函数调用foo(5)
,不需要诊断:没有约束违规。但是使用参数调用函数foo
会调用未定义的行为。与任何调用未定义行为的程序一样,它并不严格符合,编译器有权拒绝翻译该程序。
在C标准中,带有空参数列表的函数声明意味着该函数具有未指定数量的参数。但是带有空参数列表的函数定义意味着该函数没有参数。
以下是C标准中的相关段落(全部强调我的):
(C99,6.7.5.3p14)“标识符列表仅声明函数参数的标识符。函数声明符中的空列表是该函数的定义的一部分,指定函数具有没有参数。“
C标准的段落表示foo(5)
调用是未定义的行为是这样的:
(C99,6.5.2.2p6)“如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并对type float被提升为double。这些被称为默认参数 促销活动。 如果参数的数量不等于参数的数量,则 行为未定义。“
从(C99,6.9.1.1p7),我们知道foo
的定义不提供原型。
(C99,6.9.1.1p7)“如果声明符包含参数类型列表,则列表还指定所有参数的类型;这样的声明符也可以作为函数原型,以便稍后调用同一函数如果声明者包含一个标识符列表,则参数的类型应在下面的声明列表中声明。“
请参阅委员会对缺陷报告#317的答复,以获得有关该主题的权威答案: