函数原型告诉编译器有关函数采用的参数数量,参数的数据类型和函数的返回类型的信息。通过使用此信息,编译器将对函数参数及其数据类型与函数定义和函数调用进行交叉检查。如果我们忽略函数原型,程序可能会编译并显示警告,并且可能会正常运行。但是有时候,它将给出奇怪的输出,并且很难找到这样的编程错误。让我们看例子
#include
#include
int main(int argc, char *argv[])
{
FILE *fp;
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "%s\n", strerror(errno));
return errno;
}
printf("file exist\n");
fclose(fp);
return 0;
}
上面的程序检查从命令行提供的文件是否存在,如果给定的文件存在,则程序打印“文件存在”,否则打印适当的错误消息。让我们提供一个文件名,该文件名在文件系统中不存在,并检查x86_64体系结构上的程序输出。
[narendra@/media/partition/GFG]$ ./file_existence hello.c
Segmentation fault (core dumped)
为什么该程序崩溃了,它应该显示相应的错误消息。该程序在x86架构上可以正常运行,但是在x86_64架构上会崩溃。让我们看看代码出了什么问题。仔细检查程序,故意没有包含“ strerror()”函数的原型。此函数返回“指针指向字符”,这将打印错误消息,具体取决于传递给此函数的errno。请注意,x86体系结构是ILP-32模型,意味着整数,指针和long均为32位宽,这就是为什么程序可以在此体系结构上正常工作的原因。但是x86_64是LP-64模型,这意味着long和指针的宽度为64位。在C语言中,当我们不提供函数原型时,编译器会假定函数返回一个integer 。在我们的示例中,我们没有包含“字符串.h”头文件(在此文件中声明了strerror的原型),这就是为什么编译器假定函数返回整数的原因。但是它的返回类型是指向字符的指针。在x86_64中,指针是64位宽,整数是32位宽,这就是为什么从函数返回时,返回的地址被截断(即32位宽的地址,它是x86_64上整数的大小),这是无效的,并且当我们尝试取消引用该地址,结果是分段错误。
现在包括“字符串.h”头文件并检查输出,程序将正常运行。
[narendra@/media/partition/GFG]$ ./file_existence hello.c
No such file or directory
再考虑一个例子。
#include
int main(void)
{
int *p = malloc(sizeof(int));
if (p == NULL) {
perror("malloc()");
return -1;
}
*p = 10;
free(p);
return 0;
}
上面的代码在IA-32模型上可以正常工作,但在IA-64模型上将失败。该代码失败的原因是我们尚未包含malloc()函数的原型,并且在IA-64模型中返回的值被截断了。