You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:57:37