您可以使用一个小助手 MEX 功能(或未记录的 format debug 模式)检查自己:
转储.c
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
if (nrhs != 1 || nlhs > 0)
mexErrMsgIdAndTxt("dump:error", "Wrong number of arguments.");
if (!mxIsNumeric(prhs[0]))
mexErrMsgIdAndTxt("dump:error", "Expects a numeric array.");
mexPrintf("header = 0x%p\n", prhs[0]);
mexPrintf("data = 0x%p\n", mxGetData(prhs[0]));
}
现在:
>> A = 1:5
A =
1 2 3 4 5
>> dump(A)
header = 0x00000000148312F0
data = 0x000000007C97CEC0
>> A(5) = []
A =
1 2 3 4
>> dump(A)
header = 0x00000000148312F0
data = 0x000000007CEBA840
同样:
>> A = 1:5
A =
1 2 3 4 5
>> dump(A)
header = 0x000000001482A400
data = 0x000000007DDC7740
>> A = A(1:4)
A =
1 2 3 4
>> dump(A)
header = 0x000000001482A400
data = 0x000000007D7C9C60
一般来说,进行任何数组切片都会重新分配数据(A=A(:) 的特殊情况除外,其中仅更改标头中的大小而不接触数据)。
编辑 1:
现在考虑这个 MEX 函数:
droplastcol.c
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mwSize N;
if (nrhs != 1 || nlhs > 0)
mexErrMsgIdAndTxt("dump:error", "Wrong number of arguments.");
if (mxIsSparse(prhs[0]) || !mxIsNumeric(prhs[0]) || mxGetNumberOfDimensions(prhs[0])>2)
mexErrMsgIdAndTxt("dump:error", "Expects a dense numeric matrix.");
N = mxGetN(prhs[0]);
if (N > 0)
mxSetN((mxArray*)prhs[0], N-1);
}
它将删除矩阵的最后一列而不重新分配数据:
>> A = 1:5
A =
1 2 3 4 5
>> dump(A)
header = 0x0000000014829FA0
data = 0x000000007D4E1000
>> droplastcol(A)
>> A
A =
1 2 3 4
>> dump(A)
header = 0x0000000014829FA0
data = 0x000000007D4E1000
以这种方式使用mxSetN 是完全安全的,没有内存泄漏(它甚至可以很好地与“数据共享”和“懒惰的写时复制”一起使用,例如A=1:5;B=A;droplastcol(A);)。引用docs:
如果调用mxSetN 减少了mxArray 中的元素数量,您可能需要减少pr、pi、ir 和/或jc 数组的大小以使用堆空间更有效。但是,减小大小不是强制性的。
意味着为数据分配的堆内存仍然声称已使用,但在数组销毁时不会泄漏(clear A)。
注意:以上代码在 MATLAB R2014a 中测试。最近的版本似乎发生了一些变化,droplastcol MEX 函数在新版本中无法正常工作(我在 R2015b 中尝试过,函数返回时大小不受影响!)。
编辑2
似乎在较新的 MATLAB 版本上,我们必须使用未记录的 mxCreateSharedDataCopy。这是修改后的 MEX 函数:
droplastcol.c
#include "mex.h"
EXTERN_C mxArray* mxCreateSharedDataCopy(const mxArray*);
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mwSize N;
if (nrhs != 1 || nlhs > 1)
mexErrMsgIdAndTxt("dump:err", "Wrong number of arguments.");
if (mxIsSparse(prhs[0]) || !mxIsNumeric(prhs[0]) || mxGetNumberOfDimensions(prhs[0])>2)
mexErrMsgIdAndTxt("dump:err", "Expects a numeric matrix.");
plhs[0] = mxCreateSharedDataCopy(prhs[0]);
N = mxGetN(plhs[0]);
if (N > 0)
mxSetN((mxArray*)plhs[0], N-1);
}
现在我们在 R2015b 中对其进行测试:
>> mex -largeArrayDims droplastcol2.c
Building with 'Microsoft Visual C++ 2010 (C)'.
MEX completed successfully.
>> A = 1:5
A =
1 2 3 4 5
>> dump(A)
header = 0x00000000693A0B80
data = 0x00000000943A5D60
>> B = droplastcol2(A)
B =
1 2 3 4
>> A
A =
1 2 3 4 5
>> dump(A)
header = 0x00000000693A1600
data = 0x00000000943A5D60
>> dump(B)
header = 0x00000000693A0C60
data = 0x00000000943A5D60
你也可以这样做:A = droplastcol2(A),数据不会被重新分配,只会创建一个指向内存中相同数据的新标头。