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

Linux下Python ctypes传递结构体到C++共享库结果异常求助

问题描述

使用Python的ctypes调用Ubuntu下的C++共享库时,传递结构体参数出现数据异常,Windows下测试正常。相关代码及现象如下:

C++侧代码

test.h

#include <stdint.h>

#pragma pack (push, 1)
 
typedef struct
{    
    uint8_t a;
    int b;
}param;

typedef struct
{
    int a;
    bool b;
}intbool;
 
#pragma pack (pop)
extern "C" void set_struct_test(param params);

test.cpp

#include "test.h"
#include <stdio.h>
void set_struct_test(param params)
{ 
    printf("size:[%d]\n", sizeof(params));
    uint8_t* pdata = (uint8_t*)&params;
    for (int i = 0; i < sizeof(params); i++)
    {
        printf("data[%d]:[%x]\n", i, pdata[i]);
    }
    printf("%s: %d %d\n", __FUNCTION__,params.a, params.b);
}

C++测试代码(main.cpp)

#include "HelloWorld/api.h"
#include <iostream>
using namespace std;

int main() 
{ 
    param test_param;
    test_param.a = 8;
    test_param.b = 4;
    set_struct_test(test_param);
    return 0;
}

C++测试输出(正确):

size:[5]
data[0]:[8]
data[1]:[4]
data[2]:[0]
data[3]:[0]
data[4]:[0]
set_struct_test: 8 4

Python侧代码(pytest.py)

from ctypes import *

class pyparam(Structure):
    _pack_ = 1
    _fields_ = [("a", c_uint8),
                ("b", c_int32)]

class TestLib():
    def __init__(self):
        libpath = "xxx/HelloWorldLib.so"
        self.dll = cdll.LoadLibrary(libpath)
    
    def pyset_struct_test(self,params):
        ptr = pointer(params)
        raw_bytes = string_at(ptr, sizeof(params))
        print("test: ",raw_bytes)
        return self.dll.set_struct_test(params)    
  
helloworld = TestLib()

param = pyparam()
param.a = 8
param.b = 4
helloworld.pyset_struct_test(param)

Python测试输出(异常):

test:  b'\x08\x04\x00\x00\x00'
size:[5]
data[0]:[6]
data[1]:[0]
data[2]:[0]
data[3]:[0]
data[4]:[0]
set_struct_test: 6 0

关键现象

  • 仅Ubuntu系统出现异常,Windows下正常
  • Python侧打印的结构体原始字节正确,但C++侧接收到的数据完全错误

问题原因

Linux(x86-64)采用System V AMD64 ABI调用约定,Windows采用Microsoft x64 ABI,两者对结构体作为函数参数的传递规则存在差异:

  • System V ABI中,大小<=16字节的结构体优先通过寄存器传递;你的param结构体大小为5字节,符合该规则,但ctypes默认未明确指定函数参数类型和调用约定,导致参数传递时的寄存器映射出错。
  • Windows下的ABI规则与ctypes默认调用方式匹配,因此无异常。
  • 未显式指定函数的参数类型和返回类型,ctypes的默认类型推断在跨平台场景下易引发兼容性问题。

解决方案

方案1:显式指定函数参数与返回类型

修改Python代码,明确告知ctypes函数的参数类型和返回类型,使其匹配System V ABI的传递规则:

from ctypes import *

class pyparam(Structure):
    _pack_ = 1
    _fields_ = [("a", c_uint8),
                ("b", c_int32)]

class TestLib():
    def __init__(self):
        libpath = "xxx/HelloWorldLib.so"
        self.dll = cdll.LoadLibrary(libpath)
        # 显式指定函数参数类型和返回类型
        self.dll.set_struct_test.argtypes = [pyparam]
        self.dll.set_struct_test.restype = None
    
    def pyset_struct_test(self,params):
        ptr = pointer(params)
        raw_bytes = string_at(ptr, sizeof(params))
        print("test: ",raw_bytes)
        return self.dll.set_struct_test(params)    
  
helloworld = TestLib()

param = pyparam()
param.a = 8
param.b = 4
helloworld.pyset_struct_test(param)

方案2:改用指针传递结构体(规避ABI差异)

如果方案1仍有问题,可修改C++函数通过指针接收结构体,避免值传递的ABI差异:

修改C++代码

// test.h
extern "C" void set_struct_test(const param* params);

// test.cpp
void set_struct_test(const param* params)
{ 
    printf("size:[%d]\n", sizeof(*params));
    uint8_t* pdata = (uint8_t*)params;
    for (int i = 0; i < sizeof(*params); i++)
    {
        printf("data[%d]:[%x]\n", i, pdata[i]);
    }
    printf("%s: %d %d\n", __FUNCTION__,params->a, params->b);
}

修改Python对应部分

# 在TestLib的__init__中更新参数类型
self.dll.set_struct_test.argtypes = [POINTER(pyparam)]
# 调用时传递结构体指针
return self.dll.set_struct_test(byref(params))

内容的提问来源于stack exchange,提问作者刘鑫鹏Peter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 20:57:27