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

C语言内存区域读取相关问题:变量正确性与数据读取方法

问题解答

一、变量声明与使用的问题修正

你的代码能运行但存在几个不规范和潜在崩溃的问题,逐个说明:

  • 调用约定不匹配:原DLL函数是__stdcall调用约定,但你自定义的A_ReadConfig指针没加这个修饰符。Windows下__stdcall函数的导出名会被修饰(比如变为_ReadConfig@8),如果DLL按此约定导出,GetProcAddress用"ReadConfig"可能找不到函数;即使侥幸找到,调用时栈处理也会出错,必须把自定义指针改成typedef unsigned short (__stdcall * A_ReadConfig)(void **, size_t *);。
  • size_t初始化错误:size_t sizeMemoryZone = NULL;是类型不匹配的写法,size_t是无符号整数类型,NULL是指针空值,正确初始化应为size_t sizeMemoryZone = 0;。
  • 缺少错误检查:LoadLibrary和GetProcAddress都可能失败(比如DLL不存在、函数名错误),返回NULL后直接调用会导致程序崩溃,必须添加检查逻辑。
  • 未处理内存释放:DLL分配的pointerMemoryZone不能直接用free释放,必须查看DLL文档,找到对应的内存释放函数(比如FreeConfig),否则会造成内存泄漏。

二、读取内存区域数据的方法

根据你提供的示意图(翻译后结构如下):

内存区域为带头部的配置列表:

  1. 头部:存储配置条目总数(unsigned int类型)
  2. 主体:紧随头部的是N个配置条目,每个条目包含:
    • 条目类型(unsigned short)
    • 条目数据长度(size_t)
    • 条目数据(可变长度的字节流)

步骤1:定义匹配的结构体

先根据示意图结构定义对应的C结构体:

// 单个配置条目结构
typedef struct {
    unsigned short entryType;  // 条目类型
    size_t entryLength;        // 条目数据的字节长度
    char entryData[];          // 柔性数组,存储条目数据
} ConfigEntry;

// 整个配置列表的头部结构
typedef struct {
    unsigned int entryCount;   // 配置条目总数
    ConfigEntry entries[];     // 柔性数组,存储所有条目
} ConfigList;

步骤2:类型转换与数据读取

将void*类型的pointerMemoryZone转换为ConfigList*,然后遍历读取每个条目:

// 先确认函数调用成功(需参考DLL文档,比如返回0表示成功)
if (outputFunction == 0 && pointerMemoryZone != NULL) {
    ConfigList* configList = (ConfigList*)pointerMemoryZone;
    // 先检查内存大小是否至少能容纳头部
    if (sizeMemoryZone >= sizeof(ConfigList)) {
        printf("配置条目总数:%u\n", configList->entryCount);

        // 遍历每个条目(用指针偏移避免柔性数组的对齐问题)
        char* currentPos = (char*)&configList->entries[0];
        for (unsigned int i = 0; i < configList->entryCount; i++) {
            ConfigEntry* entry = (ConfigEntry*)currentPos;
            // 检查当前条目是否超出内存范围,防止越界访问
            if (currentPos + sizeof(ConfigEntry) + entry->entryLength > (char*)pointerMemoryZone + sizeMemoryZone) {
                printf("第%u个条目数据越界,停止遍历\n", i+1);
                break;
            }

            // 输出条目信息
            printf("\n第%u个条目:\n", i+1);
            printf("  类型:%hu\n", entry->entryType);
            printf("  长度:%zu\n", entry->entryLength);
            printf("  内容:");
            // 如果是字符串可以直接printf,二进制数据用fwrite输出
            fwrite(entry->entryData, 1, entry->entryLength, stdout);
            printf("\n");

            // 移动指针到下一个条目
            currentPos += sizeof(ConfigEntry) + entry->entryLength;
        }
    }
}

步骤3:释放内存

必须使用DLL提供的内存释放函数(比如FreeConfig)来释放pointerMemoryZone,不能用标准库的free,否则会跨堆释放导致崩溃:

// 定义释放函数的指针类型
typedef void (__stdcall * FreeConfig)(void* pData);
// 获取释放函数指针
FreeConfig ptrFreeConfig = (FreeConfig)GetProcAddress(dllLoadOuput, "FreeConfig");
if (ptrFreeConfig != NULL && pointerMemoryZone != NULL) {
    ptrFreeConfig(pointerMemoryZone);
}

修正后的完整代码

#include <stdio.h>
#include <windows.h>

// 严格匹配DLL的函数指针类型
typedef unsigned short (__stdcall * ReadConfig)(void** pDataOut, size_t* pSizeDataOut);
typedef void (__stdcall * FreeConfig)(void* pData);

// 对应示意图的配置结构
typedef struct {
    unsigned short entryType;
    size_t entryLength;
    char entryData[];
} ConfigEntry;

typedef struct {
    unsigned int entryCount;
    ConfigEntry entries[];
} ConfigList;

int main(int nbArg, char** listeArg)
{
    unsigned short outputFunction;
    void* pointerMemoryZone = NULL;
    size_t sizeMemoryZone = 0;  // 正确初始化

    // 加载DLL并检查结果
    HMODULE dllLoadOutput = LoadLibrary("dllFile.dll");
    if (dllLoadOutput == NULL) {
        printf("DLL加载失败,错误码:%lu\n", GetLastError());
        return 1;
    }

    // 获取ReadConfig函数指针并检查
    ReadConfig ptrReadConfiguration = (ReadConfig)GetProcAddress(dllLoadOutput, "ReadConfig");
    if (ptrReadConfiguration == NULL) {
        printf("获取ReadConfig函数失败,错误码:%lu\n", GetLastError());
        FreeLibrary(dllLoadOutput);
        return 1;
    }

    // 调用DLL函数
    outputFunction = ptrReadConfiguration(&pointerMemoryZone, &sizeMemoryZone);

    // 处理调用结果
    if (outputFunction == 0 && pointerMemoryZone != NULL) {
        printf("\n读取到的配置内存大小:%zu\n\n", sizeMemoryZone);

        ConfigList* configList = (ConfigList*)pointerMemoryZone;
        if (sizeMemoryZone >= sizeof(ConfigList)) {
            printf("配置条目总数:%u\n", configList->entryCount);

            char* currentPos = (char*)&configList->entries[0];
            for (unsigned int i = 0; i < configList->entryCount; i++) {
                ConfigEntry* entry = (ConfigEntry*)currentPos;
                if (currentPos + sizeof(ConfigEntry) + entry->entryLength > (char*)pointerMemoryZone + sizeMemoryZone) {
                    printf("第%u个条目越界,终止遍历\n", i+1);
                    break;
                }

                printf("\n第%u个条目:\n", i+1);
                printf("  类型:%hu\n", entry->entryType);
                printf("  长度:%zu\n", entry->entryLength);
                printf("  内容:");
                fwrite(entry->entryData, 1, entry->entryLength, stdout);
                printf("\n");

                currentPos += sizeof(ConfigEntry) + entry->entryLength;
            }
        }

        // 释放DLL分配的内存
        FreeConfig ptrFreeConfig = (FreeConfig)GetProcAddress(dllLoadOutput, "FreeConfig");
        if (ptrFreeConfig != NULL) {
            ptrFreeConfig(pointerMemoryZone);
        } else {
            printf("\n未找到内存释放函数,可能存在内存泄漏\n");
        }
    } else {
        printf("ReadConfig调用失败,返回码:%hu\n", outputFunction);
    }

    // 卸载DLL
    FreeLibrary(dllLoadOutput);

    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 00:54:31