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

如何在CGO中向含多结构体的Union传递字节数组?

问题描述

尝试通过Go语言向C中包含多结构体的Union传递值时出现异常:原本只想为Union内某一个结构体的成员赋值,结果Union里所有结构体起始位置的成员都被设为了同一个值。比如示例中仅想为start_id赋值5,但C程序里的map_id、start_id和prog_fd均被设为了5。


相关代码

example.h

#ifdef __cplusplus
extern "C" {
#endif

typedef union bpf_attr
{
    // BPF_MAP_GET_FD_BY_ID
    unsigned map_id; ///< ID of map for BPF_MAP_GET_FD_BY_ID to find.

    struct
    {
        unsigned start_id; ///< ID to look for an ID after. The start_id need not exist.
        unsigned next_id;  ///< On return, contains the next ID.
    }bpf_next_id;          ///< Attributes used by BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID, and BPF_LINK_GET_NEXT_ID.

    // BPF_PROG_TEST_RUN
    struct
    {
        unsigned prog_fd;       ///< File descriptor of program to run.
        unsigned retval;        ///< On return, contains the return value of the program.
        unsigned data_size_in;  ///< Size in bytes of input data.
        unsigned data_size_out; ///< Size in bytes of output data.
        unsigned long long  data_in;      ///< Pointer to input data.
        unsigned long long data_out;      ///< Pointer to output data.
        unsigned duration;      ///< Duration in milliseconds to run the program.
        unsigned ctx_size_in;   ///< Size in bytes of input context.
        unsigned ctx_size_out;  ///< Size in bytes of output context.
        unsigned long long ctx_in;        ///< Pointer to input context.
        unsigned long long ctx_out;       ///< Pointer to output context.
        unsigned flags;         ///< Flags (currently 0).
        unsigned cpu;           ///< CPU to run the program on.
        unsigned batch_size;    ///< Number of times to run the program in a batch.
        unsigned repeat;        ///< Number of times to repeat the program.
    } test;                     ///< Attributes used by BPF_PROG_TEST_RUN.
}bpf_attr1;

int display(union bpf_attr* attr, unsigned int size);

#ifdef __cplusplus
}
#endif

example.cpp

#include "example.h"
#include <stdio.h>

int display(union bpf_attr* attr, unsigned int size) {
    printf("Map id %d \n", attr->map_id);
    printf("start_id %d \n", attr->bpf_next_id.start_id);
    printf("prog_fd %d \n", attr->test.prog_fd);
    attr->bpf_next_id.next_id = 25;
    return 0;
}

main.go

package main

/*
#cgo CFLAGS: -I /cwrapper
#cgo LDFLAGS: -L. -lexample -L /cwrapper/out
#include "example.h"
*/
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
bpfAttr := new(C.union_bpf_attr)
bpfAttr[0] = 5
bpfAttrSize := C.uint(unsafe.Sizeof(bpfAttr))
C.display(bpfAttr, bpfAttrSize)
}

问题原因与解决方法

原因分析

C语言的Union特性是所有成员共享同一块内存空间,起始地址完全相同。map_id、bpf_next_id.start_id、test.prog_fd都是Union的第一个成员(unsigned类型,占4字节),所以直接通过bpfAttr[0] =5修改内存起始的4字节时,这三个变量都会读取到同一个值。

另外,unsafe.Sizeof(bpfAttr)计算的是Go指针的大小(64位系统为8字节),而非Union本身的实际大小,这也是一个潜在问题。

修正后的Go代码

直接通过Go生成的C类型成员访问赋值,而非操作内存数组下标:

package main

/*
#cgo CFLAGS: -I /cwrapper
#cgo LDFLAGS: -L. -lexample -L /cwrapper/out
#include "example.h"
*/
import "C"
import (
    "unsafe"
)

func main() {
    bpfAttr := new(C.union_bpf_attr)
    // 直接为目标结构体成员赋值
    bpfAttr.bpf_next_id.start_id = C.uint(5)
    // 计算Union的实际大小,需取指针指向的变量的大小
    bpfAttrSize := C.uint(unsafe.Sizeof(*bpfAttr))
    C.display(bpfAttr, bpfAttrSize)
}

这样修改后,只有bpf_next_id.start_id会被设为5,其他Union成员的起始值不会被意外覆盖(默认会是0或内存初始值)。


内容的提问来源于stack exchange,提问作者Binoj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:01:14