以下是src1.c的内容:
#include <stdio.h>
extern int w;
//int go(char); // no need to declare here. WHY????
main(){
char a='f';
go(a);
printf("%d\n", w);
}
这是src2.c的内容:
#include <stdio.h>
int w = 99;
int go(char t){
printf("%c\n%d\n",t,sizeof(t));
}
为什么在Linux中编译go
文件后,必须在src1.c
文件中声明 cc src1.c src2.c;
函数?
go
编译器是否将src2.c
函数的定义从#include <stdio.h>
int go(char); // need to declare here, because if not, arguments of go will be promoted to intS and they would conflict with char parameters defined in go. Error is droped!
main(){
char a='f';
go(a);
}
int go(char t){
printf("%c\n%d\n",t,sizeof(t));
}
文件放在main函数的代码之上,这样就不需要声明了?
我这样做:
int
所以每个人都说,在没有原型的情况下传递任何数量和类型的参数是错误的。在这种情况下,它们被提升为#include <stdio.h>
int go(int t){
printf("%d\n%d\n",t,sizeof(t));
}
,但必须同意定义中指定的那些。
我做了一些测试,发现即使它编译没有错误也无法正常工作。
SRC1:
#include <stdio.h>
int go(int); //if I omit this prototype, program outputs 1 which is far from correct answer :)
main(){
double b=33453.834;
go(b);
}
sr2.c:
{{1}}
所以最后答案只能是未定义的行为。
感谢Maxim Skurydin
答案 0 :(得分:7)
在使用函数之前,确实没有必要使用函数原型,但这是C早期的一个怪癖。
当没有原型存在时,编译器无法检查传递给函数或由函数返回的实际类型,这在使用和声明不匹配的情况下可能非常糟糕。
当调用go
时编译器看不到go(b);
的原型时,它假定它具有以下原型int go(<any number of arguments can be there>)
。在函数调用之前对参数执行默认参数提升。当然,如果另一个转换模块中没有函数go
,则会出现链接器错误。
来自c99标准:
6.5.2.2函数调用
如果表示被调用函数的表达式具有类型 不包括原型,执行整数促销 每个参数和类型为float的参数都被提升为 双。这些被称为默认参数促销。如果 参数个数不等于参数个数 行为未定义。如果使用类型定义函数 包括原型,并且原型以省略号结束 (,...)或促销后的参数类型不是 兼容参数的类型,行为是 未定义。如果使用不支持的类型定义函数 包括原型,以及促销后的参数类型 与促销后的参数不兼容, 除以下情况外,行为未定义:
- 一个晋升 type是有符号整数类型,另一个是提升类型 对应的无符号整数类型,该值可表示为 两种类型;
- 两种类型都是合格或不合格的指针 字符类型或void的版本。
6.3.1.1布尔,字符和整数
2 /如果int可以表示原始类型的所有值,则为值 转换为int;否则,它将转换为unsigned int。 这些被称为整数促销 .48)所有其他类型都是
<强>更新强>
编译器是否从上面的src2.c文件中放入了go函数的定义 主函数的代码,以便声明不会 需要?
不,它没有放任何东西。上述标准的引用表明不需要原型。每个文件都是独立编译的,因此在编译src1.c时,它对src2.c和go
函数定义内部一无所知。
所以每个人都说,通过任何数量和类型都是可行的 没有原型的论证是错误的。他们被提升为 在这种情况下,intS,但必须同意在中指定的那些 定义
这是可能的,并且在系统范围内的更改之后我遇到了一些模糊的错误,这些错误在没有任何警告的情况下编译得很好(实际上,它是未定义的行为)。
同样,由于每个* .c文件都是独立编译的,现在可以检查另一个翻译单元中定义的go
函数的参数数量及其类型。如果函数接受了您提供的更多参数,则“未使用”参数将填充一些随机数据。你应该记住,如果参数不匹配 - 它是未定义的行为,这意味着任何事情都可能发生。
答案 1 :(得分:0)
默认
go()将为int go()
。 即返回int并接受任意数量的参数。所以你的实际功能与默认功能类型匹配。
答案 2 :(得分:0)
当您使用这两个源文件创建一个可执行文件时,最终的可执行文件将具有
go()
的定义,所以不需要它。
但最好将declaration
放在header file
中,然后在两个源文件中包含该头文件
这里是头文件someheader.h
#ifndef __SMH_
#define __SMH_
int go(char);
#endif
现在像这样包含它
#include "someheader.h"
<{1}}和src1.c
中的
答案 3 :(得分:0)
首先,函数声明应放入头文件中。现在回答你的问题:
当您编译这两个文件时,在链接时,链接器会在src2.o中找到go()的符号定义,从而解析可执行文件中的符号引用,这就是您的程序工作的原因。
您正在尝试使用sizeof()
这是一个编译时运算符,因此它会输出1
,因为您在字符上起诉它。
您还将一个整数值> gt; 255传递给char变量,这将导致溢出,而t将存储1789modulo255。
答案 4 :(得分:0)
当编译器在str1.c中看到go()
时,它假定该函数在别处定义。它仅在链接器搜索链接时
go()
的定义。
我想,你是分别编译两个文件并将它们链接在一起,这很好。因为在链接时,go()
的定义存在。
如果您尝试单独编译str1.c(gcc str1.c
而不是gcc -c str1.c
),则会发现链接器找不到go()
的错误。
更新:
即使编译器的隐式声明也不符合标准(因为C99)。
从技术上讲,如果在编译器看到它的定义之前调用它,那么每个函数都应该有一个原型而不管它的返回类型。隐式声明一个int返回函数不再有效(在C89中有效),并且自C99(和C11)以来已被删除。
虽然,大多数编译器仍然只发出警告,而不是错误。但是如果某些编译器由于函数原型不存在而拒绝编译,那么你就不能抱怨它,因为它不符合标准。