How do I resize an mxArray

6 vues (au cours des 30 derniers jours)
Tim
Tim le 18 Nov 2016
Modifié(e) : James Tursa le 18 Nov 2016
I'm trying to write a C program that reads some data and writes to a mat-file. But I don't know how many elements to allocate for the mxArrays until all of the data has been read. I'm thinking I would allocate the mxArrays in chunks, growing their size as needed but I don't see any mx API function that would do this. Here's a skeleton:
/* Open MAT output file */
pmat = matOpen(ofile, "w"));
NROWS = 10000;
mxA = mxCreateDoubleMatrix(NROWS,1,mxREAL);
a = mxGetPr(mxA);
/* read all data and populate mxArray */
nrec = 0;
while (!EOF)
{
/* read some data from file... */
/* increase size of mxArray if needed */
if (nrec > mxGetM(mxA))
{
mxSetM(mxA,mxGetM(mxA)+NROWS); /* but this doesn't increase the size of the pr array */
???
}
/* add value to my mxArray */
a[nrec++] = mydata;
}
/* write to mat-file */
mxSetM(mxA, nrec);
matPutVariable(pmat, "a", mxA);
matClose(pmat);
mxDestroyArray(mxA);
I know there is mxRealloc, but that allocates by number of bytes. I have tried
mxSetM(mxA,mxGetM(mxA)+NROWS);
mxRealloc((void*)a, mxGetM(mxA)*sizeof(double))
Doesn't work. Program crashes. I would think there must be an easy way to do this by the number of elements but can't seem to find an answer. What am I missing?

Réponse acceptée

James Tursa
James Tursa le 18 Nov 2016
Modifié(e) : James Tursa le 18 Nov 2016
You are missing a step. After you do the mxRealloc, you need to set this new pointer into the mxArray. E.g.,
a = mxRealloc(a, mxGetM(mxA)*sizeof(double));
mxSetPr(mxA,a);
Also, your test is inconsistent. The variable nrec is a 0-based index for C array indexing, but the result of mxGetM is 1-based for MATLAB array indexing. So you should change this:
if (nrec > mxGetM(mxA))
to this:
if (nrec == mxGetM(mxA))
I.e., with your current code nrec is allowed to be equal to mxGetM(mxA), but this is 1 index beyond the actual memory allocated for 0-based indexing.
  2 commentaires
Tim
Tim le 18 Nov 2016
Works, but still surprised there isn't an easier way. If anyone from Mathworks is reading, the documentation is somewhat poor on this subject - surprising since native MATLAB arrays are re-sized so easily. For example, documentation for mxSetDimensions states, "mxSetDimensions allocates heap space to hold the input size array." But then a few lines later states, "mxSetDimensions does not allocate or deallocate any space for the pr or pi arrays." So what does it allocate for? Also, mxFree is to be used for memory allocated with the mx*alloc functions, but mxDestroyArray is used for mxCreate*. So in my example, am I supposed to use mxFree or mxDestroyArray?
James Tursa
James Tursa le 18 Nov 2016
Modifié(e) : James Tursa le 18 Nov 2016
"... documentation for mxSetDimensions states, "mxSetDimensions allocates heap space to hold the input size array." But then a few lines later states, "mxSetDimensions does not allocate or deallocate any space for the pr or pi arrays." So what does it allocate for? ..."
At the C level, the mxArray structure contains fields for the array size. E.g., a snippet of parts of the structure at the low level looks something like this:
struct mxArray {
:
size_t ndim;
:
union {
size_t M; /* Row size for 2D matrices, or */
size_t *dims; /* Pointer to dims array for nD > 2 arrays */
} Mdims;
size_t N; /* Column size for 2D matrices */
void *pr; /* Pointer to real data (or pointer to cell or field elements */
void *pi; /* Pointer to imag data (or pointer to field information */
:
};
When the object is 2D (i.e., ndim = 2) there are simply numbers stored in the M and N fields. But when the object is nD (i.e., ndims > 2) there is actually memory allocated elsewhere that contains all of the dimensions and the mxArray structure contains a pointer dims to this memory (i.e., a pointer is actually stored in the memory location where the M would have been stored inside the mxArray structure). So if you use mxSetDimensions to change a 2D to a 3D, then an allocation must take place for the dimensions to be stored behind dims. Likewise if you change a 3D to a 2D then a deallocation will take place for the memory behind dims. As a C programmer using the API functions you don't really need to be concerned with this background allocation/deallocation since it will be handled automatically by the API function mxSetDimensions.
This raises the question, what does mxGetDimensions return? For a 2D it will simply return &M (the address of the M field) in the mxArray structure. For a 3D (or higher) array it will return the value of the dims field. There is one exception to this. If you have compiled a mex routine with COMPAT32 on a 64-bit system, then the dimension data inside the mxArray will be 64-bit integers, but the mxGetDimensions routine will return a pointer to 32-bit integers. In this case, the 64-bit integer dimension data must be copied into a 32-bit integer array and the address of that is what is returned. (I suspect this is a newly allocated memory block but I can't confirm this).
The pr and pi fields, as the documentation states, must be manually handled by the programmer for any reallocation. This can actually be a benefit to the C programmer, since that allows you to do things like snip columns off of the end of the array (by changing only N) without physically reallocating any data ... so it is extremely fast.
"... So in my example, am I supposed to use mxFree or mxDestroyArray? ..."
In your example, mxA should have been declared as an (mxArray *) type, so you should be using mxDestroyArray (which you correctly are). You would NOT use mxFree(a) since the memory behind "a" is part of the mxArray mxA (i.e., mxDestroyArray(mxA) will automatically free anything behind pr, pi, jr, jc, dims also).

Connectez-vous pour commenter.

Plus de réponses (0)

Catégories

En savoir plus sur Resizing and Reshaping Matrices dans Help Center et File Exchange

Produits

Community Treasure Hunt

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

Start Hunting!

Translated by