red*_*gon 73 c gcc compiler-errors
我不明白为什么这段代码会编译?
#include <stdio.h>
void foo() {
printf("Hello\n");
}
int main() {
const char *str = "bar";
foo(str);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
gcc甚至没有发出警告,我向foo()传递了太多的参数.这是预期的行为吗?
eca*_*mur 83
在C中,使用空参数列表声明的函数在被调用时接受任意数量的参数,这些参数受常规算术促销的约束.调用者有责任确保提供的参数适合函数的定义.
要声明一个零参数的函数,你需要写void foo(void);.
这是出于历史原因; 最初,C函数没有原型,因为C是从无类型语言B演变而来的.添加原型时,原始无类型声明留在语言中以便向后兼容.
要让gcc警告空参数列表,请使用-Wstrict-prototypes:
如果声明或定义函数而不指定参数类型,则发出警告.(如果前面有一个指定参数类型的声明,则允许使用旧式函数定义而不发出警告.)
Eri*_*hil 53
由于遗留原因,()为参数列表声明函数本质上意味着"在调用函数时找出参数".要指定函数没有参数,请使用(void).
编辑:我觉得我因为年老而在这个问题上声名狼借.只是让孩子们知道以前编程是什么样的,这是我的第一个程序.(不是C;它告诉你我们之前必须使用的东西.)
oua*_*uah 10
void foo() {
printf("Hello\n");
}
foo(str);
Run Code Online (Sandbox Code Playgroud)
在C中,此代码不违反约束(如果它以原型形式定义void foo(void) {/*...*/}),并且由于没有约束违规,编译器不需要发出诊断.
但是根据以下C规则,此程序具有未定义的行为:
从:
(C99,6.9.1.1p7)"如果声明符包含参数类型列表,则列表还指定所有参数的类型;这样的声明符也可用作函数原型,以便稍后调用同一转换单元中的同一函数.如果声明者包含一个标识符列表,142)参数的类型应在下面的声明列表中声明."
该foo功能不提供原型.
从:
(C99,6.5.2.2p6)"如果表示被调用函数的表达式具有不包含原型的类型[...]如果参数数量不等于参数数量,则行为未定义."
该foo(str)函数的调用是不确定的行为.
C不强制实现为调用未定义行为的程序发出诊断,但您的程序仍然是错误的程序.
C99标准(6.7.5.3)和C11标准(6.7.6.3)均声明:
标识符列表仅声明函数参数的标识符.函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数.函数声明符中的空列表不是该函数定义的一部分,它指定不提供有关参数数量或类型的信息.
由于foo的声明是定义的一部分,声明指定foo接受0个参数,因此调用foo(str)至少在道德上是错误的.但是如下所述,C中存在不同程度的"错误",编译器在处理某些"错误"时可能会有所不同.
举一个稍微简单的例子,考虑以下程序:
int f() { return 9; }
int main() {
return f(1);
}
Run Code Online (Sandbox Code Playgroud)
如果我使用Clang编译上面的代码:
tmp$ cc tmp3.c
tmp3.c:4:13: warning: too many arguments in call to 'f'
return f(1);
~ ^
1 warning generated.
Run Code Online (Sandbox Code Playgroud)
如果我使用gcc 4.8进行编译,即使使用-Wall,我也不会收到任何错误或警告.之前的回答建议使用-Wstrict-prototypes,它正确地报告f的定义不是原型形式,但这不是重点.C标准允许以非原型形式的函数定义,例如上面的标准,并且标准清楚地声明该定义指定该函数采用0参数.
现在有一个约束(C11 Sec.5.5.2.2):
如果表示被调用函数的表达式具有包含原型的类型,则参数的数量应与参数的数量一致.
但是,在这种情况下,此约束不适用,因为函数的类型不包括原型.但是这里是语义部分中的后续语句(不是"约束"),它确实适用:
如果表示被调用函数的表达式具有不包含原型的类型...如果参数数量不等于参数数量,则行为未定义.
因此,函数调用确实导致未定义的行为(即,程序不是"严格符合").但是,标准仅要求实现在违反实际约束时报告诊断消息,并且在这种情况下,不存在违反约束的情况.因此,gcc不需要报告错误或警告以便成为"符合要求的实现".
所以我认为问题的答案,为什么gcc允许它?是gcc不需要报告任何内容,因为这不是违反约束.此外,即使使用-Wall或-Wpedantic,gcc也不会声称报告各种未定义的行为.它是未定义的行为,这意味着实现可以选择如何处理它,并且gcc选择在没有警告的情况下编译它(显然它只是忽略了参数).
| 归档时间: |
|
| 查看次数: |
4961 次 |
| 最近记录: |