C / C ++如何在没有嵌套循环的情况下复制多维char数组?

我正在寻找一种将多维char数组复制到新目标的智能方法。 我想复制char数组,因为我想编辑内容而不更改源数组。

我可以构建嵌套循环来手动复制每个字符,但我希望有更好的方法。

更新:

我没有2. level维度的大小。 给定的只是长度(行)。

代码如下所示:

char **tmp; char **realDest; int length = someFunctionThatFillsTmp(&tmp); //now I want to copy tmp to realDest 

我正在寻找一种方法,将tmp的所有内存复制到空闲内存中,并将realDest指向它。

更新2:

someFunctionThatFillsTmp()是Redis C lib credis.c中的函数credis_lrange()。

在lib tmp中创建了:

 rhnd->reply.multibulk.bulks = malloc(sizeof(char *)*CR_MULTIBULK_SIZE) 

更新3:

我试过用这行代码使用memcpy:

 int cb = sizeof(char) * size * 8; //string inside 2. level has 8 chars memcpy(realDest,tmp,cb); cout << realDest[0] << endl; prints: mystring 

但是我得到了一个: 程序接收信号:EXC_BAD_ACCESS

你可以使用memcpy

如果在编译时给出多维数组大小,即mytype myarray[1][2] ,那么只需要一个memcpy调用

 memcpy(dest, src, sizeof (mytype) * rows * coloumns); 

如果像你指示的那样动态分配数组,你将需要知道两个维度的大小,就像动态分配时一样,数组中使用的内存不会在连续的位置,这意味着memcpy将不得不多次使用。

给定一个2d数组,复制它的方法如下:

 char** src; char** dest; int length = someFunctionThatFillsTmp(src); dest = malloc(length*sizeof(char*)); for ( int i = 0; i < length; ++i ){ //width must be known (see below) dest[i] = malloc(width); memcpy(dest[i], src[i], width); } 

鉴于你的问题看起来你正在处理一个字符串数组,你可以使用strlen来查找字符串的长度(它必须以null结尾)。

在这种情况下,循环将成为

 for ( int i = 0; i < length; ++i ){ int width = strlen(src[i]) + 1; dest[i] = malloc(width); memcpy(dest[i], src[i], width); } 

当你有一个指向C中指针的指针时,你必须知道数据将如何在内存中使用和布局。 现在,第一点是显而易见的,对于任何变量都是如此:如果你不知道某个变量将如何在程序中使用,那为什么呢? :-)。 第二点更有趣。

在最基本的级别,指向类型T的指针指向一个类型为T对象。 例如:

 int i = 42; int *pi = &i; 

现在, pi指向一个int 。 如果您愿意,可以使指针指向许多此类对象中的第一个:

 int arr[10]; int *pa = arr; int *pb = malloc(10 * sizeof *pb); 

pa现在指向10个(连续的) int值序列中的第一个,并假设malloc()成功, pb指向另一个10(同样,连续) int第一个。

如果您有指向指针的指针,则同样适用:

 int **ppa = malloc(10 * sizeof *ppa); 

假设malloc()成功,现在你有ppa指向10个连续int *值的第一个序列。

所以,当你这样做时:

 char **tmp = malloc(sizeof(char *)*CR_MULTIBULK_SIZE); 

tmp指向CR_MULTIBULK_SIZE这类对象序列中的第一个char *对象。 上面的每个指针都没有初始化,所以tmp[0]tmp[CR_MULTIBULK_SIZE-1]都包含垃圾。 初始化它们的一种方法是malloc()它们:

 size_t i; for (i=0; i < CR_MULTIBULK_SIZE; ++i) tmp[i] = malloc(...); 

上面的...是我们想要的第i个数据的大小。 它可以是常数,也可以是变量,取决于i ,月亮的相位,或随机数,或其他任何东西。 要注意的要点是你在循环中对malloc()进行了CR_MULTIBULK_SIZE调用,并且当每个malloc()要返回一个连续的内存块时,不能保证malloc()调用的连续性。 换句话说,第二个malloc()调用不能保证返回一个指针,该指针从前一个malloc()数据结束的地方开始。

为了使事情更具体,让我们假设CR_MULTIBULK_SIZE为3.在图片中,您的数据可能如下所示:

  +------+ +---+---+ tmp: | |--------+ +----->| a | 0 | +------+ | | +---+---+ | | | | | +------+------+------+ +-------->| 0 | 1 | 2 | +------+------+------+ | | | | +---+---+---+---+---+ | +--->| t | e | s | t | 0 | +------+ +---+---+---+---+---+ | | | +---+---+---+ +--->| h | i | 0 | +---+---+---+ 

tmp指向3个char *值的连续块。 第一个指针tmp[0]指向一个由3个char值组成的连续块。 类似地, tmp[1]tmp[2]指向5和2个char 。 但是tmp[0]指向tmp[2]的内存并不是一个整体连续的。

由于memcpy()复制了连续的内存,因此一个memcpy()无法完成您想要做的事情。 此外,您需要知道每个tmp[i]分配方式。 所以,一般来说,你想做的事情需要一个循环:

 char **realDest = malloc(CR_MULTIBULK_SIZE * sizeof *realDest); /* assume malloc succeeded */ size_t i; for (i=0; i < CR_MULTIBULK_SIZE; ++i) { realDest[i] = malloc(size * sizeof *realDest[i]); /* again, no error checking */ memcpy(realDest[i], tmp[i], size); } 

如上所述,您可以在循环中调用memcpy() ,因此您不需要在代码中使用嵌套循环。 (很可能memcpy()是通过循环实现的,因此效果就好像你有嵌套循环一样。)

现在,如果你有像这样的代码:

 char *s = malloc(size * CR_MULTIBULK_SIZE * sizeof *s); size_t i; for (i=0; i < CR_MULTIBULK_SIZE; ++i) tmp[i] = s + i*CR_MULTIBULK_SIZE; 

即,您在一个malloc()调用中为所有指针分配了连续空间,然后您可以在代码中没有循环的情况下复制所有数据:

 size_t i; char **realDest = malloc(CR_MULTIBULK_SIZE * sizeof *realDest); *realDest = malloc(size * CR_MULTIBULK_SIZE * sizeof **realDest); memcpy(*realDest, tmp[0], size*CR_MULTIBULK_SIZE); /* Now set realDest[1]...realDest[CR_MULTIBULK_SIZE-1] to "proper" values */ for (i=1; i < CR_MULTIBULK_SIZE; ++i) realDest[i] = realDest[0] + i * CR_MULTIBULK_SIZE; 

从上面可以看出,简单的答案是,如果你有多个malloc()tmp[i]分配内存,那么你需要一个循环来复制所有数据。

您只需计算数组的整体大小,然后使用memcpy复制它。

 int cb = sizeof(char) * rows * columns; memcpy (toArray, fromArray, cb); 

编辑:问题中的新信息表明数组的行数和列数未知,并且数组可能是不规则的,因此memcpy可能不是解决方案。

让我们探讨一下这里发生的事情的可能性:

 int main(int argc; char **argv){ char **tmp1; // Could point any where char **tmp2 = NULL; char **tmp3 = NULL; char **tmp4 = NULL; char **tmp5 = NULL; char **realDest; int size = SIZE_MACRO; // Well, you never said int cb = sizeof(char) * size * 8; //string inside 2. level has 8 chars /* Case 1: did nothing with tmp */ memcpy(realDest,tmp,cb); // copies 8*size bytes from WHEREEVER tmp happens to be // pointing. This is undefined behavior and might crash. printf("%p\n",tmp[0]); // Accesses WHEREEVER tmp points+1, undefined behavior, // might crash. printf("%c\n",tmp[0][0]); // Accesses WHEREEVER tmp points, undefined behavior, // might crash. IF it hasn't crashed yet, derefernces THAT // memory location, ALSO undefined behavior and // might crash /* Case 2: NULL pointer */ memcpy(realDest,tmp2,cb); // Dereferences a NULL pointer. Crashes with SIGSEGV printf("%p\n",tmp2[0]); // Dereferences a NULL pointer. Crashes with SIGSEGV printf("%c\n",tmp2[0][0]); // Dereferences a NULL pointer. Crashes with SIGSEGV /* Case 3: Small allocation at the other end */ tmp3 = calloc(sizeof(char*),1); // Allocates space for ONE char*'s // (4 bytes on most 32 bit machines), and // initializes it to 0 (NULL on most machines) memcpy(realDest,tmp3,cb); // Accesses at least 8 bytes of the 4 byte block: // undefined behavior, might crash printf("%p\n",tmp3[0]); // FINALLY one that works. // Prints a representation of a 0 pointer printf("%c\n",tmp3[0][0]); // Derefereces a 0 (ie NULL) pointer. // Crashed with SIGSEGV /* Case 4: Adequate allocation at the other end */ tmp4 = calloc(sizeof(char*),32); // Allocates space for 32 char*'s // (4*32 bytes on most 32 bit machines), and // initializes it to 0 (NULL on most machines) memcpy(realDest,tmp4,cb); // Accesses at least 8 bytes of large block. Works. printf("%p\n",tmp3[0]); // Works again. // Prints a representation of a 0 pointer printf("%c\n",tmp3[0][0]); // Derefereces a 0 (ie NULL) pointer. // Crashed with SIGSEGV /* Case 5: Full ragged array */ tmp5 = calloc(sizeof(char*),8); // Allocates space for 8 char*'s for (int i=0; i<8; ++i){ tmp5[i] = calloc(sizeof(char),2*i); // Allocates space for 2i characters tmp5[i][0] = '0' + i; // Assigns the first character a digit for ID } // At this point we have finally allocated 8 strings of sizes ranging // from 2 to 16 characters. memcpy(realDest,tmp5,cb); // Accesses at least 8 bytes of large block. Works. // BUT what works means is that 2*size elements of // realDist now contain pointer to the character // arrays allocated in the for block above/ // // There are still only 8 strings allocated printf("%p\n",tmp5[0]); // Works again. // Prints a representation of a non-zero pointer printf("%c\n",tmp5[0][0]); // This is the first time this has worked. Prints "0\n" tmp5[0][0] = '*'; printf("%c\n",realDest[0][0]); // Prints "*\n", because realDest[0] == tmp5[0], // So the change to tmp5[0][0] affects realDest[0][0] return 0; } 

这个故事的寓意是: 你必须知道指针的另一面是什么。 要不然。

这个故事的第二个 道理是: 只是因为你可以使用[][]符号来访问双指针,而不是使它与二维数组相同。 真。


让我稍微澄清第二道德。

一个数组 (无论是一维,二维,无论什么)都是一块已分配的内存,编译器知道它有多大(但从不对你进行任何范围检查),以及它启动的地址。 你声明数组

 char string1[32]; unsigned int histo2[10][20]; 

和类似的事情;

指针是一个可以保存内存地址的变量。 你用它声明指针

 char *sting_ptr1; double *matrix_ptr = NULL; 

他们是两个不同的东西。

但:

  1. 如果将[]语法与指针一起使用,编译器将为您执行指针运算。
  2. 在几乎任何地方使用数组而不解除引用它,编译器会将其视为指向数组起始位置的指针。

所以,我能做到

  strcpy(string1,"dmckee"); 

因为规则2说string1(数组)被视为char* )。 同样,我可以说:

  char *string_ptr2 = string1; 

最后,

  if (string_ptr[3] == 'k') { prinf("OK\n"); } 

由于规则1,将打印“OK”。

你为什么不使用C ++?

 class C { std::vector data; public: char** cpy(); }; char** C::cpy() { std::string *psz = new std::string [data.size()]; copy(data.begin(), data.end(), psz); char **ppsz = new char* [data.size()]; for(size_t i = 0; i < data.size(); ++i) { ppsz[i] = new char [psz[i].length() + 1]; ppsz[i] = psz[i].c_str(); } delete [] psz; return(ppsz); } 

或类似的东西? 另外,你需要使用C字符串吗? 我对此表示怀疑。

请注意,在以下示例中:

 char **a; 

a[i]char* 。 因此,如果你执行a的memcpy() ,那么你正在做一个指针的浅表副本。

我会抛弃多维方面,并使用大小为n n的扁平缓冲区。 您可以使用A[i + j width]模拟A[i][j] A[i + j width] 。 然后你可以memcpy(newBuffer, oldBuffer, width * height * sizeof(*NewBuffer))

正如其他人所说,看起来这是一个指针数组而不是多个数组。

而不是它

char mdArray [10] [10];

它是:

char * pArray [10];

如果是这种情况,你可以做的唯一事情是循环使用你获得的一个长度值,如果有意图是字符串(它看起来像是)然后使用strlen,在这种情况下它将是:

 char **tmp; int length = getlengthfromwhereever; char** copy = new char*[length]; for(int i=0; i