您提供的第二个代码不安全,不应使用,因为它可能会导致MATLAB崩溃。相反,您应该编写:
mxArray *arr = mxCreateCellMatrix(len, 1);
mxArray *str = mxCreateString("Hello");
for(mwIndex i=0; i<len; i++) {
mxSetCell(arr, i, mxDuplicateArray(str));
}
mxDestroyArray(str);
plhs[0] = arr;
很遗憾,这不是最有效的存储内存使用方式。想象一下,如果我们不是使用一个小字符串,而是存储一个非常大的矩阵(在单元格中复制),那么情况会怎样。
现在可以完成您最初想要的操作,但您需要采用非文档化的技巧(例如创建共享数据副本或手动增加
mxArray_tag
结构体中的引用计数)。
实际上,在MATLAB幕后通常会发生这种情况。 以这个为例:
>> c = cell(100,100);
>> c(:) = {rand(5000)};
如您所知,在MATLAB中,一个单元数组基本上是一个
mxArray
,它的数据指针指向其他
mxArray
变量的数组。
在上述情况下,MATLAB首先创建与5000x5000矩阵相对应的
mxArray
。这将存储在第一个单元中。
对于其余的单元格,MATLAB创建“轻量级”
mxArray
,它们基本上与第一个单元格元素共享其数据,即其数据指针指向保存巨大矩阵的同一块内存。
因此,始终只有一个矩阵的副本,除非您修改其中之一(
c{2,2}(1)=99
),此时MATLAB必须“取消链接”该数组并为此单元格元素制作单独的副本。
您可以看到,每个
mxArray
结构在内部都有一个引用计数器和交叉链接指针,以使这种数据共享成为可能。
提示:您可以通过打开
format debug
选项并比较各个单元格的
pr
指针地址来研究此数据共享行为。
相同的概念也适用于结构字段,因此当我们编写以下代码时:
x = rand(5000);
s = struct('a',x, 'b',x, 'c',x);
所有字段都将指向
x
中相同的数据副本。
编辑:
我忘记展示我提到的未记录的解决方案 :)
mex_test.cpp
#include "mex.h"
extern "C" mxArray* mxCreateReference(mxArray*);
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mwSize len = 10;
mxArray *arr = mxCreateCellMatrix(len, 1);
mxArray *str = mxCreateString("Hello");
for(mwIndex i=0; i<len; i++) {
mxSetCell(arr, i, mxCreateReference(str));
}
mxDestroyArray(str);
plhs[0] = arr;
}
MATLAB
>>
>> c = mex_test()
>> c{1} = 'bye'
>> clear c
mxCreateReference
函数每次被调用时都会增加
str
数组的内部引用计数器,告诉MATLAB它有其他副本。
因此,当您清除生成的单元格数组时,每个单元格将依次减少该计数器,直到计数器达到0,此时可以安全地销毁相应的数组。
直接使用数组(
mxSetCell(arr, i, str)
)存在问题,因为在销毁第一个单元格后,引用计数器立即达到零。因此,对于后续单元格,MATLAB将尝试释放已经被释放的数组,导致内存损坏。