memcpy()用于3D数组的可变尺寸

时间:2019-01-04 14:24:14

标签: c

我有一个函数concat来连接矩阵mat1以形成mat2。这里mat1mat2使用固定尺寸(即DIM1 = 2,DIM2 = 2,DIM3 = 3,DIM1_1 = 4),但实际上我需要它们具有可变尺寸,因此我声明了尺寸3d数组的最大大小,如下面我的代码所述(即DIM2 = 20,DIM3 = 30),但是当我这样做时,程序会给出垃圾值。有人可以建议不使用malloc()如何使用可变大小的3d数组吗?

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

//!< Dimensions of a 3d array
#define DIM1     2
#define DIM2     2
#define DIM3     3
#define DIM1_1   4


void Concat(int (*mat1)[DIM2][DIM3], int (*mat2)[DIM2][DIM3], int len);
// void Concat(int (*mat1)[20][30], int (*mat2)[20][30], int len);

int main()
{
    int mat1[DIM1][DIM2][DIM3] = {{{1,2,3},{4,5,6}},{{1,2,3},{4,5,6}}};
    int mat2[DIM1_1][DIM2][DIM3];

    Concat(mat1,mat2,2);

    return 0;
}
// void Concat(int (*mat1)[20][30], int (*mat2)[20][30], int len){
void Concat(int (*mat1)[DIM2][DIM3], int (*mat2)[DIM2][DIM3], int len){  
    /*concatenate mat1 to mat2 */
    memcpy(mat2[0], mat1, DIM1*DIM2*DIM3 * sizeof mat1);
    memcpy(mat2[0+len], mat1, DIM1*DIM2*DIM3 * sizeof mat1);
}

2 个答案:

答案 0 :(得分:2)

很抱歉,但是无法完成(事先不知道最大长度)。当您的函数生成mat1时,它将在数组的堆栈上精确地保留2*2*3 int s的内存,并将每个子数组紧挨着。如果是静态分配的,则无法在完成后对其进行更改。

但是 ...通过动态内存分配(即malloc和朋友)可以做到这一点。具体来说,使用realloc分配额外的空间。

对不起。

答案 1 :(得分:2)

  

我在下面的代码中声明将3d数组的大小声明为最大大小(即DIM2 = 20,DIM3 = 30),但是程序执行此操作时会给出垃圾值。

我希望在运行时获取垃圾值之前,编译器警告您有关以这种方式创建的类型不匹配的信息。如果没有,那么通过弄清楚如何提高警告级别或选择一个更有用的编译器,将对您很有帮助。

无论哪种方式,Concat()函数的参数都是指向特定尺寸的2D数组的指针。如果实际参数是指向其他内容(甚至是维数不同的数组)的指针,那么结果差就不足为奇了。实际上,这违反了所谓的“严格别名规则”,因此行为是不确定的。

  

有人可以建议不使用malloc()如何使用可变大小的3d数组吗?

我的解释是,您想要一个单一的函数可用于各种尺寸的数组,而不是任何单个数组的尺寸都会随时间变化。您可以做几件事,但是这里有一些不错的选择:

  • 如果您的实现支持VLA (在C99中是必需的,但自C11起是可选的),则可以使用它们来支持Concat()功能。这种东西是我最喜欢的VLA用法之一,因为它避免了对VLA使用的最大实际关注,它围绕着自动分配可用的空间。这样的方法可能看起来像这样:

    void Concat(size_t dim1, size_t dim2, size_t dim3 int (*source)[dim2][dim3],
            int (*dest)[dim2][dim3]) {
        size_t source_size = dim1 * sizeof(*source);
    
        memcpy(dest, source, source_size);
        memcpy(dest + dim1, source, source_size);
    }
    

    您会注意到,这要求您将源数组的尺寸传递给 all 作为单独的参数,并且除两个数组的第一个匹配之外的所有尺寸。您将非常简单地使用它:

    int mat1[DIM1][DIM2][DIM3] = /* ... */
    int mat2[DIM1 * 2][DIM2][DIM3];
    
    Concat(DIM1, DIM2, DIM3, mat1, mat2);
    

    当然,您可以在同一程序中使用相同的函数来连接各种维数的数组对,前提是已满足已描述的约束。

  • 如果您不能或不想依靠VLA ,那么最好的选择就是简单地使上述内容适应这种情况,就像这样:

    void Concat(size_t dim1, size_t dim2, size_t dim3, void *source, void *dest) {
        size_t source_size = dim1 * dim2 * dim3 * sizeof(int);
    
        memcpy((char *) dest, source, source_size);
        memcpy((char *) dest + source_size, source, source_size);
    }
    

    您可以使用与上一个相同的方式来调用该函数。

  • 如果您想变得更通用,同时提供更简单的函数签名,则可以将以上内容转换为连接任意对象:

    void Concat(size_t source_size, void *source, void *dest) {
        memcpy((char *) dest, source, source_size);
        memcpy((char *) dest + source_size, source, source_size);
    }
    

    这通过将调用者的职责放在计算上并传递源对象的大小(以字节为单位)来实现完全的通用性,尽管这不一定是一个大问题。例如,

    int mat1[DIM1][DIM2][DIM3] = /* ... */
    int mat2[DIM1 * 2][DIM2][DIM3];
    
    Concat(sizeof(mat1), mat1, mat2);