我正在研究模型,它將結果輸出爲matlab的.mat文件格式,並且最初與matlab庫鏈接以使用其文件輸出函數。 最近,需求發生了變化(誰會猜到),以前只有linux的代碼現在必須在Windows上編譯,並且最好不需要matlab來編譯 - 但仍然輸出.mat文件。
所以我搜索並找到了libmatio(http://sourceforge.net/projects/matio/)。雖然這很容易與Linux鏈接(你只是從存儲庫安裝它),但它在windows上很糟糕(基本上沒有關於在Windows上構建它的信息)。事實上,似乎windows 1.3的支持實際上是在1.3.3版本(2008年的時候)中默默無聞地下降了。
此外,APi與matlab提供的完全不同,它需要我重寫/重構大量代碼。編寫替代Matlab C API寫入.matfiles
所以我想出了這個瘋狂的想法... 我需要一個替代Matlab API的插件,最好不用庫(爲了讓非編程人員易於編譯),所以我開始寫一。
我只實現我需要的功能(編寫雙精度,字符串和複數雙精度數組,以及結構和結構嵌套)。所有這些都已經正常工作,除了一個:結構數組。
所有的matlab數據都包含在一個名爲'mxArray'的結構中,並且根據它的類型,它包含指向double,complex double或一個或多個其他mxArray的指針。
在將mxArray寫入文件之前的最後一步是通過計算calcArraySize()
來計算字節中的大小(以及它的子節點大小)。
這會導致段錯誤,因爲我試圖訪問空指針。爲了追查我通過valgrind運行代碼的原因。與往常一樣,我會按照它們出現的順序來處理任何問題,因爲它們可能是後來發生的事情的原因。
所以第一件事情的valgrind告訴我的是:
==8405== Invalid write of size 8
==8405== at 0x00404541: mxSetFieldByNumber (mxSetFieldByNumber.c:18) [A]
==8405== by 0x00411679: calcAllRayInfo (calcAllRayInfo.c:156)
==8405== by 0x0041dd42: main (cTraceo.c:111)
==8405== Address 0x5500250 is 0 bytes inside a block of size 4 alloc'd
==8405== at 0x04c28f9f: malloc (vg_replace_malloc.c:236)
==8405== by 0x00401066: mallocChar (toolsMemory.c:69)
==8405== by 0x00404314: mxCreateStructMatrix (mxCreateStructMatrix.c:43) [B]
==8405== by 0x00411235: calcAllRayInfo (calcAllRayInfo.c:105)
==8405== by 0x0041dd42: main (cTraceo.c:111)
注:我標記[A]和[B]在下面的代碼。
結構定義(只顯示相關成員):
struct mxArray{
bool isStruct; //determines if this mxArray is a structure (which contains other mxArrays)
bool isChild; //determines wheter this mxArray is a Child of another (when set, its name will not be written to the matfile, as it is already defined in the parent's fieldnames
uintptr_t nFields;
char **fieldNames; //something like: {"theta","r","z"};
struct mxArray **field; //pointer to member mxArrays. only used when isStruct is set.
};
typedef struct mxArray mxArray;
我用它來與它一起的structMatrix分配內存函數的內容:爲mxArrays存在
mxArray* mxCreateStructMatrix(uintptr_t nRows, uintptr_t nCols, uintptr_t nFields, const char **fieldNames){
/*
* creates a 2D array of structures
*/
mxArray* outArray = NULL;
/* do some input value validation */
// allocate memory
outArray = malloc(nRows*nCols*sizeof(mxArray));
if (outArray == NULL){
fatal("mxCreateStructMatrix(): memory allocation error.");
}
// allocate memory for structure members (fields)
for (uintptr_t iStruct=0; iStruct<nCols*nRows; iStruct++){
outArray[iStruct].nFields = nFields;
outArray[iStruct].fieldNames = malloc(nFields*sizeof(char*));
//copy fieldnames into struct info
for (uintptr_t iField=0; iField<nFields; iField++){
//NOTE: strlen returns length of string not including the terminating NULL character
outArray[iStruct].fieldNames[iField] = mallocChar(strlen(fieldNames[iField])+1); // [B] <=======
strncpy(outArray[iStruct].fieldNames[iField], fieldNames[iField], strlen(fieldNames[iField]));
}
outArray[iStruct].field = NULL;
outArray[iStruct].field = malloc(nFields*sizeof(mxArray*));
if (outArray[iStruct].field == NULL){
fatal("mxCreateStructMatrix(): memory allocation error.\n");
}
}
return outArray;
}
兩個其他分配功能:
mxArray* mxCreateDoubleMatrix(uintptr_t nRows, uintptr_t nCols, uintptr_t numericType){
/*
* creates a 2D array of double precision floating point values.
* can be real or complex.
*/
[snip]
}
mxArray* mxCreateString(const char *inString)
/*
* creates an mxArray containing a string.
*/
[snip]
}
此函數分配一個mxArray作爲另一個mxArray的子項:
void mxSetFieldByNumber(mxArray* mxStruct, //pointer to the mxStruct
uint32_t index, //linear index of the element
uint32_t iField, //index of the structure's field which we want to set.
mxArray* inArray){ //the mxArray we want to assign to the mxStruct
/*
* Assigns an mxArray to one of the fields of a structArray
*/
inArray->isChild = true; //determines that this mxArray is a child of another one
mxStruct[index].field[iField] = inArray; // [A] <===============
}
用法爲:
//create parent mxArray:
mxStruct = mxCreateStructMatrix(1, //number of rows
1, //number of columns
2, //number of fields in each element
fieldNames1); //list of field names
//create children:
mxY = mxCreateDoubleMatrix(1 ,1, mxREAL);
mxZ = mxCreateDoubleMatrix(1 ,1, mxREAL);
mxSubStruct = mxCreateStructMatrix(1, //number of rows
1, //number of columns
3, //number of fields in each element
fieldNames2); //list of field names
/* copy some values into the mxArrays */
[snip]
//link children to parents
mxSetFieldByNumber(mxStruct, //pointer to the parent mxArray
0, //index of the element (linear)
0, //position of the field (in this case, field 0 is "w"
mxY); //the mxArray we want to add to the mxStruct
mxSetFieldByNumber(mxStruct, 0, 1, mxZ);
mxSetFieldByNumber(mxSubStruct, 0, 0, mxY);
mxSetFieldByNumber(mxSubStruct, 0, 1, mxZ);
mxSetFieldByNumber(mxStruct, 0, 2, mxSubStruct);
所以aparently,mxStruct[index].field[iField] = inArray;
被寫入mxStruct[index].fieldNames
,從而留下mxStruct[index].field[iField] == NULL
,然後導致段錯誤,當我嘗試訪問它。
這怎麼可能?當撥打mxCreateStructMatrix
時,兩者都被正確分配,那麼這些指針如何重疊呢?我忽略了什麼?
任何機會,你可以使用C++與乾淨的類,std :: vectors等? – Gabriel
你需要寫什麼版本的.mat文件?更新版本的Matlab使用hdf5格式,因此您可以使用hdf5庫並將Matlab特定信息添加到它們。 – Jonas
@ Gabriel:我從來沒有使用C++,只有C. –