您建议的第二个代码不安全,不应使用,因为它可能会使 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;
不幸的是,这不是内存存储的最有效使用。想象一下,我们不是使用一个小字符串,而是存储一个非常大的矩阵(沿单元格复制)。
现在可以执行您最初想要的操作,但您必须求助于未记录的 hack(例如创建共享数据副本或手动增加mxArray_tag
结构中的引用计数)。
事实上,这就是 MATLAB 幕后通常发生的事情。以此为例:
>> c = cell(100,100);
>> c(:) = {rand(5000)};
如您所知,MATLAB 中的元胞数组基本上是一个mxArray
其数据指针指向其他mxArray
变量数组的数组。
在上面的例子中,MATLAB 首先创建一个mxArray
对应于 5000x5000 的矩阵。这将存储在第一个单元格中c{1}
。
对于其余的单元格,MATLAB 创建“轻量级” mxArray
s,基本上与第一个单元格元素共享其数据,即它的数据指针指向保存巨大矩阵的同一块内存。
因此,始终只有一个矩阵副本,除非您当然修改其中一个 ( 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++) {
// I simply replaced the call to mxDuplicateArray here
mxSetCell(arr, i, mxCreateReference(str));
}
mxDestroyArray(str);
plhs[0] = arr;
}
MATLAB
>> %c = repmat({'Hello'}, 10, 1);
>> c = mex_test()
>> c{1} = 'bye'
>> clear c
该函数将在每次调用mxCreateReference
时递增数组的内部引用计数器,从而让 MATLAB 知道它还有其他副本。str
因此,当您清除生成的单元格数组时,它会依次为每个单元格递减该计数器,直到计数器达到 0,此时可以安全地销毁相关数组。
直接使用数组 ( mxSetCell(arr, i, str)
) 是有问题的,因为 ref-counter 在破坏第一个单元后立即变为零。因此,对于后续单元,MATLAB 将尝试释放已释放的数组,从而导致内存损坏。