Home > Software design >  How to copy an array of double pointers in C?
How to copy an array of double pointers in C?

Time:12-08

I have tried everything I could think of and find, but I am not being able to copy an array of double pointers into another one of the very same kind. How do I do that in C? I am trying to copy double *positions_particles_source[3] into double *positions_particles_destination[3]. I have tried copying it using direct assignment and memcpy but I have been unsuccessful in doing that. The last thing I have tried is:

double *positions_particles_source[3];
double *positions_particles_destination[3];
char *initial_structure_file_entries[3];

for (i = 0; i < 3; i  ) {
    positions_particles_source[i] = (double *) calloc(number_particles_total, sizeof(double));
    for (j = 0; j < number_particles_total; j  ) {
        positions_particles_source[i][j] = strtod(initial_structure_file_entries[i], NULL);
    }
    positions_particles_destination[i] = (double *) calloc(number_particles_total, sizeof(double));
    memcpy(positions_particles_destination[i],
           positions_particles_source[i],
           number_particles_total * sizeof(double));
}

I am actually not entirely sure that handling matrix data the way I have been is the most recommended in this case. I have tried to use double *positions_particles_source[3] to store number_particles_total by 3 data contained in an input text file. I am not very experienced with C and have been extremely confused as to how to manipulate matrix data in this language. I have the impression that the line positions_particles_source[i][j] = strtod(initial_structure_file_entries[i], NULL); assigns the output of strtod as an address versus as the content of a variable, which I would think would be more appropriate. Can someone let me know what the correct way to think about how data is handled through that line in my code is? The only reason I have been handling my input data that way is not being able to do it differently.

CodePudding user response:

You have arrays of double pointers, so simply copying the pointers from one array to the next will not duplicate the data. It will duplicate the pointers that point to the same data, no matter whether you use direct assignment or memcpy. That's called a shallow copy. What you want is a deep copy. Here's a possible implementation:

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

void printArray(double* array[], size_t rows, size_t cols)
{
    for (size_t r=0; r<rows; r  )
    {
        for (size_t c=0; c<cols; c  )
        {
            printf("%.2f, ", array[r][c]);
        }
        printf("\n");
    }
}

int main(void)
{
    // declare 2 arrays of double pointers
    double *positions_particles_source[3];
    double *positions_particles_destination[3];
    // arbitrarily using 5
    size_t number_particles_total = 5;

    // allocate space for these pointers to point to
    for (size_t i=0; i<3; i  )
    {
        // allocate the space of 5 doubles for each pointer in each array
        positions_particles_source[i] =
           calloc(number_particles_total, sizeof(*(positions_particles_source[0])));
// ^^ sizeof(*(positions_particles_source[0])) is the preferred
// method for sizing since you get the type "automatically",
// even if the type changes down the line. Later on I got lazy
// and switched to sizeof(double) because it was less typing
        if (positions_particles_source[i] == NULL) exit(-1);  // handle error how you want
        positions_particles_destination[i] =
           calloc(number_particles_total, sizeof(*(positions_particles_destination[0])));
        if (positions_particles_destination[i] == NULL) exit(-1);
    }

    // arbitrarily enter some data in first array
    for (size_t i=0; i<3; i  )
    {
        for (size_t j=0; j<number_particles_total; j  )
        {
            positions_particles_source[i][j] = (double)i   (double)j   100.13;
        }
    }

    printf("printing source array\n");
    printArray(positions_particles_source, 3, number_particles_total);

    // deep copy into destination
    for (size_t i=0; i<3; i  )
    {
        // could also use an inner loop from [0, num_particles)
        // and do direct assignment instead of memcpy
        memcpy(positions_particles_destination[i],
               positions_particles_source[i],
               sizeof(double) * number_particles_total);
    }

    printf("\nprinting dest array\n");
    printArray(positions_particles_destination, 3, number_particles_total);

    // clear source array
    for (size_t i=0; i<3; i  )
    {
        memset(positions_particles_source[i], 0, sizeof(double) * number_particles_total);
    }

    // you can see the source array is zeroed out here
    printf("\nprinting source array\n");
    printArray(positions_particles_source, 3, number_particles_total);

    // proves dest array has a deep copy since its data is retained even though
    // source has been cleared
    printf("\nprinting dest array\n");
    printArray(positions_particles_destination, 3, number_particles_total);

    // clean up
    for (size_t i=0; i<3; i  )
    {
        free(positions_particles_source[i]);
        free(positions_particles_destination[i]);
    }

    return 0;
}

Output:

printing source array
100.13, 101.13, 102.13, 103.13, 104.13, 
101.13, 102.13, 103.13, 104.13, 105.13, 
102.13, 103.13, 104.13, 105.13, 106.13, 

printing dest array
100.13, 101.13, 102.13, 103.13, 104.13, 
101.13, 102.13, 103.13, 104.13, 105.13, 
102.13, 103.13, 104.13, 105.13, 106.13, 

printing source array
0.00, 0.00, 0.00, 0.00, 0.00, 
0.00, 0.00, 0.00, 0.00, 0.00, 
0.00, 0.00, 0.00, 0.00, 0.00, 

printing dest array
100.13, 101.13, 102.13, 103.13, 104.13, 
101.13, 102.13, 103.13, 104.13, 105.13, 
102.13, 103.13, 104.13, 105.13, 106.13, 

Demo

EDIT: Based on your comment, I think you have a bit of confusion on the memory layout. I will try to diagram this below, although there is software that draws much prettier pictures than this:

Say you have the code (shortening your variable names ;) )

double* dPtrs[3];

In memory, this is what you have

 ----------------------------- 
| double* | double* | double* |
 ----------------------------- 
 dPtrs[0] | dPtrs[1] | dPtrs[2]  

Just on declaration, these 3 double pointers point nowhere, dereferencing them is undefined behavior. You must allocate space for them (using calloc, malloc, etc) before you can use them. The pointers can point to one-to-many doubles each, so say you do

dPtrs[0] = malloc(sizeof(double) * 4);

Assuming malloc doesn't return NULL (you should always check), then dPtrs[0] points to space that can fit 4 doubles somewhere in memory:

 ----------------------------- 
| double* | double* | double* |
 ----------------------------- 
 dPtrs[0] | dPtrs[1] | dPtrs[2]
   ^^     
   |       ------------------------------------------------------- 
   ~~~~~> | dPtrs[0][0] | dPtrs[0][1] | dPtrs[0][2] | dPtrs[0][3] |
           ------------------------------------------------------- 

You can do something similar for dPtrs[1] and dPtrs[2]. As I hope you can see, dPtrs[i] is a double* type, and dPtrs[i][j] is a double type (always be sure i and j are in bounds). Something like *dPtrs[i][j] is too many dereferences. A double is not an address, and treating it as such will only lead to problems. This is what your compiler is screaming about if I'm understanding you correctly.

  • Related