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

C++中复制cv::Mat图像到指定地址时程序崩溃的原因及解决办法

程序崩溃原因分析与解决方法

嘿,我来帮你拆解下这段代码崩溃的核心问题,以及对应的解决办法:

首先先把你的代码贴出来方便分析:

int main() { 
    cv::Mat inImg = cv::imread("M6_traffic.jpg"); 
    //Data point copy 
    unsigned char * pData = (unsigned char *)inImg.data; 
    unsigned char * Dest = (unsigned char *)0x0f000000;; 
    int width = inImg.rows; 
    int height = inImg.cols; 
    //data copy using memcpy function 
    memcpy(Dest, pData, sizeof(unsigned char)*width*height*3); 
}

为什么会崩溃?

你这段代码崩溃的关键原因在于直接硬编码了内存地址0x0f000000作为目标地址Dest。

现代操作系统(不管是Windows、Linux还是macOS)都有严格的虚拟内存保护机制:每个进程只能访问自己被分配到的虚拟内存空间,随便写一个地址大概率是未分配的内存区域,或者属于系统内核/其他进程的受保护内存,你的程序完全没有读写权限。一旦执行memcpy往这个地址写数据,操作系统就会触发内存访问错误(比如Linux下的段错误、Windows下的访问违规),直接把程序终止掉。

另外还有个小细节:你把inImg.rows赋值给了width,inImg.cols赋值给了height——其实rows是图像的高度,cols才是宽度,虽然乘法交换律不影响总字节数,但逻辑上是错的,建议修正。

解决方法

根据你的实际需求,分两种情况给出方案:

情况1:只是需要一块普通内存存储图像数据

如果你的需求只是把图像数据复制到一块可用的内存里,完全不需要硬编码地址,用标准的内存分配方式就好:

方案A:用new/delete手动管理堆内存

#include <opencv2/opencv.hpp>
#include <cstring>

int main() {
    cv::Mat inImg = cv::imread("M6_traffic.jpg");
    // 先判断图像是否加载成功,避免空指针问题
    if (inImg.empty()) {
        printf("图像加载失败!\n");
        return -1;
    }
    
    unsigned char* pData = inImg.data;
    // 用channels()获取通道数,比硬写3更通用(比如灰度图是1通道)
    int totalBytes = inImg.rows * inImg.cols * inImg.channels();
    
    // 分配合法的堆内存
    unsigned char* Dest = new unsigned char[totalBytes];
    // 复制数据
    memcpy(Dest, pData, totalBytes);
    
    // 这里可以使用Dest中的图像数据
    
    // 记得释放内存,避免内存泄漏
    delete[] Dest;
    return 0;
}

方案B:用std::vector自动管理内存(更安全)

用std::vector的话,不需要手动释放内存,它会自动在析构时清理:

#include <opencv2/opencv.hpp>
#include <cstring>
#include <vector>

int main() {
    cv::Mat inImg = cv::imread("M6_traffic.jpg");
    if (inImg.empty()) {
        printf("图像加载失败!\n");
        return -1;
    }
    
    int totalBytes = inImg.rows * inImg.cols * inImg.channels();
    // 直接创建对应大小的vector
    std::vector<unsigned char> Dest(totalBytes);
    // 复制数据到vector的内存中
    memcpy(Dest.data(), inImg.data, totalBytes);
    
    // 通过Dest.data()访问图像数据即可,无需手动释放
    return 0;
}

情况2:确实需要写入特定的物理内存地址(比如嵌入式硬件场景)

如果0x0f000000是硬件设备的物理地址(比如嵌入式开发中需要往显存、外设内存写数据),那在现代操作系统下不能直接访问物理地址,需要通过内存映射把物理地址映射到进程的虚拟地址空间,获得合法的虚拟地址后再操作。

以Linux为例,大致实现代码如下(需要root权限):

#include <opencv2/opencv.hpp>
#include <cstring>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    cv::Mat inImg = cv::imread("M6_traffic.jpg");
    if (inImg.empty()) {
        printf("图像加载失败!\n");
        return -1;
    }
    
    int totalBytes = inImg.rows * inImg.cols * inImg.channels();
    // 目标物理地址
    off_t physicalAddr = 0x0f000000;
    
    // 打开/dev/mem设备文件
    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd == -1) {
        perror("打开/dev/mem失败");
        return -1;
    }
    
    // 将物理地址映射到进程的虚拟地址空间
    unsigned char* Dest = (unsigned char*)mmap(
        NULL,                // 让系统自动选择虚拟地址
        totalBytes,          // 映射的大小
        PROT_READ | PROT_WRITE, // 读写权限
        MAP_SHARED,          // 共享映射
        fd,                  // 设备文件描述符
        physicalAddr         // 要映射的物理地址
    );
    
    if (Dest == MAP_FAILED) {
        perror("内存映射失败");
        close(fd);
        return -1;
    }
    
    // 复制图像数据到映射后的地址
    memcpy(Dest, inImg.data, totalBytes);
    
    // 这里可以使用Dest中的数据
    
    // 解除映射并关闭文件
    munmap(Dest, totalBytes);
    close(fd);
    return 0;
}

如果是Windows系统,需要用CreateFileMapping和MapViewOfFile等函数来实现类似的内存映射功能,逻辑是一样的:先把物理地址/设备内存映射到进程虚拟空间,再进行读写。

内容的提问来源于stack exchange,提问作者c.garry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:42:22