简体   繁体   中英

How do I return a struct with dynamic multidimensional arrays in C?

I have the following problem: "If I try to return a struct with a dynamic multidimensional array, I lose the data inside of that array. If I use pointers for returning the struct I got the same problem. In some cases I have some positions in the array with the expected data" -> How can i return a struct which contains a dynamic multidimensional array? -> Where is my mistake?

Here is a sample program which shows the problem:

#include <stdio.h>
#include <stdlib.h>

struct just_a_structure
        {
         double **lf_multi_array;
         float   *f_array;
        };

struct just_a_structure get_struct(int i_length)
    {
     struct just_a_structure return_this_struct;
     return_this_struct.lf_multi_array  = (double**)malloc(i_length*sizeof(double*));
     return_this_struct.f_array         = (float*)malloc(i_length*sizeof(float*));

     double lf_dummy_0[10] = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9};
     double lf_dummy_1[10] = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9};

     return_this_struct.lf_multi_array[0] = lf_dummy_0;
     return_this_struct.f_array[0]       = 0.00;

     return_this_struct.lf_multi_array[1] = lf_dummy_1;
     return_this_struct.f_array[1]       = 0.01;    
    
     printf("\n output in function \n");
     printf("ma[0][0] %f \n", return_this_struct.lf_multi_array[0][0]);
     printf("ma[0][1] %f \n", return_this_struct.lf_multi_array[0][1]);
     printf("ma[0][2] %f \n", return_this_struct.lf_multi_array[0][2]);
     printf("ma[0][3] %f \n", return_this_struct.lf_multi_array[0][3]);
     printf("ma[0][4] %f \n", return_this_struct.lf_multi_array[0][4]);
     printf("ma[0][5] %f \n", return_this_struct.lf_multi_array[0][5]);
     printf("ma[0][6] %f \n", return_this_struct.lf_multi_array[0][6]);
     printf("ma[0][7] %f \n", return_this_struct.lf_multi_array[0][7]);
     printf("ma[0][8] %f \n", return_this_struct.lf_multi_array[0][8]);
     printf("ma[0][9] %f \n", return_this_struct.lf_multi_array[0][9]);
     printf("a[0] %f \n", return_this_struct.f_array[0]);
     
     printf("\n \n");
     printf("ma[1][0] %f \n", return_this_struct.lf_multi_array[1][0]);
     printf("ma[1][1] %f \n", return_this_struct.lf_multi_array[1][1]);
     printf("ma[1][2] %f \n", return_this_struct.lf_multi_array[1][2]);
     printf("ma[1][3] %f \n", return_this_struct.lf_multi_array[1][3]);
     printf("ma[1][4] %f \n", return_this_struct.lf_multi_array[1][4]);
     printf("ma[1][5] %f \n", return_this_struct.lf_multi_array[1][5]);
     printf("ma[1][6] %f \n", return_this_struct.lf_multi_array[1][6]); 
     printf("ma[1][7] %f \n", return_this_struct.lf_multi_array[1][7]); 
     printf("ma[1][8] %f \n", return_this_struct.lf_multi_array[1][8]); 
     printf("ma[1][9] %f \n", return_this_struct.lf_multi_array[1][9]);
     printf("a[1] %f \n", return_this_struct.f_array[1]);
    
     return return_this_struct;
    }
    
    
int main()
    {    
     int i_length = 500;
     
     struct just_a_structure returned_struct;
    
     returned_struct = get_struct(i_length);
    
     printf("\n output in main \n");
     printf("ma[0][0] %f \n", returned_struct.lf_multi_array[0][0]);
     printf("ma[0][1] %f \n", returned_struct.lf_multi_array[0][1]);
     printf("ma[0][2] %f \n", returned_struct.lf_multi_array[0][2]);
     printf("ma[0][3] %f \n", returned_struct.lf_multi_array[0][3]);
     printf("ma[0][4] %f \n", returned_struct.lf_multi_array[0][4]);
     printf("ma[0][5] %f \n", returned_struct.lf_multi_array[0][5]);
     printf("ma[0][6] %f \n", returned_struct.lf_multi_array[0][6]);
     printf("ma[0][7] %f \n", returned_struct.lf_multi_array[0][7]);
     printf("ma[0][8] %f \n", returned_struct.lf_multi_array[0][8]);
     printf("ma[0][9] %f \n", returned_struct.lf_multi_array[0][9]);
     printf("a[0] %f \n", returned_struct.f_array[0]);
     
     printf("\n \n");
     printf("ma[1][0] %f \n", returned_struct.lf_multi_array[1][0]);
     printf("ma[1][1] %f \n", returned_struct.lf_multi_array[1][1]);
     printf("ma[1][2] %f \n", returned_struct.lf_multi_array[1][2]);
     printf("ma[1][3] %f \n", returned_struct.lf_multi_array[1][3]);
     printf("ma[1][4] %f \n", returned_struct.lf_multi_array[1][4]);
     printf("ma[1][5] %f \n", returned_struct.lf_multi_array[1][5]);
     printf("ma[1][6] %f \n", returned_struct.lf_multi_array[1][6]); 
     printf("ma[1][7] %f \n", returned_struct.lf_multi_array[1][7]); 
     printf("ma[1][8] %f \n", returned_struct.lf_multi_array[1][8]); 
     printf("ma[1][9] %f \n", returned_struct.lf_multi_array[1][9]);
     printf("a[1] %f \n", returned_struct.f_array[1]);

     return 0;
    }

Here is the output of the program:

 output in function
ma[0][0] 0.000000
ma[0][1] 0.100000
ma[0][2] 0.200000
ma[0][3] 0.300000
ma[0][4] 0.400000
ma[0][5] 0.500000
ma[0][6] 0.600000
ma[0][7] 0.700000
ma[0][8] 0.800000
ma[0][9] 0.900000
a[0] 0.000000


ma[1][0] 1.000000
ma[1][1] 1.100000
ma[1][2] 1.200000
ma[1][3] 1.300000
ma[1][4] 1.400000
ma[1][5] 1.500000
ma[1][6] 1.600000
ma[1][7] 1.700000
ma[1][8] 1.800000
ma[1][9] 1.900000
a[1] 0.010000

 output in main
ma[0][0] 27471811305989270000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[0][1] 27471811305989273000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[0][2] 24361411570075108000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[0][3] 22932960320429427000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[0][4] 0.000000
ma[0][5] 0.000000
ma[0][6] 23754367043167420000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[0][7] 27471811305989273000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[0][8] -519591837366427200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000 
ma[0][9] 0.000000
a[0] 0.000000


ma[1][0] 21266032917854245000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[1][1] 21427427439475540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[1][2] 0.000000
ma[1][3] 0.000000
ma[1][4] 0.000000
ma[1][5] 0.000000
ma[1][6] 22334679298120726000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[1][7] -1.#QNAN0
ma[1][8] 23669996832223273000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000
ma[1][9] 0.000000
a[1] 0.010000

i tried to implement the solution provided by @Etini and @Sourav Ghost. But know the program crashes immediately after starting it.

     return_this_struct.lf_multi_array[0] = (double*)malloc(10*sizeof(double));
     return_this_struct.lf_multi_array[1] = (double*)malloc(10*sizeof(double));
     return_this_struct.f_array           = (float*)malloc(i_length*sizeof(float*));

     double lf_dummy_0[10] = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9};
     double lf_dummy_1[10] = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9};
     
     for(int i = 0; i < 10; i++)
        {
         return_this_struct.lf_multi_array[0][i] = lf_dummy_0[i];
        }
     for(int i = 0; i < 10; i++)
        {
         return_this_struct.lf_multi_array[1][i] = lf_dummy_1[i];
        }
        
     return_this_struct.f_array[0]       = 0.00;
     return_this_struct.f_array[1]       = 0.01;    
    ```

In your code

return_this_struct.lf_multi_array[0] = lf_dummy_0;

and

return_this_struct.lf_multi_array[1] = lf_dummy_1;

both are storing the address of a local variable in the pointer, hence once returned from the called function, these local variables go out of scope and you're accessing invalid memory, creating undefined behaviour.

If you want the values to be returned, allocate memory for the array, and copy individual element values.

As Sourav Ghost said you need to allocate and copy individual elements, this is how i made it work for the first array

 return_this_struct.lf_multi_array[0] = (double*) malloc(10*sizeof(double));
     for(int i = 0; i < 10; i++){
         return_this_struct.lf_multi_array[0][i] = lf_dummy_0[i];
     }

My C is rusty so bear with me.

These two lines are creating a stack memory which will be deleted when exiting the function:

double lf_dummy_0[10] = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9};
double lf_dummy_1[10] = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9};

These two lines are not copying the values stored in lf_dummy_0 and lf_dummy_0 into your multi_array as you are thinking.

return_this_struct.lf_multi_array[0] = lf_dummy_0;
...
return_this_struct.lf_multi_array[1] = lf_dummy_1;

Now, how about we break your problem into pieces?

First, lets write a method that will return a single array of values and be sure the values are accessible after the function ends.

#include <stdio.h>
#include <stdlib.h>

double *getPointerToArrayWithValues(int capacity){
    double * array = (double *) malloc(sizeof(double) * capacity);    

    double value = 0.0;
    for (int i = 0; i < capacity; ++i){
        array[i] = value;
        value += 0.1;
    }
    
    return array;
}

int main()
{
    int size = 10;
    double * array = getPointerToArrayWithValues(size);
    for (int i = 0; i < size; ++i){
        printf("arra[%d]: %f\n", i, array[i]);
    }
    free(array);
    return 0;
}

You might notices that after I obtain the values and displayed them, I am freeing the memory that we use. We must clean after ourselves.

Notices also that I am using double instead of float. The reason is that it will be easier to use this later than mixing float and double.

Second, lets see if we can create an array or arrays aka. multi-array.

#include <stdio.h>
#include <stdlib.h>

double **getPointerToMultiArrayWithValues(int rowCapacity, int columnCapacity){
    // Add space for rows
    double ** multiArray = (double **) malloc(sizeof(double * ) * rowCapacity);
     
    // Add space for columns
    for(int i = 0; i < rowCapacity; ++i){
        multiArray[i] = (double *) malloc(sizeof(double *) * columnCapacity);   
    }
    
    double value = 0.0;
    for (int row = 0; row < rowCapacity; ++row){
        for (int column = 0; column < columnCapacity; ++column){
            multiArray[row][column] = value;
            value += 0.1;
        }
    }
    
    return multiArray;
}

int main()
{
    int numberOfArrayToHold = 10;
    int sizeOfEachArray = 10;
    double ** multiArray = getPointerToMultiArrayWithValues(numberOfArrayToHold, sizeOfEachArray);
    
    for(int i = 0; i < numberOfArrayToHold; ++i){
        for (int j = 0; j < sizeOfEachArray; ++j){
            printf("multiArray[%d][%d]: %f\n", i, j, multiArray[i][j]);
        }
    }

    // Lets be grown up and clean after ourselves
    for (int i = 0; i < numberOfArrayToHold; ++i){
        free(multiArray[i]);
    }
    free(multiArray);

    return 0;
}

Now, that you have solve how to create a single array and a multi-array (double in this case), we can focus on creating a struct that will hold such array:

#include <stdio.h>
#include <stdlib.h>

struct ArrayHolder{
  int rows;
  int columns;
  double ** multiArray;
};

double **getPointerToMultiArrayWithValues(int rowCapacity, int columnCapacity){
    // Add space for rows
    double ** multiArray = (double **) malloc(sizeof(double * ) * rowCapacity);
     
    // Add space for columns
    for(int i = 0; i < rowCapacity; ++i){
        multiArray[i] = (double *) malloc(sizeof(double *) * columnCapacity);   
    }
    
    double value = 0.0;
    for (int row = 0; row < rowCapacity; ++row){
        for (int column = 0; column < columnCapacity; ++column){
            multiArray[row][column] = value;
            value += 0.1;
        }
    }
    
    return multiArray;
}

int main()
{
    struct ArrayHolder arrayHolder;
    arrayHolder.rows = 10;
    arrayHolder.columns = 10;
    arrayHolder.multiArray = getPointerToMultiArrayWithValues(arrayHolder.rows, arrayHolder.columns);
    
    for(int i = 0; i < arrayHolder.rows; ++i){
        for (int j = 0; j < arrayHolder.columns; ++j){
            printf("multiArray[%d][%d]: %f\n", i, j,  arrayHolder.multiArray[i][j]);
        }
    }
    
    for (int i = 0; i < arrayHolder.rows; ++i){
        free(arrayHolder.multiArray[i]);
    }
    free( arrayHolder.multiArray);

    return 0;
}

However, we wish to return a struct, isn't it? Then,

#include <stdio.h>
#include <stdlib.h>

struct ArrayHolder{
  int rows;
  int columns;
  double ** multiArray;
};

double **getPointerToMultiArrayWithValues(int rowCapacity, int columnCapacity){
    // Add space for rows
    double ** multiArray = (double **) malloc(sizeof(double * ) * rowCapacity);
     
    // Add space for columns
    for(int i = 0; i < rowCapacity; ++i){
        multiArray[i] = (double *) malloc(sizeof(double *) * columnCapacity);   
    }
    
    double value = 0.0;
    for (int row = 0; row < rowCapacity; ++row){
        for (int column = 0; column < columnCapacity; ++column){
            multiArray[row][column] = value;
            value += 0.1;
        }
    }
    
    return multiArray;
}

struct ArrayHolder getStructWithArray(){
    struct ArrayHolder arrayHolder;
    arrayHolder.rows = 10;
    arrayHolder.columns = 10;
    arrayHolder.multiArray = getPointerToMultiArrayWithValues(arrayHolder.rows, arrayHolder.columns);
    return arrayHolder;
}

int main()
{
   
    struct ArrayHolder arrayHolder = getStructWithArray(); 
    for(int i = 0; i < arrayHolder.rows; ++i){
        for (int j = 0; j < arrayHolder.columns; ++j){
            printf("multiArray[%d][%d]: %f\n", i, j,  arrayHolder.multiArray[i][j]);
        }
    }
    
    for (int i = 0; i < arrayHolder.rows; ++i){
        free(arrayHolder.multiArray[i]);
    }
    free( arrayHolder.multiArray);

    return 0;
}

In this case, we are returning the struct by value (copied)

However, we can also do it by pointer!

#include <stdio.h>
#include <stdlib.h>

struct ArrayHolder{
  int rows;
  int columns;
  double ** multiArray;
};

double **getPointerToMultiArrayWithValues(int rowCapacity, int columnCapacity){
    // Add space for rows
    double ** multiArray = (double **) malloc(sizeof(double * ) * rowCapacity);
     
    // Add space for columns
    for(int i = 0; i < rowCapacity; ++i){
        multiArray[i] = (double *) malloc(sizeof(double *) * columnCapacity);   
    }
    
    double value = 0.0;
    for (int row = 0; row < rowCapacity; ++row){
        for (int column = 0; column < columnCapacity; ++column){
            multiArray[row][column] = value;
            value += 0.1;
        }
    }
    
    return multiArray;
}

struct ArrayHolder * getPointerToStructWithArray(){
    struct ArrayHolder* arrayHolder = (struct ArrayHolder *) malloc(sizeof(struct ArrayHolder));
    arrayHolder -> rows = 10;
    arrayHolder -> columns = 10;
    arrayHolder -> multiArray = getPointerToMultiArrayWithValues(arrayHolder -> rows, arrayHolder -> columns);
    return arrayHolder;
}

int main()
{
   
    struct ArrayHolder * arrayHolder = getPointerToStructWithArray(); 
    for(int i = 0; i < arrayHolder -> rows; ++i){
        for (int j = 0; j < arrayHolder -> columns; ++j){
            printf("multiArray[%d][%d]: %f\n", i, j,  arrayHolder -> multiArray[i][j]);
        }
    }
    
    for (int i = 0; i < arrayHolder -> rows; ++i){
        free(arrayHolder -> multiArray[i]);
    }
    free( arrayHolder -> multiArray);
    
    free(arrayHolder);

    return 0;
}

Let me know if this helped you out.

Before using the loop to assign the values you have to allocate the array like this return_this_struct.lf_multi_array[0] = (double*) malloc(10*sizeof(double));

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