为什么指针的值会意外改变?
Why does the value of a pointer unexpectedly change?
我已经将 C++ 集成到 Matlab 中;在我的示例中,有三个简单文件:一个 Matlab 脚本 main.m
、一个 Matlab class 文件 myClass.m
和一个 C++ 文件 myMex.cpp
.
在 main.m
中,我调用 class 给出一个字符串作为输入。
main.m
:
myVar = myClass('test.bin');
myClass.m
:
classdef myClass < handle
properties
bitStreamName;
cabac_handle;
end
methods
function obj = myClass(fn)
obj.bitStreamName = fn;
obj.cabac_handle = myMex('First', obj.bitStreamName);
myMex('Second', obj.cabac_handle);
end
end
end
myMex.cpp
:
#include "mex.h"
#include <iostream>
using namespace std;
void _main();
class CABAC {
public:
CABAC() {};
~CABAC() {};
char* fn;
};
// the MEX interface function
void mexFunction(
int nlhs, // Number of expected output mxArrays
mxArray *plhs[], // Array of pointers to the expected output mxArrays
int nrhs, // Number of input mxArrays
const mxArray *prhs[] // Array of pointers to the input mxArrays
)
{
CABAC *c; // pointer to (new) instance of the CABAC class
char* fn = 0;
char cmd[64]; // temp char array to hold the command
// start parsing the input command
mxGetString(prhs[0], cmd, sizeof(cmd));
string inputCmd(cmd);
if (inputCmd == "First")
{
// get the filename string
fn = mxArrayToString(prhs[1]);
c = new CABAC;
uintptr_t c_value = (uintptr_t)c;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
*mxGetPr(plhs[0]) = c_value; // return pointer to matlab environment
c->fn = fn;
mexPrintf("Pointer: %p\n", c);
mexPrintf("Filename: %s\n", c->fn);
}
else if (inputCmd == "Second")
{
uintptr_t my_value = *mxGetPr(prhs[1]);
CABAC *my_pointer = (CABAC *)my_value;
mexPrintf("Pointer: %p\n", my_pointer);
mexPrintf("Filename: %s\n", my_pointer->fn);
}
}
第一次调用 myMex.cpp
时,我创建了一个 class CABAC
的对象,并将字符串 "test.bin"
关联到它的属性 fn
.最后将指针的值返回给Matlab环境。
第二次,我只是得到了指向先前实例化对象的指针,这里发生了一些奇怪的事情。特别是,同时打印指针的地址和属性 fn
,前者总是正确的(即对象的地址相同),但后者有时是正确的("test.bin"
是打印),有时完全随机错误(出现奇怪的字符串,每次执行都不同)。
要执行我的代码,您可以简单地 运行 main.m
在使用以下指令编译 myMex.cpp
之后:
mex CXXFLAGS="$CXXFLAGS -std=c++11" -g myMex.cpp
或
mex COMPFLAGS="$CXXFLAGS -std=c++11" -g myMex.cpp
你能帮我理解文件名发生了什么吗?
编辑:似乎 c
在第一个 myMex
调用后被删除。如何将指向 CABAC
对象(及其所有属性)的指针保存在内存中?
这里有很多问题。首先,您将指针投射到 double
,它不保留其表示形式:
uintptr_t c_value = (uintptr_t)c;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
*mxGetPr(plhs[0]) = c_value; // return pointer to matlab environment
相反,创建一个 64 位整数数组以将指针保存到:
plhs[0] = mxCreateNumericMatrix(1, 1, mxUINT64_CLASS, mxREAL);
*(uint64_t*)mxGetData(plhs[0]) = (uint64_t)c;
(自 MATLAB R2018a 起有 alternatives to mxGetData
for each type,在本例中为 mxGetUint64s
)。
其次,内存 new
ed 可能会在卸载 MEX 文件时被删除。这随时可能发生。为防止出现这种情况,请使用 mexLock
将 MEX 文件锁定在内存中。为防止内存泄漏,您需要包含在用完内存后删除内存的代码。由于你的MATLAB-class是一个handleclass,这个是可以完成的
第三,很容易用错误的指针调用 MEX 文件,这很可能会导致整个 MATLAB 崩溃。您当前的实施无法解决这个问题。
第四,正如Navan所指出的,以下行创建了字符串的副本,但在由 MATLAB 维护的内存缓冲区中:
fn = mxArrayToString(prhs[1]);
MATLAB 将自动删除由 mxMalloc
分配的所有内存以及 mexFunction
末尾的类似内存。因此,fn
将指向释放的内存。您需要在您管理的内存中手动创建一个副本。我建议您将字符串复制到 std::string
中:
class CABAC {
public:
std::string fn;
};
fn = mxArrayToString(prhs[1]); // Will copy the string to `fn`.
但是,我会推荐一种不同的方法:
- 在您的 MEX 文件中,保留指向已分配对象的指针数组。
- MEX 文件没有 return 指针,而是 return 数组索引。
- MEX 文件现在可以测试输入 "handle" 是否在数组的有效范围内,以及该数组是否包含有效指针或
NULL
指针(例如,对于已删除的对象或尚未使用的数组元素)。
- 锁定 MEX 文件,但允许命令解锁它。解锁 MEX 文件也会删除所有分配的对象。
- 您可以包含在删除最后一个分配的对象时自动解锁 MEX 文件的逻辑。
您应该知道全局变量将在对 MEX 文件的调用之间持续存在(只要它被锁定)。因此,您需要一个全局数组(在 mexFunction
之外定义)来存储指针。或者,在 mexFunction
.
中声明数组 static
This MEX-file 实现了此处描述的建议,虽然它远非一个微不足道的示例,但我希望它可以作为一个有用的起点。在此函数中,我使用 std::map
而不是普通数组来存储句柄。
我已经将 C++ 集成到 Matlab 中;在我的示例中,有三个简单文件:一个 Matlab 脚本 main.m
、一个 Matlab class 文件 myClass.m
和一个 C++ 文件 myMex.cpp
.
在 main.m
中,我调用 class 给出一个字符串作为输入。
main.m
:
myVar = myClass('test.bin');
myClass.m
:
classdef myClass < handle
properties
bitStreamName;
cabac_handle;
end
methods
function obj = myClass(fn)
obj.bitStreamName = fn;
obj.cabac_handle = myMex('First', obj.bitStreamName);
myMex('Second', obj.cabac_handle);
end
end
end
myMex.cpp
:
#include "mex.h"
#include <iostream>
using namespace std;
void _main();
class CABAC {
public:
CABAC() {};
~CABAC() {};
char* fn;
};
// the MEX interface function
void mexFunction(
int nlhs, // Number of expected output mxArrays
mxArray *plhs[], // Array of pointers to the expected output mxArrays
int nrhs, // Number of input mxArrays
const mxArray *prhs[] // Array of pointers to the input mxArrays
)
{
CABAC *c; // pointer to (new) instance of the CABAC class
char* fn = 0;
char cmd[64]; // temp char array to hold the command
// start parsing the input command
mxGetString(prhs[0], cmd, sizeof(cmd));
string inputCmd(cmd);
if (inputCmd == "First")
{
// get the filename string
fn = mxArrayToString(prhs[1]);
c = new CABAC;
uintptr_t c_value = (uintptr_t)c;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
*mxGetPr(plhs[0]) = c_value; // return pointer to matlab environment
c->fn = fn;
mexPrintf("Pointer: %p\n", c);
mexPrintf("Filename: %s\n", c->fn);
}
else if (inputCmd == "Second")
{
uintptr_t my_value = *mxGetPr(prhs[1]);
CABAC *my_pointer = (CABAC *)my_value;
mexPrintf("Pointer: %p\n", my_pointer);
mexPrintf("Filename: %s\n", my_pointer->fn);
}
}
第一次调用 myMex.cpp
时,我创建了一个 class CABAC
的对象,并将字符串 "test.bin"
关联到它的属性 fn
.最后将指针的值返回给Matlab环境。
第二次,我只是得到了指向先前实例化对象的指针,这里发生了一些奇怪的事情。特别是,同时打印指针的地址和属性 fn
,前者总是正确的(即对象的地址相同),但后者有时是正确的("test.bin"
是打印),有时完全随机错误(出现奇怪的字符串,每次执行都不同)。
要执行我的代码,您可以简单地 运行 main.m
在使用以下指令编译 myMex.cpp
之后:
mex CXXFLAGS="$CXXFLAGS -std=c++11" -g myMex.cpp
或
mex COMPFLAGS="$CXXFLAGS -std=c++11" -g myMex.cpp
你能帮我理解文件名发生了什么吗?
编辑:似乎 c
在第一个 myMex
调用后被删除。如何将指向 CABAC
对象(及其所有属性)的指针保存在内存中?
这里有很多问题。首先,您将指针投射到 double
,它不保留其表示形式:
uintptr_t c_value = (uintptr_t)c;
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
*mxGetPr(plhs[0]) = c_value; // return pointer to matlab environment
相反,创建一个 64 位整数数组以将指针保存到:
plhs[0] = mxCreateNumericMatrix(1, 1, mxUINT64_CLASS, mxREAL);
*(uint64_t*)mxGetData(plhs[0]) = (uint64_t)c;
(自 MATLAB R2018a 起有 alternatives to mxGetData
for each type,在本例中为 mxGetUint64s
)。
其次,内存 new
ed 可能会在卸载 MEX 文件时被删除。这随时可能发生。为防止出现这种情况,请使用 mexLock
将 MEX 文件锁定在内存中。为防止内存泄漏,您需要包含在用完内存后删除内存的代码。由于你的MATLAB-class是一个handleclass,这个是可以完成的
第三,很容易用错误的指针调用 MEX 文件,这很可能会导致整个 MATLAB 崩溃。您当前的实施无法解决这个问题。
第四,正如Navan所指出的,以下行创建了字符串的副本,但在由 MATLAB 维护的内存缓冲区中:
fn = mxArrayToString(prhs[1]);
MATLAB 将自动删除由 mxMalloc
分配的所有内存以及 mexFunction
末尾的类似内存。因此,fn
将指向释放的内存。您需要在您管理的内存中手动创建一个副本。我建议您将字符串复制到 std::string
中:
class CABAC {
public:
std::string fn;
};
fn = mxArrayToString(prhs[1]); // Will copy the string to `fn`.
但是,我会推荐一种不同的方法:
- 在您的 MEX 文件中,保留指向已分配对象的指针数组。
- MEX 文件没有 return 指针,而是 return 数组索引。
- MEX 文件现在可以测试输入 "handle" 是否在数组的有效范围内,以及该数组是否包含有效指针或
NULL
指针(例如,对于已删除的对象或尚未使用的数组元素)。 - 锁定 MEX 文件,但允许命令解锁它。解锁 MEX 文件也会删除所有分配的对象。
- 您可以包含在删除最后一个分配的对象时自动解锁 MEX 文件的逻辑。
您应该知道全局变量将在对 MEX 文件的调用之间持续存在(只要它被锁定)。因此,您需要一个全局数组(在 mexFunction
之外定义)来存储指针。或者,在 mexFunction
.
static
This MEX-file 实现了此处描述的建议,虽然它远非一个微不足道的示例,但我希望它可以作为一个有用的起点。在此函数中,我使用 std::map
而不是普通数组来存储句柄。