2011-11-09 56 views
3

我正在研究模型,它將結果輸出爲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時,兩者都被正確分配,那麼這些指針如何重疊呢?我忽略了什麼?

+0

任何機會,你可以使用C++與乾淨的類,std :: vectors等? – Gabriel

+1

你需要寫什麼版本的.mat文件?更新版本的Matlab使用hdf5格式,因此您可以使用hdf5庫並將Matlab特定信息添加到它們。 – Jonas

+0

@ Gabriel:我從來沒有使用C++,只有C. –

回答

2

我認爲這個問題是你的最後聲明:

mxSetFieldByNumber(mxStruct, 0, /* THIRD FIELD */ 3, mxSubStruct); 

您要分配的mxStruct第三場另一個嵌套結構變量,問題是mxStruct與只有兩個字段定義:

mxStruct = mxCreateStructMatrix(1, 1, /* TWO */ 2, fieldNames1); 

與MATLAB,你的代碼(據我可以告訴)不支持動態添加結構域:

%# -- MATLAB code -- 
s = struct('f1',[], 'f2',[]); 
s.f3 = 99;  %# add a new field 

這不會很難實現,你只需重新分配指針數組來容納一個字段並增加字段數。

+0

實際上你試圖訪問結構的第4個字段(C語言中從零開始的索引) – Amro

+0

你是對的,但是這個錯誤來源於我爲這篇文章所做的代碼簡化。在它失敗的情況下,我實際上有一個有12個字段的結構,但這只是巨大的發佈在這裏。我已編輯帖子以更正索引。 –

+0

對於即時添加結構字段:這是一個有用的功能,也許我稍後將實現它 - 感謝! –