文不可无观点,观点不可无论据。

转载请注明出处

MATLAB功能强大,编程方便,是国际广泛使用的计算软件。目前已有很多书籍介绍其在工程上的应用,但很少有从程序设计语言的角度写的书或文章。

MATLAB的核心之处为其一切皆为数组的设计,我们将继续从内存中数据存储的研究展示这一点。

通过上述几个mex程序,对MATLAB储存格式进行较为底层的、直观的解析。

b=zeros(1,1);

c=zeros(3,1);

d=zeros(3,4,5,6);

disp('a:'); dispmem(getaddr(a), 56);

disp('b:'); dispmem(getaddr(b), 56);

disp('c:'); dispmem(getaddr(c), 56);

disp('d:');s=dispmem(getaddr(d), 56);

此处贴出的输出中加粗部分。32位MATLAB程序的mxArray的17~20字节处变量代表了数组维度,其中空数组(0维)、1维、2维在MATLAB中均视为二维矩阵,29~32字节,以及33~36字节处给出了维度信息。

对于多维矩阵,在MATLAB中输入

03 00 00 00 04 00 00 00  05 00 00 00 06 00 00 00

正好是d矩阵各维度信息。MATLAB中的sub2ind、ind2sub、reshape、size、length、numel,以及isempty函数,均是根据这些信息进行操作的。而数据本身是不更改的。

1. posn=@(n) (n-1)*8+[7 8 5 6 3 4 1 2];

2. a=int8([3+4*sqrt(-1) 5+6*sqrt(-1)]);

3. disp('a: ');s=dispmem(getaddr(a), 56);

4. disp('Re: ');dispmem(uint64(hex2dec(s(posn(10)))), 2);

5. disp('Im: ');dispmem(uint64(hex2dec(s(posn(11)))), 2);

即32位MATLAB的mxArray的37~40字节显示了数组的实部的地址,41~44字节显示了数组的虚部的地址。MATLAB存储中,复数的实部和虚部是分开存储的。再测试

1. a=[1 2;3 4;5 6];

2. b={1 2;3 4;{5 6} {}};

3. c=struct('a', a);

4. disp('a: ');dispmem(getaddr(a), 56);

5. disp('b: ');dispmem(getaddr(b), 56);

6. disp('c: ');dispmem(getaddr(c), 56);

看起来在32位程序中,第4字节到第8字节代表不同的数据类型,其中06为矩阵(更确切地为双精度矩阵)、04为字符串、01为元胞数组、02为结构体、10为函数。其实还不止。在MATLAB安装目录/extern/include/matrix.h有清晰的定义。

* Enumeration corresponding to all the valid mxArray types.

typedef enum

mxUNKNOWN_CLASS = 0,

mxCELL_CLASS,

mxSTRUCT_CLASS,

mxLOGICAL_CLASS,

mxCHAR_CLASS,

mxVOID_CLASS,

mxDOUBLE_CLASS,

mxSINGLE_CLASS,

mxINT8_CLASS,

mxUINT8_CLASS,

mxINT16_CLASS,

mxUINT16_CLASS,

mxINT32_CLASS,

mxUINT32_CLASS,

mxINT64_CLASS,

mxUINT64_CLASS,

mxFUNCTION_CLASS,

mxOPAQUE_CLASS,

mxOBJECT_CLASS, /* keep the last real item in the list */

#if defined(_LP64) || defined(_WIN64)

mxINDEX_CLASS = mxUINT64_CLASS,

mxINDEX_CLASS = mxUINT32_CLASS,

/* TEMPORARY AND NASTY HACK UNTIL mxSPARSE_CLASS IS COMPLETELY ELIMINATED */

mxSPARSE_CLASS = mxVOID_CLASS /* OBSOLETE! DO NOT USE */

即MATLAB使用一套数据结构,对所有的类型进行了枚举和区分(在C语言中,enum从第一个数字进行加1累积计数),可惜的是,作者未能在MATLAB的头文件中找到mxArray结构的定义。

上面的程序中一个个输入命令查找太麻烦,为方便解析,利用MATLAB显示函数disp的功能,将指针解析为超链接,便可以点击超链接遍历内存块内容。首先,更改之前的dispmem.cpp,删除其中的mexPrintf循环,并命名为getmem.cpp,编译。再编写超链接生成函数如下。

dispmem_href.m

function dispmem_href(addr, nbyte)

% 显示带超链接的头信息,超链接按指针长度得到(可能不正确)

% addr为uint64的头地址,nbyte为显示的字节数,如不输入,则认为为mxArray大小

[c maxsize endian]=computer; % 得到计算机信息

if(strcmpi(c, 'pcwin') || strcmpi(c, 'glnx86')) nbit=32; % 32位

else nbit=64;end

if(nbit==32)

if(strcmpi(endian, 'L')) norder=[7 8 5 6 3 4 1 2]; % big median

else norder=1:8;end

if(strcmpi(endian, 'L')) norder=[15 16 13 14 11 12 9 10 7 8 5 6 3 4 1 2];

else norder=1:16;end

if(nargin==2) len=nbyte;end

haddr=lower(dec2hex(addr, nbit/4)); % 将addr解析为hex

disp([haddr(norder),':']);

context=getmem(addr, len); % 得到内容

for i=0:nbit/4:len*2-1

s=context(i+(1:nbit/4)); % 按指针长度截取内容

if( i~=0 && mod(i, nbit)==0) str=[str sprintf('\n')];end  % 回车

str=[str, sprintf('<a href="matlab: dispmem_href(uint64(hex2dec(''%s'')),%d)">%s</a>  ', s(norder), len,  s)];  % 增加超链接

disp(sprintf('----------------------------------------<a href= "matlab: clc; dispmem_href(uint64(%d), %d)">******</a>', addr, len)); % 清屏仅显示本项

如果读者有较多时间,可以寻找或采用各种数据进行一一测试,相信可以大部分还原其信息。下图为笔者还原的部分信息。

32位部分MATLAB数据头信息

以上进行了初步热身,这些看起来碎片化的信息,了解它,可以理解MATLAB部分函数语法,可以编写效率更高、更为合理的程序。

往期文章:

微信扫一扫

关注“理念世界的影子”

版权声明:本文是"洞穴之外"作者原创文章,欢迎转载,须署名并注明来自“理念世界的影子”公众号。