如何在 C 中复制双指针数组?

How to copy an array of double pointers in C?

我已经尝试了所有我能想到和找到的方法,但我无法将双指针数组复制到另一个同类数组中。我如何在 C 中做到这一点?我正在尝试将 double *positions_particles_source[3] 复制到 double *positions_particles_destination[3]。我曾尝试使用直接赋值和 memcpy 复制它,但我没有成功。我最后尝试的是:

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));
}

我实际上并不完全确定在这种情况下,我最推荐的方式是处理矩阵数据。我尝试使用 double *positions_particles_source[3] 通过输入文本文件中包含的 3 数据来存储 number_particles_total。我对 C 不是很有经验,并且对于如何用这种语言处理矩阵数据感到非常困惑。我的印象是 positions_particles_source[i][j] = strtod(initial_structure_file_entries[i], NULL); 行将 strtod 的输出分配为地址而不是变量的内容,我认为这更合适。有人可以让我知道通过我的代码中的那一行来思考如何处理数据的正确方法是什么吗?我一直以这种方式处理输入数据的唯一原因是无法以不同的方式进行处理。

您有 double 指针数组 ,因此简单地将指针从一个数组复制到下一个数组不会复制数据。无论你使用直接赋值还是memcpy,它都会复制指向相同数据的指针。这就是所谓的shallow copy. What you want is a deep copy。这是一个可能的实现:

#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;
}

输出:

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

编辑: 根据您的评论,我认为您对内存布局有些困惑。我将尝试在下面绘制图表,尽管有软件可以绘制比这更漂亮的图片:

假设您有代码(缩短您的变量名;))

double* dPtrs[3];

记忆中,这就是你所拥有的

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

就在声明中,这 3 个双指针指向任何地方,取消引用它们是未定义的行为。在使用它们之前,您必须为它们分配 space(使用 callocmalloc 等)。每个指针可以指向一对多 doubles,所以说你做

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

假设 malloc 不 return NULL(你应该 总是 检查),那么 dPtrs[0] 指向 space可以在内存中的某处容纳 4 doubles:

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

您可以对 dPtrs[1]dPtrs[2] 执行类似的操作。正如我希望你能看到的那样,dPtrs[i] 是一个 double* 类型,而 dPtrs[i][j] 是一个 double 类型(始终确保 ij在界限内)。 *dPtrs[i][j] 之类的东西是太多的取消引用。 double 不是地址,这样对待它只会导致问题。如果我对你的理解正确的话,这就是你的编译器尖叫的内容。