0

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.

  • I want to duplicate the data, not only copy pointers. This is what I’m trying with `memcpy`: `memcpy(positions_particles_destination[i], positions_particles_source[i], number_particles_total * sizeof(double));` That command shows toward the end in the code. – Felipe Evaristo Dec 07 '21 at 06:28
  • I steered you wrong by saying the double index `[i][j]` was nonsense. My apologies, answer is forthcoming. – yano Dec 07 '21 at 07:11

1 Answers1

1

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.

yano
  • 4,827
  • 2
  • 23
  • 35
  • Your code worked for me! Thanks! Question, though, does `positions_particles_source[i][j] = (double)i + (double)j + 100.13;` assign data as "address data" or as "variable-content data"? I've seen examples where to assign data as "variable-content data" (which is my intention), I must precede `positions_particles_source[i][j]` with the dereference operator `*`; however, my debugger throws the warning "Indirection requires pointer operand ('double' invalid)" when I try to do that. I'm afraid that I'm storing data as "storage data" the way I've been doing things. I don't know how to change that. – Felipe Evaristo Dec 07 '21 at 16:40
  • @FelipeEvaristo I'd have to google the what those words mean exactly. All I meant with `(double)i + (double)j + 100.13;` was to arbitrarily assign data to the array strictly for the purposes of the example. It has no meaning. I just as easily could have written `45.6`. I used `i` and `j` to add some variation to the data. That is a `double` value, not an address. When you dereference one of these arrays with `[i][j]`, the resulting type is a `double`. If you try to dereference further using `*`, that's incorrect, there are no pointers left to follow. – yano Dec 07 '21 at 16:45
  • @FelipeEvaristo see my edit, hope it clarifies things. – yano Dec 07 '21 at 17:03
  • 1
    Great! My confusion had to do with what you explained in "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)." I wasn't sure what type `dPtrs[i][j]` was. I was under the impression it was `double*`. I'm glad to learn it's actually `double`, as that's what I originally had intended it to be! :) – Felipe Evaristo Dec 07 '21 at 18:17
  • @FelipeEvaristo ah ok, yes remember the `[]` and `*` can both be used to dereference in C. For any type pointer `T* myPtr`, then `*(myPtr + i)` is equivalent to `myPtr[i]` , and for any `T** myDblPtr`, then `*((*(mDblPtr + i)) + j)` is equivalent to `myDblPtr[i][j]`. – yano Dec 07 '21 at 18:23