[英]Function declaration in C and C++
我有两个C ++文件,比如file1.cpp和file2.cpp as
//file1.cpp
#include<cstdio>
void fun(int i)
{
printf("%d\n",i);
}
//file2.cpp
void fun(double);
int main()
{
fun(5);
}
当我编译它们并将它们作为c ++文件链接时,我收到错误“undefined reference to fun(double)”。
但是,当我将其作为C文件执行时,我不会收到错误,而是打印0而不是5。
请解释原因。
此外,我想问一下在定义函数之前是否需要声明一个函数,因为
我没有在file1.cpp中声明它,但编译时没有错误。
这很可能是因为函数重载。 使用C进行编译时,对fun(double)
的调用将转换为对汇编函数_fun
的调用,该函数将在稍后阶段链接。 实际定义也有程序集名称_fun
,即使它采用int而不是double,并且链接器会在调用fun(double)
时使用它。
另一方面,C ++会破坏程序集名称,所以你会得到像_fun@int
for fun(int)
和_fun@double
for fun(double)
,以便重载工作。 链接器将看到它们具有不同的名称并喷出错误,它无法找到fun(double)
的定义。
对于第二个问题,声明函数原型总是一个好主意,通常在头文件中完成,特别是如果函数在多个文件中使用。 在编译器中应该有一个缺少原型的警告选项,gcc使用-Wmissing-prototypes
。 如果设置如此,您的代码会更好
// file1.hpp
#ifndef FILE1_HPP
#define FILE1_HPP
void fun(int)
#endif
// file1.c
#include "file1.hpp"
...
// file2.c
#include "file1.hpp"
int main()
{
fun(5);
}
然后,您的程序中不会有多个冲突的原型。
这是因为C ++允许您重载函数而C不允许。 在C ++中使用它是有效的:
double fun(int i);
double fun(double i);
...
double fun(int i) { return 1;}
double fun(double i) { return 2.1; }
但不是在C.
您无法使用C ++编译器编译它的原因是因为C ++编译器将声明视为double并尝试为其找到定义。 使用C编译器,你也应该得到一个错误,我认为你没有完全按照你在用C编译器测试时所说的那样输入代码。
要点:C ++有函数重载,C没有。
C ++(一个虐待狂的野兽,你会同意)喜欢破坏这些函数的名称。 因此,在C部分的头文件中:在顶部:
#ifdef __cplusplus
extern "C" {`
#endif
在底部:
#ifdef __cplusplus
}
#endif
这将说服它不要破坏一些名字。 看这里
或者,你可以说你的cpp
extern "C" void fun( double );
C语言的一个保留是它允许调用函数而不需要在转换中实现可见的声明 - 它只是假设这些函数的参数都是int。
在您的示例中,C ++允许重载,并且不支持隐式函数声明 - 编译器使用可见函数fun(double),并且链接器失败,因为函数fun(double)从未实现。 fun(int)具有不同的签名(在C ++中),并作为唯一符号存在,而C编译器(或链接器,取决于可见性)在将fun(int)和fun(double)声明为时会产生错误C符号。
这就是语言多年来(或不是)的演变方式。 您的编译器可能会对此问题发出警告(隐式函数声明)。
当您将函数声明为C函数时,您会看到不同的结果(在编译为C ++源文件时,它们在您的示例中被声明为C ++函数)。
C ++要求函数在使用之前声明,C不要(除非你告诉你的编译器警告你这个问题)。
当编译为C ++时,您可以拥有两个具有相同名称的函数(只要它们具有不同的参数)。 在C ++中,使用了名称修改,因此链接器可以区分这两者:
fun_int(int x);
fun_double(double x);
在C中编译时,只有一个具有特定名称的函数。
编译file1.c时,它会生成一个从堆栈中读取整数并打印它的函数。
当你编译file2.c时,它看到fun()需要一个double。 因此,它将输入参数转换为双击将其推入堆栈,然后将fun()调用插入代码中。 由于函数位于不同的编译单元中,因此不会在此处解析实际地址,而是仅在调用链接器时解析。 当调用链接器时,它会看到需要解析的fun函数并插入正确的地址,但它没有用于验证调用的类型信息。
在运行时5现在转换为双精度并推入堆栈。 然后调用fun()。 fun()从堆栈中读取一个整数然后打印出来。 因为double具有与整数不同的布局,所以将打印的是实现定义,并且取决于double和int在内存中的布局方式。
#include <stdio.h>
int Sum(int j, int f)
{
int k;
k = j + f;
return k;
}
main()
{
int i=3;
int j = 6;
int k = sum(i,j);
printf("%d",k);
}
输出为9
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.