matlab 与c/c++ 混合MEX编程的几个需要注意的地方

最近做一个机器学习的课题,主体是matlab写的,其中有部分训练的核心算法是用c++写的,因为有太多的循环和数值计算用c++比较快。这也是我第一次用c++写matlab的模块,感觉走了很多弯路,下面给大家分享一点经验。

matlab中的c++编程称为mex编程:matlab executive matlab 可执行文件,至于其中的具体机制我不是很清楚,有的大神会比较清楚编译期间产生的各种文件。

1) mex编程中指针和索引:

matlab中默认的数据类型是double,用class()函数可以看到变量的数据类型:

matlab代码如下:

mex mex.cpp ‘-g‘;
a = [1.1,2.1,3;4,5,6;7,8,9]
mex(a)

命令mex 用来编译mex文件,上面代码中  mex  mex.cpp ‘-g’ 编译了mex.cpp这个c++文件,编译完成之后会生成一个“mex.mexw64”的文件,后缀名说明这是在win64下编译完成的mex文件,后面的‘-g‘是一个附加参数,在这里不用理解。编译后的mex文件可以当matlab函数使用。

在matlab代码文件同目录下的c文件mex.cpp代码如下:

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    double *input;
    input = mxGetPr(prhs[0]);
    printf("第一个值%f\n",*input);
    printf("第二个值%f\n",*(input+1));
}

mexFunction有四个参数,nlhs( number left hand s):左边参数个数,也就是matlab函数输出值得个数,mxArray *plhs[]是一个指针数组,数组中的每一个元素都是一个指针,指向输出的矩阵;nrhs 是右边参数个数,也就是输入参数的个数,mxArray *prhs[]数组中的每个指针指向输入矩阵。mxGetPr()函数返回一个double*型的指针,指向矩阵的第一个元素,在matlab代码中调用:mex(a),那也就是 prhs[0]是输入矩阵a的地址,而 input = mxGetPr(prhs[0]) ,input指向了a第一个元素1 。
那矩阵第一排第二列的值a(1,2)的地址是多少呢?是(input+1)吗?在这里我们运行上面的matlab代码,得到的结果如下:

可以看出,输出的*(input+1)是4,也就是说,c++中的matlab矩阵是按列进行索引的。这里是一个需要注意的地方,因为很多地方要对matlab输入的矩阵进行遍历得到矩阵的元素值,如果索引出错,那就完全错了。其实这里的内在原因,是因为在matlab中矩阵是按列进行索引的,而c++中指针式按行往后加的。

有很多函数可以方便我们对矩阵进行索引,uint32 mxGetM(mxArray *)输入一个矩阵的指针,返回该矩阵的行数,uint32 mxGetN(mxArray *)返回列数,对行数和列数适当的计算,可以方便的访问矩阵元素,例如,访问a(i,j):   *(input+N*(j-1)+(i-1))  ,N为矩阵行数,这里需要-1的原因是,matlab的行数列数从1开始计数,而c的数组则从0开始索引。

2)mex编程中的数据类型与指针移位的重要关系,mxGetPr() 与 mxGetData():

前面说过,matlab里的默认数据类型是double,那么,如果把mex函数的输入矩阵的数据类型转换一下,会出现什么结果呢?

matlab 代码:

1 clc
2 mex mex.cpp ‘-g‘;
3 a = [1.1,2.1,3;4,5,6;7,8,9];
4 a=single(a)
5 mex(a)

c++代码:

1 #include "mex.h"
2 void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
3 {
4     double *input;
5     input = mxGetPr(prhs[0]);
6     printf("第一个值%f\n",*input);
7     printf("第二个值%f\n",*(input+1));
8 }

c++代码并没有变,matlab代码也仅仅进行了一个数据类型转换,我们看看输出结果:

可以看到这里输出的已经不是我们期望的数值了。在我调试mex代码的时候这个问题苦恼了我很久,因为mex不方便调试,很多时候输出的结果不是想要的,而且我的输入矩阵都是上万维的,很难调试。这里输入矩阵a变成了single单精度类型,前面我们说过,mxGetPr()返回double类型的指针,当我们用double类型指针访问一个单精度(在c++)中我们称之为浮点型float的数据的时候,当然会发成内存越界,用取值符号*去取值的时候超过了数据的内存块,因此发生错误,如果我们修改c++代码:

1 #include "mex.h"
2 void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
3 {
4     float* input;
5     input = (float*)mxGetPr(prhs[0]);
6     printf("第一个值%f\n",*input);
7     printf("第二个值%f\n",*(input+1));
8 }

将input类型设置成float,并将mxGetPr()的返回类型强制转换为float*就可以了。在这里还有一个函数mxGetData()也可以返回输入矩阵的头地址,只不过mxGetData()返回的是char*类型的指针,而mxGetPr()返回的是double*类型的指针,可以根据自己的需要选取函数,或者转换指针类型。如果指针类型不对,极有可能造成内存访问错误,导致matlab死掉。

3) nlhs 与 nrhs的作用

mexFunction函数中,两个指针参数分别指向输入输出的矩阵,而nrhs和nlhs分别记录输入输出矩阵的个数,在一般的操作中,我们仅仅对输入矩阵进行取值,运算,对输出矩阵进行赋值,nrhs和nlhs不是很常用,但是也是极其重要的。例如,在上面的代码中,如果我在matlab代码中这样调用mex:mex(),不输入任何参数,matlab就会马上死掉。因为在mex文件的cpp代码中,你用指针访问了输入矩阵的值,而在参数中你没有给mex输入任何参数,使得矩阵指针为野指针,导致内存错误。如果编码中出现这种参数不对的情况,将导致matlab频繁死掉,我的工作中数据特别多,准备数据需要几十分钟,这样让我非常痛苦。解决的方法就是利用nlhs和nrhs这两个参数。在mexFunction中判断nlhs的值来判断输入参数的个数,用nrhs判断输入参数的个数。如果输入参数少于某个值或者不满足你的要求可以让mexFunction直接return,避免后续的程序导致内存错误。

    
时间: 2024-10-12 02:51:56

matlab 与c/c++ 混合MEX编程的几个需要注意的地方的相关文章

matlab和C/C++混合编程--Mex

最近的项目需要matlab和C的混合编程,经过一番努力终于完成了项目要解决的问题.现在就将Mex的一些经验总结一下,当然只是刚刚开始,以后随着学习的深入继续添加.首先讲讲写Mex的一些常规规定,然后我们会重点关注混合编程中最难解决数据的问题--结构到底如何转换,并且后面会重点说一下自己的程序. 一.Mex的结构 先看一个简单的程序(该程序保存在matlab主目录下名字是mexDemon.cpp,或者在主目录下新建一个.cpp文件): #include "mex.h" //加入头文件,该

【5.1送礼】国内第一部Matlab和C#.Net混合编程入门级视频教程【完全免费】

上一次写博客很久了,一直在忙彩票分析系统架构的事情,写博客真是件费神的事情,非常花时间.今天抽空发布这篇博客,是为了开源一部自己录制的视频教程-Matlab和C#.Net混合编程视频教程[入门级].下面说说这部视频教程的来由和一些事情,想获取的仔细看看,别忘了点[推荐]哦! 一.为啥要开源 1.1 视频的来源 这部视频教程是在2012年年底闲时比较多,当初也是很多朋友,网友提出这个Matlab.Net混合编程入门比较难,没有资料,所有就特意录制了一部比较简单的视频教程.并有条件的对广大网友免费开

[Matlab] matlab与C/C++混合编程汇总

matlab 与外部程序的编程接口两大类: 一是 如何在matlab里调用其他语言写的代码. (见例子:使用C-MEX技术,ActiveX技术) 二是 如何在其他语言里调用matlab. (见 使用matlab引擎, MAT数据交换, matlab发布com组件, DeployTool) matlab接口技术包含以下几个方面: 1. 数据导入导出,主要是MAT文件数据的导入导出. 2.普通的动态链接库dll文件的交互,Matlab6.5起,直接matlab环境中调用dll文件导出的函数. 3.m

Matlab和C语言混合编程,包含目录的设定

如果.c文件不依赖于任何第三方库,那么mex编译很简单,只需要在matlab的命令行输入 mex test.c 即可. 但是如果这个c文件使用了第三方库文件,如opencv.gsl等等,那么就需要更改一下mex的编译选项,否则会报fetal error C1083,找不到包括文件. 这里需要注意的是,在mex之前,需要使用 mex -setup 选择编译器,假如我选的是VS2012,那么我需要把相应的选项加到VS2012所对应的opt文件中,具体的位置在 ‘MATLAB路径\R2013a\bin

Swift和C混合Socket编程实现简单的ping命令

这个是用Mac下的Network Utility工具实现ping命令,用Wireshark抓取的ICMP数据包: 发送ICMP数据包内容 接受ICMP数据包内容 一.icmp结构 要真正了解ping命令实现原理,就要了解ping命令所使用到的TCP/IP协议.ICMP(Internet Control Message,网际控制报文协议)是为网关和目标主机而提供的一种差错控制机制,使它们在遇到差错时能把错误报告给报文源发方.ICMP协议是IP层的 一个协议,但是由于差错报告在发送给报文源发方时可能

【iOS与EV3混合机器人编程系列之三】编写EV3 Port Viewer 应用监测EV3端口数据

在前两篇文章中,我们对iOS与EV3混合机器人编程做了一个基本的设想,并且介绍了要完成项目所需的软硬件准备和知识准备. 那么在今天这一篇文章中,我们将直接真正开始项目实践. ==第一个项目: EV3 Port Viewer== 项目目的:在iOS设备上通过WiFi连接EV3并且读取EV3每个端口的数据. 大家可以一周之后在App Store上搜索EV3 Port Viewer,那么我已经做了一个范例App发布了,正在审核中 应用的基本使用要求:将EV3和iPhone同时连接到同一个WiFi网络中

【iOS与EV3混合机器人编程系列之二】工欲善其事,必先利其器(准备篇)

在上一篇文章中,我们论述了iOS与EV3结合后机器人开发的无限可能, 那么,大家要不要一起来Hacking一把呢? 为了能够完整地完成我接下来我讲的项目,我们需要做以下准备: 1.一台Mac运行MAC OS X 10.9.3以上的操作系统. 2.Xcode6.这是iOS在Mac上的开发工具.我们将使用Xcode来进行所有的项目程序的编写 3.一两个iOS设备,iPhone或iPad都行.实际上大家最好有两个iOS设备,因为最后的项目中iPhone将和EV3机器人放在一起,而用另一个iOS设备来查

Matlab 为什么要使用面向对象的编程——开篇

基本上,Matlab的入门资料都会涉及到Matlab编程,但一般的书只涉及到面向过程的编程,忽视了面向对象的编程.实际上,Matlab很早就支持面向对象的编程,我们平时在利用图像对象.坐标系对象.图像对象都或多或少的使用了一些面向对象的性质. 本系列博文是本人在学习利用Matlab进行面向对象编程时自己的理解,主要的资料就是Matlab的帮助文档.因为本人对面向对象的编程只知皮毛,一些理解上的错误在所难免,欢迎指正,互相学习. 类是面向对象的编程中重要的一个概念,类就是描述具有共同特征对象的抽象

【iOS与EV3混合机器人编程系列之五】iOS_WiFi_EV3_Library 剖析之连接EV3

在上一篇文章中,我们讲解了如何用开源代码库CocoaAsyncSocket来实现iOS上的UDP和TCP数据通信.那么在本文中,我们将介绍在CocoaAsyncSocket的基础如何使用UDP和TCP连接EV3的机制. 之所以我们能够通过无线连接EV3,根本原因在于EV3的源代码内建了一套无线连接通信的机制. 这套机制是这样的: 1)EV3在连接到无线网络后,就不断地从3015端口发送UDP数据,数据的格式如下: Serial-Number: 0016533f0c1ePort: 5555Name