函数原型告诉编译器函数的参数个数,参数类型以及函数的返回类型.通过使用这些信息,编译器反复校对(cross check)函数定义与函数调用的参数及其数据类型.如果我们忽略了函数原型,程序可能会带着警告编译通过,且可能会正常工作.但有些时候,它会产生一些奇怪的输出而且很难找到这些程序错误,我们看个例子
#include <errno.h>
#include <stdio.h>
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;
}
上面的例子检查文件是否存在,文件名通过命令行输入,如果给定的文件存在,则程序打印出"file exist",否则打印出对应的错误信息.我们为程序提供一个文件系统上不存在的文件名,然后在一个x86_64架构的机器上看看程序输出:
[narendra@/media/partition/GFG]$ ./file_existence hello.c
Segmentation fault (core dumped)
为什么改程序崩溃了,他本该显示对应的错误信息的.改程序在x86架构的机器上运行良好,但在x86_64的机器上就崩溃了.我们看看代码究竟出了什么问题.仔细检查一下程序,我故意的没有包含"strerror()"的原型.这个函数返回指向字符的指针,它将传递给函数的错误信息打印出来.注意x86架构是ILP-32模型的,这意味着整数,指针和长整型都是32位的,这就是为什么在这个架构上程序能够正常运行.而x86_64是LP-64模型的,这意味着长整型和指针都是64位的.在C语言中,当不提供函数原型时,编译器假定函数返回整形.在我们的例子中,我们没有包含string.h头文件(strerror的原型在其中声明),这就是为什么编译器假定函数返回的是整形.但是其返回类型是指向字符的指针.在x86_64中,指针是64位,整形是32位,这就是为什么当从函数返回时返回的地址被截断了(32位是x86_64上的整形大小),截断后的地址是错误的,当我们试着解引用截断后的地址时,结果就造成了段错误.
现在在程序中加入头文件string.h后并检查输出,程序就会正常工作.
[narendra@/media/partition/GFG]$ ./file_existence hello.c
No such file or directory
再看下面一个例子
#include <stdio.h>
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的机器上函数的返回值被截断了.
文章由Narendra Kangralkar编辑,如果你发现任何错误或者你想分享一些与上面讨论的话题有关系的信息,请在下面评论
下面是一些对文章有意思的评论:
我试了一下第二个例子,在x86_64的机器上他可以运行,尽管会有警告.在我的机器上,sizeof(int*)=8而且sizeof(int)=4.我猜在malloc返回时会有截断,但是被截断的值(就是要写入的内存地址)还是正确的.但我不确定,希望看看作者的答案.