当前位置: 首页 > 知识库问答 >
问题:

Extern“C”和C结构和typedef

柳飞飙
2023-03-14

我已经开始在win32上使用C。正如我们所知,C结构与类相同,但默认为公共成员等。。。现在我想要的是简单的C结构,它没有默认的构造函数、复制或移动操作或任何其他魔法。因为我想将其存储在文件中,还可以执行memcpy,用作字节数组等。。。所以我想用#ifdef u cplusplus在标题中定义它,如下所示。

#ifdef __cplusplus
extern "C" {
#endif

typedef struct PersonTag 
{
    int ID;
    char Name[200];

} Person, *PPerson;

#ifdef __cplusplus
}
#endif

但这只能防止函数名称的混乱。但如果在cpp文件中,struct仍然编译为cpp struct,如果在C文件中,则编译为C struct。我通过在cpp文件和C文件中包含头文件来测试它。两者都可以很好地编译,但如果我在结构体中添加构造函数,如下所示。

#ifdef __cplusplus
extern "C" {
#endif

typedef struct PersonTag 
{
    PersonTag();
    int ID;
    char Name[200];

} Person, *PPerson;

#ifdef __cplusplus
}
#endif

C编译它时不会出错,但C按其应该的方式失败。这是否意味着,即使我在#ifdef _ucplusplus中包含结构定义,它仍然会编译为C结构,并带有所有复制和移动魔法?我在想,如果struct有C风格的构造函数等,通过在“extern”C“侧定义struct,也会在cpp文件中产生err。。。所以我的问题是,有没有一种方法可以告诉c编译器(vc)考虑并编译它,就像它是纯c结构一样,没有默认构造函数,没有默认析构函数,像移动或复制之类的东西,或者其他任何东西。

我的第二个问题是关于定义cpp函数指针,并将这个函数指针传递给C函数,C函数可能会调用它。我正在使用C库(sqlite3)并传递回调函数。现在,如果我用下面的方法在cpp文件中键入def函数指针。

typedef int (*SQL_CALLBACK_FUN)(void* NotUsed, int argc, char** argv, char** azColName);

在Cpp文件中,函数可能如下所示

int Callback_GetAllPerson(void* NotUsed, int argc, char** argv, char** azColName);

而不是函数指针

SQL_CALLBACK_FUN sql_callback_fun = Callback_GetAllPerson;

现在,我将把这个“sql_callback_fun”函数指针传递给C lib函数(sqlite3_exec)。我的问题是,这里我在CPP中输入def函数指针,回调函数也将编译为CPP。现在我将它作为指针传递给C lib,C lib函数将通过指针回调这个cpp函数。

我可能会遇到运行时错误吗?或者cpp和c的函数指针(typdef)都是相同的。不管名称有多混乱?

共有3个答案

松波
2023-03-14

extern“C”与结构无关。但是你走在正确的轨道上,你只需要#ifdef构造函数:

/* Compiles as both C++ and C */
struct Person {
    int ID;
    char Name[200];
#ifdef __cplusplus
    Person() : ID(0), Name("") {}
#endif
};

typedef struct Person Person, *PPerson;

如果确实不想访问编译器生成的构造函数,可以删除它们:

#ifdef __cplusplus
Person() = delete;
Person(const Person&) = delete;
Person(Person&&) = delete;
#endif

编译以下代码进行测试证明这些结构具有相同的大小:

int main () {
    printf("Person is %d bytes\n", sizeof(Person));
}

输出:

$ g++ -o foo_cpp foo.c
$ ./foo_cpp
Person is 204 bytes
$ gcc -o foo_c foo.c
$ ./foo_c
Person is 204 bytes

至于你的第二个问题:是的,传递一个C函数指针将按预期工作。符号损坏和地址转换由C编译器处理。

东门令
2023-03-14

但这只能防止函数名称的混乱。但如果在cpp文件中,struct仍然编译为cpp struct,如果在C文件中,则编译为C struct。

这其实是件好事。这允许您在两种语言中使用struct。

我在想,如果struct有C风格的构造函数等,那么通过在“extern”C端定义struct,也会在cpp文件中产生err

你想错了。它只会影响名字的扭曲。

有没有办法告诉c编译器(vc)把它当作纯c结构来考虑和编译

C编译器编译C;C编译器编译C。

如果希望结构定义在两种语言中都兼容,那么必须用两种语言的公共子集编写。

我[通过回调]可能会遇到任何运行时错误吗?

典型的潜在问题是,如果回调抛出,那么事情就会爆炸,因为调用C代码不会处理异常。您必须确保回调不会抛出。

卫华奥
2023-03-14

但如果在cpp文件中,struct仍然编译为cpp struct,如果在C文件中,则编译为C struct

不能使用C编译器将某些代码编译为C。C编译器将一切编译为C代码。

幸运的是,C语言保证了“标准布局”类与C语言的布局兼容。因此,您无需担心,当编译为C或C时,您的结构将是相同的。extern“C”只会影响名称篡改(例如,添加了一个前导的底图),因此C代码可以在链接过程中“找到”编译为C的标识符。

显然,如果要将结构编译为C和C,就不应该像构造函数一样添加C特性。

对于自由函数指针,请确保使用extern"C"来强制执行C语言链接。

所以你的第一个代码片段看起来很好。

 类似资料:
  • 本文向大家介绍C++中extern "C"的用法,包括了C++中extern "C"的用法的使用技巧和注意事项,需要的朋友参考一下 学习过C++的人都知道,extern关键字可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里起到的是声明作用范围的用处。另外,extern还可以与”C”连用,作为链接指示。本文就此进行实例说明如下:

  • 我的程序返回错误et,我不知道为什么。我如何解决这个问题? 错误: 错误:无法将“std::ostream{aka std::basic\u ostream}”左值绑定到“std::basic\u ostream”

  • 本文向大家介绍C#常用数据结构和算法总结,包括了C#常用数据结构和算法总结的使用技巧和注意事项,需要的朋友参考一下 1.数据   数据(Data)是外部世界信息的载体, 是能够被计算机识别,加工,存储的。在现实生活中也就是我们的产品原材料。   计算机中的数据包括数值数据,图片,影音资料等. 2. 数据元素和数据项   数据元素(Data Element)是数据的基本单位,在计算机处理的过程中通常

  • 本文向大家介绍实例详解C/C++中extern关键字,包括了实例详解C/C++中extern关键字的使用技巧和注意事项,需要的朋友参考一下 1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。 也就是说extern有两个作用,第一个,当它与"C"一起连用时,如: exter

  • 主要内容:1、定义结构体,2、结构体的特征,3、类与结构体在 C# 中,结构体也被称为结构类型(“structure type”或“struct type”),它是一种可封装数据和相关功能的值类型,在语法上结构体与类(class)非常相似,它们都可以用来封装数据,并且都可以包含成员属性和成员方法。 1、定义结构体 要定义一个结构体需要使用 struct 关键字,每个结构体都可以被看作是一种新的数据类型,其中可以包含多个成员(成员属性和成员方法),例如下面

  • 本文向大家介绍C#枚举类型和结构体详解,包括了C#枚举类型和结构体详解的使用技巧和注意事项,需要的朋友参考一下 注意:枚举类型和结构体都属于值类型。 结构体:就是一个自定义的集合,里面可以放各种类型的元素,用法大体跟集合一样。  一、定义的方法: 以上的语句就是定义一个名称为student的结构体,其中包含int类型的年龄、分数、总和,和string类型的姓名、性别。  二、用法:  在main主