How do I store a string from a MATLAB structure into a C structure in a MEX file?

I am building a MEX file that converts a MATLAB structure to a C structure, manipulates the C structure, and returns a modified MATLAB structure. A portion of the code that produces behavior I cannot explain is shown below. In the code portion, the variable "mx" is an mxArray structure, and the variable "cstructu" is a C structure.
field = mxGetField(mx, 0, "Field1");
ch = mxArrayToString(field);
strcpy(cstructu->Field1,ch);
mexPrintf("\n%s",cstructu->Field1);
field = mxGetField(mx, 0, "Field2");
ch = mxArrayToString(field);
strcpy(cstructu->Field2,ch);
mexPrintf("\n%s",cstructu->Field2);
field = mxGetField(mx, 0, "Field3");
ch = mxArrayToString(field);
strcpy(cstructu->Field3,ch);
mexPrintf("\n%s",cstructu->Field3);
mexPrintf("\n%s",cstructu->Field1);
mexPrintf("\n%s",cstructu->Field2);
mexPrintf("\n%s",cstructu->Field3);
The above code is divided into four code "blocks". The first three code blocks end in a "mexPrintf" statement that outputs to the command prompt the value stored for a given field in the C structure. The outputs from these three statements are as expected. The issue is with the "mexPrintf" statements in the fourth code block. When I print the value of "Field1", I get a concatenation of the values of "Field1", "Field2", and "Field3". When I print the value of "Field2", I get a concatenation of the values of "Field2" and "Field3". And when I print the value of "Field3", I just get the value of "Field3". What causes this behavior, and what do I need to do to correct it?

 Accepted Answer

Please show us the definitions of the variables involved. How large is cstructu->Field1, cstructu->Field2, and cstructu->Field3? I.e., how many characters can these hold? Are these arrays or dynamically allocated? How long are the strings you are getting from mx?
If the strings you are getting from MATLAB are longer than the number of characters that cstructu->Field1 can hold, then it might be that when writing into cstructu->Field2 you are overwriting the null character of cstructu->Field1, and similarly for the other cases. I.e., it could be a simple case of writing beyond the array size. You should check this, but we can't check this because you didn't show us enough code. E.g., if the MATLAB string is 10 characters then your C struct array needs to hold at least 11 characters because you need room for the trailing null character.
As a side note, your code is leaking the memory behind ch. You should have an mxFree(ch) statement after you are done using a particular ch.

4 Comments

I am placing my full code here. The previous code was for brevity, but I realize now per your response that I didn't provide enough information the first time through.
#include "mex.h"
#include <windows.h> // Needed for DLL commands
struct SIMP_STRUCT {
char Field1[40];
char Field2[2];
char Field3[12];
double Field4;
};
void mx2c(struct SIMP_STRUCT *, const mxArray *);
mxArray *c2mx(struct SIMP_STRUCT *, const mxArray *);
void mexFunction(int nlhs, mxArray *plhs[], int nrhs,
const mxArray *prhs[])
{
struct SIMP_STRUCT cstructu;
mx2c(&cstructu, prhs[0]);
plhs[0] = c2mx(&cstructu, prhs[0]);
}
void mx2c(struct SIMP_STRUCT *cstructu, const mxArray *mx)
{
mxArray *field;
char *ch;
double *pr;
field = mxGetField(mx, 0, "Field1");
ch = mxArrayToString(field);
strcpy(cstructu->Field1,ch);
mexPrintf("\n%s",cstructu->Field1);
field = mxGetField(mx, 0, "Field2");
ch = mxArrayToString(field);
strcpy(cstructu->Field2,ch);
mexPrintf("\n%s",cstructu->Field2);
field = mxGetField(mx, 0, "Field3");
ch = mxArrayToString(field);
strcpy(cstructu->Field3,ch);
mexPrintf("\n%s",cstructu->Field3);
mexPrintf("\n%s",cstructu->Field1);
mexPrintf("\n%s",cstructu->Field2);
mexPrintf("\n%s",cstructu->Field3);
field = mxGetField(mx, 0, "Field4");
pr = mxGetPr(field);
cstructu->Field4 = *pr;
}
mxArray *c2mx(struct SIMP_STRUCT *cstructu, const mxArray *mxIn)
{
int ifield, nfields;
const char **fnames; /* pointers to field names */
mxArray *mx, *field;
double *pr;
nfields = mxGetNumberOfFields(mxIn);
fnames = mxCalloc(nfields, sizeof(*fnames));
for (ifield=0; ifield< nfields; ifield++){
fnames[ifield] = mxGetFieldNameByNumber(mxIn,ifield);
}
mx = mxCreateStructMatrix(1, 1, nfields, fnames);
field = mxCreateString(cstructu->Field1);
mxSetField(mx, 0, "Field1", field);
field = mxCreateString(cstructu->Field2);
mxSetField(mx, 0, "Field2", field);
field = mxCreateString(cstructu->Field3);
mxSetField(mx, 0, "Field3", field);
field = mxCreateDoubleMatrix(1, 1, mxREAL);
pr = mxGetPr(field);
*pr = cstructu->Field4;
mxSetField(mx, 0, "Field4", field);
return mx;
}
What you expected was causing my issue was correct. My original structure definition did not take into account the additional NULL characters in the strings. When I modified my code above such that the C structure fields with strings were allocated one additional character (e.g. .Field1[41] instead of .Field1[40]), the issue was resolved.
strcpy() does not protect against overwriting when the source is too long for the destination.
I worry about the possibility that the source character vector might happen to have characters beyond U+00FF . strcpy() is really only designed for char* and not for wide characters. The documentation for mxArrayToString specifically mentions multibyte but it is not specific enough for my tastes as to how it handles multibyte.
There also https://www.mathworks.com/help/matlab/apiref/mxarraytoutf8string.html which is specific that it encodes to UTF-8 .
If you might possibly have multibyte then the required destination size is variable.
field = mxCreateDoubleMatrix(1, 1, mxREAL);
pr = mxGetPr(field);
*pr = cstructu->Field4;
can be replaced with this
field = mxCreateDoubleScalar(cstructu->Field4);
And to make your code more robust I would suggest putting in code to check the length of the MATLAB strings before you copy them into your C struct. Throw an error if they are too long.

Sign in to comment.

More Answers (0)

Categories

Products

Release

R2019b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!