简体   繁体   中英

How are C structs with arrays returned?

Suppose we have:

typedef struct {
    uint8_t someVal;
} Entry

typedef struct {
    Entry grid[3][3];
} Matrix

//Make a 3x3 matrix of all 0s
Matrix emptyMatrix(void) {
    Entry zero = {.value = 0}
    Matrix matrix;
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++) {
            //Shallow copy empty struct
            matrix.grid[i][j] = zero;
        }
    return matrix;
}

Matrix myMatrix = emptyMatrix();

I understand that in C we're allowed to return a struct from a function so this works and I've tested it. However, it's unclear to me HOW the assignment works.

Does the compiler allocate the memory for myMatrix then copy each Entry element of the array in the Matrix struct returned by emptyMatrix() ?

I guess it would also be helpful to know the memory map of Matrix - I assumed that since grid is an array that Matrix 's memory would contain pointers. However, it apparently stores the value . If this is the case, my guess for how the assignment works makes much more sense to me.

Edit: It seems like people are answering the question incompletely. I want to know whether my guess of how the assignment works is correct.

Each instance of Matrix will contain a 3x3 array of Entry . When you assign one instance of Matrix to another, the contents of the source matrix will be copied to the destination matrix.

Arrays are not pointers. Array expressions will "decay" to pointers if the expression is not the operand of the sizeof or unary & operators, or is not a string literal used to initialize a character array in a declaration.

For example, if you had a function call like

printMatrix( myMatrix.grid );

the expression myMatrix.grid has type "3-element array of 3-element array of Entry "; since it's not the operand of the sizeof or unary & operator, it "decays" to an expression of type "pointer to 3-element array of Entry " ( Entry (*)[3] ) and the value of the expression is the address of the first element of grid (which will also be the address of the whole Matrix instance).

The ABI for each environment defines how structures are passed and returned by value. A common choice is this:

  • small structures, up to the size of 2 or 4 registers are returned in registers.

  • for larger objects, the caller allocates space on its stack frame for the return value and passes a pointer to the function. When returning, the function copies whatever object is being returned into the space to which it received a pointer for the return value. That's it. This simple method allows for recursive calls.

  • the optimizer tries to minimize the amount of copying, especially if it can expand the function inline or if the returned value is stored into an object, as opposed to discarded or passed by value to another function.

It does not matter if the structure has one or more member arrays. The same method applies to unions as well.

Identifiers bound to arrays may be interpreted as pointers, but that doesn't mean that the array variables are pointers.

The storage for the array is part of the memory layout of the struct itself. The same way as arrays declared on the stack are on the stack itself.

You can test this by yourself by checking sizeof(Matrix) .

So in short:

Does the compiler allocate the memory for myMatrix then copy each Entry element of the array in the Matrix struct returned by emptyMatrix()?

Yes

Matrix is a data type struct struct {Entry grid[3][3];} . For every object of the type Metrix memory of size Entry grid[3][3] will be allocated.
Function emptyMatrix is returning an object of type Matrix and the object will be returned by value just like any other object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM