使用cgo引入C头文件时如何避免'multiple definition'错误?
解决cgo编译时的
multiple definition链接错误 在用cgo开发提供C API的原生库时,因C头文件中包含常量数组定义,编译时出现重复定义的链接错误:
C:\Users\danie\AppData\Local\Temp\go-link-1146178960\000011.o:C:\Temp\cgo-error\mypackage/errors.h:29: multiple definition of `MYMODULE_ERROR_STRS'; C:\Users\danie\AppData\Local\Temp\go-link-1146178960\000010.o:C:/Temp/cgo-error/mypackage/errors.h:29: first defined here collect2.exe: error: ld returned 1 exit status
问题代码示例
main.go
package main import "C" import "example.com/cgo-error/mypackage" func main() {} //export exported_function func exported_function() { mypackage.CallCFunction() }
mypackage.go
package mypackage // #include "errors.h" import "C" func CallCFunction() { _ = C.mymodule_func1() }
errors.h
#ifndef ERRORS_H_ #define ERRORS_H_ /// @brief Error codes for library "mymodule" typedef enum mymodule_error_e { /// No error MYMODULE_ERROR_OK = 0, MYMODULE_ERROR_INVARG, MYMODULE_ERROR_NOMEM, MYMODULE_ERROR_MYERROR, MYMODULE_ERROR_COUNT, } mymodule_error_t; // Array of strings to map enum error types to printable strings const char *const MYMODULE_ERROR_STRS[] = { "MYMODULE_ERROR_OK", "MYMODULE_ERROR_INVARG", "MYMODULE_ERROR_NOMEM", "MYMODULE_ERROR_MYERROR", }; // To get a printable error string const char *mymodule_error_str(mymodule_error_t err); // Other functions in mymodule mymodule_error_t mymodule_func1(void); mymodule_error_t mymodule_func2(void); mymodule_error_t mymodule_func3(void); #endif
errors.c
#include "errors.h" #include <stdio.h> /// @brief Function to get a printable string from an enum error type /// @param[in] err a valid error code for this module /// @return A printable C string corresponding to the error code input above, or NULL if an invalid error code /// was passed in const char *mymodule_error_str(mymodule_error_t err) { const char *err_str = NULL; // Ensure error codes are within the valid array index range if (err >= MYMODULE_ERROR_COUNT) { goto done; } err_str = MYMODULE_ERROR_STRS[err]; done: return err_str; } // Let's just make some empty dummy functions to return some errors; fill these in as appropriate for your // library module mymodule_error_t mymodule_func1(void) { return MYMODULE_ERROR_OK; } mymodule_error_t mymodule_func2(void) { return MYMODULE_ERROR_INVARG; } mymodule_error_t mymodule_func3(void) { return MYMODULE_ERROR_MYERROR; }
解决办法
方法1:将数组定义移到.c文件,头文件仅保留声明
这是C语言的标准做法,头文件只声明变量,实际定义放在.c文件中,避免多次包含头文件导致重复定义。
修改后的errors.h:
// 仅保留数组声明 extern const char *const MYMODULE_ERROR_STRS[];
errors.c保留原数组定义:
// 数组定义放在此处 const char *const MYMODULE_ERROR_STRS[] = { "MYMODULE_ERROR_OK", "MYMODULE_ERROR_INVARG", "MYMODULE_ERROR_NOMEM", "MYMODULE_ERROR_MYERROR", };
方法2:给头文件中的数组添加static关键字
用static修饰数组,使其仅在当前编译单元可见。每个包含该头文件的编译单元会生成独立的数组副本,不会在链接阶段冲突。适合只读常量场景,不会引发逻辑问题。
修改后的errors.h:
static const char *const MYMODULE_ERROR_STRS[] = { "MYMODULE_ERROR_OK", "MYMODULE_ERROR_INVARG", "MYMODULE_ERROR_NOMEM", "MYMODULE_ERROR_MYERROR", };
方法3:在cgo中避免不必要的头文件导入
如果Go代码不需要直接访问MYMODULE_ERROR_STRS,仅需调用C函数,可以通过宏控制头文件内容,避免重复定义:
在mypackage.go中添加编译宏:
package mypackage // #cgo CFLAGS: -DGO_BUILD // #include "errors.h" import "C"
修改errors.h:
#ifndef GO_BUILD // 非Go编译时定义数组 const char *const MYMODULE_ERROR_STRS[] = { "MYMODULE_ERROR_OK", "MYMODULE_ERROR_INVARG", "MYMODULE_ERROR_NOMEM", "MYMODULE_ERROR_MYERROR", }; #else // Go编译时仅声明数组 extern const char *const MYMODULE_ERROR_STRS[]; #endif
内容的提问来源于stack exchange,提问作者Daniel Vernall
相关产品推荐
相关产品推荐

