为什么这在C中合法?

时间:2011-03-15 03:58:05

标签: c language-design

我正在为我大学的编译/语言课程编写一个玩具C编译器。

我正在尝试充实C中符号解析的语义,并提出了这个测试用例,我尝试对常规编译器clang& GCC。

void foo() { }
int main() { foo(5); } // foo has extraneous arguments

大多数编制者似乎只是警告无关的论点。

问题:这背后的根本原因是什么?

对于我的符号表生成/解析阶段,我正在考虑一个函数是一个带有返回类型的符号,以及几个参数化的参数(基于语法),每个参数都有一个相应的类型。

感谢。

4 个答案:

答案 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的答复,以获得有关该主题的权威答案:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_317.htm