我正在学习C.当我学习一些章节时,我做了一些小实验。
主要问题是我无法理解为什么代码执行的结果会跟随,因为它不符合我认为的代码。
源代码:
#include <stdio.h>
int imax();
int main(void)
{
printf("%zd %zd\n", sizeof(int), sizeof(double));
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3));
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3.0, 1000.0));
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3.0));
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(1000.0));
return 0;
}
int imax(n, m)
int n, m;
{
return (n > m ? n: m);
}
输出:
我无法理解的是为什么最后三个印刷语句打印相同的单词! 我知道我正在做一个测试,用于研究在使用不关心形式参数类型的旧样式声明函数时会发生什么。 在上下文中,我设计了四种情况,调用函数的实际参数与被调用函数的形式参数的要求不匹配。
我知道这与C中的堆栈机制有关。我尽力搜索原因。在我看来,最后三个印刷语句应该表现不同。事实上,我认为声明imax(3.0,1000.0)可能与imax(3.0)或imax(1000.0)相同,但两者都不可能相同!
答案 0 :(得分:5)
int imax();
和
int imax(n, m)
int n, m;
{
return (n > m ? n: m);
}
是一种古老的C代码。不要使用它,因为其中一个问题是它没有进行任何函数参数检查。
正确的标准化代码是
int imax( int n, int m)
和
int imax( int n, int m)
{
return (n > m ? n: m);
}
答案 1 :(得分:2)
您的代码不符合C99或C11标准。不要使用任何旧版本(例如K&R C)。
这是错误或至少undefined behaviour,你应该是scared。
你真的应该有一个原型(C99或C11风格)每个功能,你需要有一个声明<每个使用过的函数的/ em>。
(在旧的C89或K&R C中,这不是强制性的;但今天您应该至少在C99中编码)
实际上你应该编码:
#include <stdio.h>
int imax(int, int);
int main(void)
{
printf("%zu %zu\n", sizeof(int), sizeof(double));
// WRONG CODE BELOW: the compiler should reject it or emit warnings.
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3));
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3.0, 1000.0));
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3.0));
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(1000.0));
return 0;
}
int imax(int n, int m)
{
return (n > m ? n: m);
}
并且最近的编译器会拒绝该代码。确保在启用所有警告的情况下进行编译,例如gcc -Wall -Wextra -g
)
真正发生的事情(在声明的函数签名和不正确的调用之间不匹配)取决于ABI和calling conventions。今天,在x86-64 with SVR4/Linux ABI上,一些参数通过寄存器传递。因此,即使您强制编译器(例如,通过将某些函数地址转换为函数指针并使用它),将会发生未定义和特定于实现的情况。
如果您预订显示K&R C代码,则需要使用更好的图书显示C99代码(或C11)。书签还有一些C reference site(至少为C99)。
阅读C11标准,例如: n1570
在2018年,你应该避免在K&amp; R C中编码(除非被迫)。
我知道这与C中的堆栈机制有关。
这是一种误解。实现不是必需才能拥有call stack。并且最近的实现不会传递堆栈上的每个参数(大多数调用约定可能使用寄存器,详细信息是特定于实现的。)
在Ubuntu上,我建议编译with gcc -Wall -Wextra -g
-all警告和调试信息。可能eliciting语言标准,例如添加-std=c99
或-std=gnu11
。如果您想要进行基准测试,请启用更多compiler optimizations(例如使用-O2
)
答案 2 :(得分:2)
编辑:更确切地说,这是因为您使用K&amp; R风格实现了您的功能。改变&#34; int imax(n,m)&#34; to&#34; int imax(int n,int m)&#34;太
你必须更正imax
的原型,因为括号中没有任何东西的功能允许这样的事情。
当函数的参数中没有任何内容时,该函数可以接受任意数量的参数。但这是无用的和危险的,因为它可能导致安全漏洞。它没用,因为你无法检索正确数量的参数,这是一个安全漏洞,因为你可以放置任意数量的参数,这可能会粉碎你的调用堆栈。
只需
int imax();
并转换
int imax(int n, int m);
现在,你的函数必须带两个参数,如果不是这样的话,编译就会失败。
如果你想要一个没有参数的函数,可以在其中加入void以确保函数不能用参数调用
int funcname(void);