是否有可能返回包含多个单元格中的一个实例的单元格数组?

use*_*803 5 matlab memory-management mex

我写了一些mex函数,并且必须返回大量的字符串.

我这样做如下:

  mxArray * array = mxCreateCellMatrix(ARRAY_LEN, 1);
  for (size_t k = 0; k < ARRAY_LEN; ++ k) {
      mxArray *str = mxCreateString("Hello");
      mxSetCell(array, k, str);
  }
  prhs[0] = array;
Run Code Online (Sandbox Code Playgroud)

但是,由于字符串始终具有相同的值,因此我只想创建一个实例.喜欢

  mxArray * array = mxCreateCellMatrix(ARRAY_LEN, 1);
  mxArray *str = mxCreateString("Hello");

  for (size_t k = 0; k < ARRAY_LEN; ++ k) {
      mxSetCell(array, k, str);
  }
  prhs[0] = array;
Run Code Online (Sandbox Code Playgroud)

有可能吗?垃圾收集器如何知道释放它?谢谢.

Amr*_*mro 6

您建议的第二个代码不安全,不应该使用,因为它可能会导致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;
Run Code Online (Sandbox Code Playgroud)

遗憾的是,这并不是内存存储最有效的用途.想象一下,我们不是使用一个很小的字符串,而是存储一个非常大的矩阵(沿着单元格重复).


现在可以做你最初想要的事情,但你必须求助于无证件的黑客攻击(比如创建共享数据副本或手动增加mxArray_tag 结构中的引用计数).

事实上,这是MATLAB幕后通常会发生的事情.以此为例:

>> c = cell(100,100);
>> c(:) = {rand(5000)};
Run Code Online (Sandbox Code Playgroud)

如您所知,MATLAB中的单元数组基本上是一个mxArray数据指针指向其他mxArray变量数组的数组.

在上面的例子中,MATLAB首先创建一个mxArray对应于5000x5000矩阵的.这将存储在第一个单元格中c{1}.

对于其余的单元格,MATLAB创建"轻量级" mxArray,它基本上与第一个单元格元素共享其数据,即其数据指针指向保存巨大矩阵的同一块内存.

因此,矩阵只有一个副本,除非您修改其中一个(c{2,2}(1)=99),此时MATLAB必须"取消链接"数组并为此单元格元素制作单独的副本.

您会在内部看到每个mxArray结构都有一个引用计数器和一个交叉链接指针,以使这种数据共享成为可能.

提示:您可以format debug 在打开选项的情况下研究此数据共享行为,并比较pr各个单元格的指针地址.

同样的概念适用于结构域,所以当我们写:

x = rand(5000);
s = struct('a',x, 'b',x, 'c',x);
Run Code Online (Sandbox Code Playgroud)

所有字段都指向相同的数据副本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;
}
Run Code Online (Sandbox Code Playgroud)

MATLAB

>> %c = repmat({'Hello'}, 10, 1);
>> c = mex_test()
>> c{1} = 'bye'
>> clear c
Run Code Online (Sandbox Code Playgroud)

mxCreateReference函数str每次调用时都会递增数组的内部引用计数器,从而让MATLAB知道它的其他副本.

因此,当您清除生成的单元格数组时,它将依次为每个单元格递减此计数器,直到计数器达到0,此时可以安全地销毁相关阵列.

直接使用数组(mxSetCell(arr, i, str))是有问题的,因为在销毁第一个单元后ref-counter立即达到零.因此,对于后续单元,MATLAB将尝试释放已释放的阵列,从而导致内存损坏.