在 VC++ 中呼叫 MATLAB Engine API 進行科學運算時,將 C++ 標準容器(如 std::vector<double>)的資料高效傳遞給 mxArray 是關鍵步驟。本文整理六種主流賦值方式,並說明記憶體管理注意事項。
| 函式 | 用途 |
|---|---|
mxCreateDoubleMatrix(m, n, mxREAL) | 建立 m×n 雙精度實數矩陣 |
mxGetPr(mxArray*) | 取得資料指標 (double*) |
mxSetPr(mxArray*, double*) | 設定資料指標(不複製資料,需自管生命週期) |
mxDestroyArray(mxArray*) | 釋放 mxArray 記憶體 |
engPutVariable(Engine*, name, mxArray*) | 將變數送入 MATLAB 工作區 |
engEvalString(Engine*, cmd) | 在 MATLAB 執行指令 |
engGetVariable(Engine*, name) | 從 MATLAB 取回變數 |
std::vector x = {...}, y = {...};
mxArray *mxa_x = mxCreateDoubleMatrix(x.size(), 1, mxREAL);
mxArray *mxa_y = mxCreateDoubleMatrix(y.size(), 1, mxREAL);
| 編號 | 程式碼 | 原理 | 優缺點 |
|---|---|---|---|
| 1 | memcpy(mxGetPr(mxa_x), &x[0], x.size()*sizeof(double)); | 直接複製記憶體,&x[0] 為首元素位址 | 效能最佳,標準 C 風格,需確保 vector 不重新配置 |
| 2 | memcpy(mxGetPr(mxa_x), &x.at(0), x.size()*sizeof(double)); | 同 1,使用 at() 邊界檢查 | 較安全,Debug 模式有檢查,Release 無額外開銷 |
| 3 | memcpy(mxGetPr(mxa_x), (void*)&(*x.begin()), x.size()*sizeof(double)); | 迭代器解參考取首元素位址 | 泛型寫法,適用於所有連續容器 |
| 4 | std::copy(x.begin(), x.end(), mxGetPr(mxa_x)); | STL 演算法複製 | 程式碼最簡潔,型別安全,效能等同 memcpy |
| 5 | mxSetPr(mxa_x, &x[0]); | 直接將 vector 內部陣列指標給 mxArray | 零複製,極快,但 mxDestroyArray 會嘗試 free 該記憶體導致崩潰,嚴禁混用 |
| 6 | mxSetData | 同 mxSetPr,較新 API | 同 5,同樣記憶體所有權問題 |
bool ellipsefit_engine(Engine *ep,
double &Xc, double &Yc, double &A, double &B, double &Phi, double &P,
const std::vector &x, const std::vector &y) {
mxArray *mxa_x = mxCreateDoubleMatrix(x.size(), 1, mxREAL);
mxArray *mxa_y = mxCreateDoubleMatrix(y.size(), 1, mxREAL);
// 推薦方式 4:std::copy,簡潔安全
std::copy(x.begin(), x.end(), mxGetPr(mxa_x));
std::copy(y.begin(), y.end(), mxGetPr(mxa_y));
// 送入 MATLAB 工作區
engPutVariable(ep, "x", mxa_x);
engPutVariable(ep, "y", mxa_y);
// 設定 MATLAB 搜尋路徑並呼叫函式
engEvalString(ep, "userpath('C:\\Users\\YourPath\\matlab_scripts');");
engEvalString(ep, "[Xc,Yc,A,B,Phi,P]=ellipsefit(x,y);");
// 取回結果
mxArray *mxa_Xc = engGetVariable(ep, "Xc");
mxArray *mxa_Yc = engGetVariable(ep, "Yc");
mxArray *mxa_A = engGetVariable(ep, "A");
mxArray *mxa_B = engGetVariable(ep, "B");
mxArray *mxa_Phi= engGetVariable(ep, "Phi");
mxArray *mxa_P = engGetVariable(ep, "P");
Xc = mxGetPr(mxa_Xc)[0];
Yc = mxGetPr(mxa_Yc)[0];
A = mxGetPr(mxa_A)[0];
B = mxGetPr(mxa_B)[0];
Phi = mxGetPr(mxa_Phi)[0];
P = mxGetPr(mxa_P)[0];
// 釋放所有 mxArray
mxDestroyArray(mxa_x); mxDestroyArray(mxa_y);
mxDestroyArray(mxa_Xc); mxDestroyArray(mxa_Yc);
mxDestroyArray(mxa_A); mxDestroyArray(mxa_B);
mxDestroyArray(mxa_Phi); mxDestroyArray(mxa_P);
return true;
}
memcpy 或 std::copy 時,資料被複製到 MATLAB 管理的記憶體,呼叫 mxDestroyArray 安全;使用 mxSetPr 時,所有權轉移給 MATLAB,vector 釋放或擴容會導致雙重釋放或使用已釋放記憶體。mxDestroyArray 之後,且不可 push_back/reserve 導致重新配置。mxCreateDoubleMatrix(rows, cols, mxREAL) 的 rows×cols 必須等於 vector size()。\\\\ 或使用原始字串字面值 R"(C:\Path)" (C++11)。memmove 指令mxArray,可重用緩衝區結語:VC++/MATLAB 混合程式的核心在於正確管理 mxArray 的記憶體所有權。採用 std::copy 複製資料至 MATLAB 內部緩衝區,再配合 mxDestroyArray 釋放,是最穩健、可維護的工程實踐。