MATLAB程序设计语言(3.2)---一切皆为数组2(MATLAB的底层实现)
公众号:理念世界的影子
文不可无观点,观点不可无论据。
转载请注明出处
MATLAB功能强大,编程方便,是国际广泛使用的计算软件。目前已有很多书籍介绍其在工程上的应用,但很少有从程序设计语言的角度写的书或文章。
MATLAB的核心之处为其一切皆为数组的设计,我们将继续从内存中数据存储的研究展示这一点。
通过上述几个mex程序,对MATLAB储存格式进行较为底层的、直观的解析。
a=[];
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中输入
1. dispmem(uint64(hex2dec('0d801710'), 16); % 字符串需与d矩阵输出的地址一致,或者使用s((8-1)*8+[7 8 5 6 3 4 1 2])代替
将输出
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,
#else
mxINDEX_CLASS = mxUINT32_CLASS,
#endif
/* TEMPORARY AND NASTY HACK UNTIL mxSPARSE_CLASS IS COMPLETELY ELIMINATED */
mxSPARSE_CLASS = mxVOID_CLASS /* OBSOLETE! DO NOT USE */
}
mxClassID;
即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
len=56;
else
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
len=104;
end
if(nargin==2) len=nbyte;end
haddr=lower(dec2hex(addr, nbit/4)); % 将addr解析为hex
disp([haddr(norder),':']);
context=getmem(addr, len); % 得到内容
str=[];
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)]; % 增加超链接
end
disp(str);
disp(sprintf('----------------------------------------<a href= "matlab: clc; dispmem_href(uint64(%d), %d)">******</a>', addr, len)); % 清屏仅显示本项
如果读者有较多时间,可以寻找或采用各种数据进行一一测试,相信可以大部分还原其信息。下图为笔者还原的部分信息。
32位部分MATLAB数据头信息
以上进行了初步热身,这些看起来碎片化的信息,了解它,可以理解MATLAB部分函数语法,可以编写效率更高、更为合理的程序。
往期文章:
MATLAB《MATLAB程序设计语言(2)---help的see also与六度空间理论》
微信扫一扫
关注“理念世界的影子”
版权声明:本文是"洞穴之外"作者原创文章,欢迎转载,须署名并注明来自“理念世界的影子”公众号。