从C库调用C ++函数指针

时间:2010-11-16 19:23:18

标签: c++

我有一个只有静态成员的类。

我想在退出时使用“atexit”库函数注册其成员函数之一(下面的代码中的VerifyClean)。

C++ FQA表示我必须为我希望以这种方式注册的函数指定extern“C”,如下例所示。

class Example
{
public:
    static void Initialize();
    static void DoDirtyStuff {++dirtLevel;}
    static void CleanUpStuff {--dirtLevel;}
private:
    static void VerifyClean();
    // DOESN'T COMPILE: extern "C" static void VerifyClean();
    static int dirtLevel;
}

int Example::dirtLevel;

extern "C" void Example::VerifyClean() // DO I NEED extern "C" HERE?
{
    assert(dirtLevel == 0);
}

void Example::Initialize()
{
    dirtLevel = 0;
    atexit(&VerifyClean);
}

我真的必须使用extern“C”吗?

如果我将“atexit”替换为非库函数(在纯C中实现),答案是否会改变?

如果函数VerifyClean是公共的并且我决定直接从C ++代码调用它,我会得到链接错误或运行时崩溃吗?我问这个是因为声明根本没有提到extern“C”,所以常规的C ++代码可能会错误地处理函数调用。这在我的MS Visual Studio 2005系统上运行正常。

5 个答案:

答案 0 :(得分:7)

可能,编译器对C和C ++代码使用不同的调用约定;然而,在实践中,这几乎从未发生过。

如果您只是想让它工作而不关心支持模糊编译器,请不要理会extern "C"。在任何广泛使用的编译器中都没有必要。

如果你想要绝对迂腐,或者需要支持一个迂腐的编译器,写一个包装器:

extern "C" static void ExampleVerifyClean()
{
  Example::VerifyClean();
}

void Example::Initialize()
{
    dirtLevel = 0;
    atexit(&ExampleVerifyClean);
}

答案 1 :(得分:1)

链接错误。

C ++执行所谓的名称修改,它生成带有类型信息的链接时函数名称。

extern C将 off 转换为更简单的标识符。

编辑:

如果所有内容都是由C ++编译器编译的,那么它就不是问题。但是如果你有一个由C编译器编译的目标文件和一个由C ++编译器编译的目标文件,那么你将遇到一些问题。

我似乎回想起需要外部“C”规范的DLL,但此时内存可能已有10年了


好。

我用一个带签名的函数掀起了一个测试用例

  

int foo(float,float)

并在3种不同的gcc调用下编译它 -

gcc test_c.c -S
g++ test.cpp -S

这两个调用在程序集中生成不同的标识符。 C ++在其通常的类型修改方法中破坏了名称。 (当然编译器可能会这样做)

然后,我将foo包裹在Extern“C”中并再次调用G ++ ......

g++ test.cpp -S

然后删除了受损的C ++名称,留下了一个普通的C unmangled名称。

虽然这里涉及其他细微之处,例如,参数的顺序被推到了堆栈上,但我根据数据来讨论这一点。

答案 2 :(得分:0)

如果没有extern“C”,您的函数名称将被编译器破坏,因此函数名称最终可能与您期望的不同。您需要使用其错位名称调用该函数(例如在Windows中使用GetProcAddress),否则您将遇到链接器错误。不同的编译器以不同的方式对其进行了修改,因此最好继续使用extern关键字。

答案 3 :(得分:0)

你可以用这个:

class yourname
{
    public:
    ...
    static void _cdecl AtExitCall ();
};

int main()
{
    ataexit( yourname::AtExitCall );
}

答案 4 :(得分:-1)

在这种情况下使用extern“ c”是一个错误,原因有两个:

  1. 要使用g ++(c ++编译器)交叉编译程序以与gcc(C编译器)链接时,请使用externC。它告诉g ++关闭函数名称修饰。显然,这不是您的情况,否则您的整个文件应位于外部“ C”
  2. 使用外部“ C”时,应使用类似于c的函数名称,例如Example_VerifyClean。 示例:: VerifyClean不是有效的C函数名称,如果不进行修改就无法存储