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;
他们是两个不同的东西。
但:
- 如果将
[]
语法与指针一起使用,编译器将为您执行指针运算。 - 在几乎任何地方使用数组而不解除引用它,编译器会将其视为指向数组起始位置的指针。
所以,我能做到
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