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

使用ctypes实现Python调用C++ DLL传参报错:内存访问违规

问题分析与解决方案

核心错误原因

  1. 参数类型不匹配:Python直接传递字符串给C的char*参数时,ctypes默认会转为宽字符(wchar_t*),但C函数期望窄字符指针,导致内存读取异常。
  2. C++代码内存越界:第二个循环中i的取值范围超过了r_data/s_data的数组长度(数组仅len个元素,但i最大到len*2),直接访问非法内存触发访问违规。
  3. 返回值类型不兼容:C函数返回std::string(C专属类型),但ctypes指定返回值为c_char_p(C风格字符串指针),两者内存布局完全不同,导致解析错误。
  4. 缺少参数类型声明:未指定lib.encode.argtypes,ctypes无法正确处理参数转换逻辑。

修正方案

方案一:C++返回C风格字符串(需手动管理内存)

修正后的C++ DLL代码

#include <cstdlib>
#include <ctime>
#include <cstring>

// 用extern "C"避免C++名字修饰,确保Python能找到函数
extern "C" __declspec(dllexport) char* encode(const char* data, unsigned int len)
{   
    // 仅初始化一次随机数生成器
    static bool rand_init = false;
    if (!rand_init) {
        srand(time(nullptr));
        rand_init = true;
    }

    // 分配结果缓冲区(+1用于存储字符串结束符)
    char* res_data = new char[len * 2 + 1];
    memset(res_data, 0, len * 2 + 1);

    for (unsigned int i = 0; i < len; ++i) {
        unsigned char chr_num = static_cast<unsigned char>(data[i]);
        unsigned short salt = static_cast<unsigned short>(rand() % 512 + 512);
        unsigned short encrypted = chr_num + salt;

        // 将加密值拆分为高低字节存入缓冲区
        res_data[2*i] = static_cast<char>(encrypted & 0xFF);
        res_data[2*i + 1] = static_cast<char>((encrypted >> 8) & 0xFF);
    }

    return res_data;
}

// 提供内存释放函数,让Python回收C++分配的内存
extern "C" __declspec(dllexport) void free_char_ptr(char* ptr)
{
    delete[] ptr;
}

修正后的Python代码

import ctypes
import os

# 加载DLL
lib_path = os.path.join(os.getcwd(), "testDllForPy.dll")
lib = ctypes.CDLL(lib_path)

# 声明函数参数与返回值类型
lib.encode.argtypes = [ctypes.c_char_p, ctypes.c_uint]
lib.encode.restype = ctypes.c_char_p

lib.free_char_ptr.argtypes = [ctypes.c_char_p]
lib.free_char_ptr.restype = None

def encode(data: str) -> str:
    # 将Python字符串转为UTF-8字节流,匹配C++的char*类型
    data_bytes = data.encode('utf-8')
    # 调用C++函数
    result_ptr = lib.encode(data_bytes, len(data_bytes))
    # 读取C风格字符串并转为Python字符串
    result_str = ctypes.string_at(result_ptr).decode('utf-8')
    # 释放C++分配的内存
    lib.free_char_ptr(result_ptr)
    return result_str

# 测试
if __name__ == "__main__":
    print(encode("Hello"))

方案二:Python提供缓冲区(自动管理内存,更安全)

这种方式避免手动内存管理,由Python负责缓冲区的创建与回收。

修正后的C++ DLL代码

#include <cstdlib>
#include <ctime>
#include <cstring>

extern "C" __declspec(dllexport) void encode(const char* data, unsigned int len, char* output_buf, unsigned int buf_len)
{   
    static bool rand_init = false;
    if (!rand_init) {
        srand(time(nullptr));
        rand_init = true;
    }

    // 检查缓冲区长度是否足够
    if (buf_len < len * 2 + 1) {
        memset(output_buf, 0, buf_len);
        return;
    }

    memset(output_buf, 0, len * 2 + 1);

    for (unsigned int i = 0; i < len; ++i) {
        unsigned char chr_num = static_cast<unsigned char>(data[i]);
        unsigned short salt = static_cast<unsigned short>(rand() % 512 + 512);
        unsigned short encrypted = chr_num + salt;

        output_buf[2*i] = static_cast<char>(encrypted & 0xFF);
        output_buf[2*i + 1] = static_cast<char>((encrypted >> 8) & 0xFF);
    }
}

修正后的Python代码

import ctypes
import os

lib_path = os.path.join(os.getcwd(), "testDllForPy.dll")
lib = ctypes.CDLL(lib_path)

# 声明函数参数类型
lib.encode.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint]
lib.encode.restype = None

def encode(data: str) -> str:
    data_bytes = data.encode('utf-8')
    data_len = len(data_bytes)
    # 计算所需缓冲区长度
    buf_len = data_len * 2 + 1
    # 创建Python管理的缓冲区
    output_buf = ctypes.create_string_buffer(buf_len)
    # 调用C++函数写入缓冲区
    lib.encode(data_bytes, data_len, output_buf, buf_len)
    # 转换为Python字符串
    return output_buf.value.decode('utf-8')

# 测试
if __name__ == "__main__":
    print(encode("Hello"))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:40:14