C函数声明和定义在多个源文件中

时间:2012-10-26 11:05:07

标签: c declaration

以下是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

5 个答案:

答案 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)以来已被删除。

虽然,大多数编译器仍然只发出警告,而不是错误。但是如果某些编译器由于函数原型不存在而拒绝编译,那么你就不能抱怨它,因为它不符合标准。