C / C++ 如何在没有嵌套循环的情况下复制多维字符数组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2225850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C / C++ How to copy a multidimensional char array without nested loops?
提问by dan
I'm looking for a smart way to copy a multidimensional char array to a new destination. I want to duplicate the char array because I want to edit the content without changing the source array.
我正在寻找一种将多维字符数组复制到新目的地的智能方法。我想复制 char 数组,因为我想在不更改源数组的情况下编辑内容。
I could build nested loops to copy every char by hand but I hope there is a better way.
我可以构建嵌套循环来手动复制每个字符,但我希望有更好的方法。
Update:
更新:
I don't have the size of the 2. level dimension. Given is only the length (rows).
我没有 2. 级别维度的大小。给定的只是长度(行)。
The code looks like this:
代码如下所示:
char **tmp;
char **realDest;
int length = someFunctionThatFillsTmp(&tmp);
//now I want to copy tmp to realDest
I'm looking for a method that copies all the memory of tmp into free memory and point realDest to it.
我正在寻找一种将 tmp 的所有内存复制到空闲内存并将 realDest 指向它的方法。
Update 2:
更新 2:
someFunctionThatFillsTmp() is the function credis_lrange() from the Redis C lib credis.c.
someFunctionThatFillsTmp() 是 Redis C 库credis.c 中的credis_lrange() 函数。
Inside the lib tmp is created with:
在 lib tmp 内部创建:
rhnd->reply.multibulk.bulks = malloc(sizeof(char *)*CR_MULTIBULK_SIZE)
Update 3:
更新 3:
I've tried to use memcpy with this lines:
我尝试使用 memcpy 与此行:
int cb = sizeof(char) * size * 8; //string inside 2. level has 8 chars
memcpy(realDest,tmp,cb);
cout << realDest[0] << endl;
prints: mystring
But I'm getting a: Program received signal: EXC_BAD_ACCESS
但我得到一个:程序收到信号:EXC_BAD_ACCESS
回答by Yacoby
You could use memcpy
.
你可以使用memcpy
.
If the multidimensional array size is given at compile time, i.e mytype myarray[1][2]
, then only a single memcpy call is needed
如果在编译时给出多维数组大小,即mytype myarray[1][2]
,则只需要一个 memcpy 调用
memcpy(dest, src, sizeof (mytype) * rows * columns);
If, like you indicated the array is dynamically allocated, you will need to know the size of both of the dimensions as when dynamically allocated, the memory used in the array won't be in a contiguous location, which means that memcpy will have to be used multiple times.
如果像您指出的数组是动态分配的那样,您将需要知道动态分配时两个维度的大小,数组中使用的内存将不会位于连续位置,这意味着 memcpy 将必须多次使用。
Given a 2d array, the method to copy it would be as follows:
给定一个二维数组,复制它的方法如下:
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);
}
Given that from your question it looks like you are dealing with an array of strings, you could use strlento find the length of the string (It must be null terminated).
鉴于您的问题看起来您正在处理一个字符串数组,您可以使用strlen来查找字符串的长度(它必须以空字符结尾)。
In which case the loop would become
在这种情况下,循环将变成
for ( int i = 0; i < length; ++i ){
int width = strlen(src[i]) + 1;
dest[i] = malloc(width);
memcpy(dest[i], src[i], width);
}
回答by Alok Singhal
When you have a pointer to a pointer in C, you have to know how the data is going to be used and laid out in the memory. Now, the first point is obvious, and true for any variable in general: if you don't know how some variable is going to be used in a program, why have it? :-). The second point is more interesting.
当你有一个指向 C 中的指针的指针时,你必须知道数据将如何使用和在内存中布局。现在,第一点是显而易见的,并且对于一般的任何变量都是正确的:如果您不知道某个变量将如何在程序中使用,为什么要使用它?:-)。第二点更有趣。
At the most basic level, a pointer to type T
points to oneobject of type T
. For example:
在最基本的级别,指针输入T
点到一个类型的对象T
。例如:
int i = 42;
int *pi = &i;
Now, pi
points to one int
. If you wish, you can make a pointer point to the first of many such objects:
现在,pi
指向一int
。如果你愿意,你可以让一个指针指向许多这样的对象中的第一个:
int arr[10];
int *pa = arr;
int *pb = malloc(10 * sizeof *pb);
pa
now points to the first of a sequence of 10 (contiguous) int
values, and assuming that malloc()
succeeds, pb
points to the first of another set of 10 (again, contiguous) int
s.
pa
现在指向 10 个(连续)int
值序列中的第一个,假设malloc()
成功,则pb
指向另一组 10 个(再次,连续)值中的int
第一个。
The same applies if you have a pointer to a pointer:
如果您有指向指针的指针,则同样适用:
int **ppa = malloc(10 * sizeof *ppa);
Assuming that malloc()
succeeds, now you have ppa
pointing to the first of a sequence of 10 contiguous int *
values.
假设malloc()
成功,现在您已ppa
指向 10 个连续int *
值序列中的第一个。
So, when you do:
所以,当你这样做时:
char **tmp = malloc(sizeof(char *)*CR_MULTIBULK_SIZE);
tmp
points to the first char *
object in a sequence of CR_MULTIBULK_SIZE
such objects. Each of the pointers above is not initialized, so tmp[0]
to tmp[CR_MULTIBULK_SIZE-1]
all contain garbage. One way to initialize them would be to malloc()
them:
tmp
指向此类对象char *
序列中的第一个CR_MULTIBULK_SIZE
对象。每个上面的指针没有初始化,所以tmp[0]
对tmp[CR_MULTIBULK_SIZE-1]
所有包含垃圾。初始化它们的一种方法是对malloc()
它们:
size_t i;
for (i=0; i < CR_MULTIBULK_SIZE; ++i)
tmp[i] = malloc(...);
The ...
above is the size of the i
th data we want. It could be a constant, or it could be a variable, depending upon i
, or the phase of the moon, or a random number, or anything else. The main point to note is that you have CR_MULTIBULK_SIZE
calls to malloc()
in the loop, and that while each malloc()
is going to return you a contiguous block of memory, the contiguity is not guaranteed across malloc()
calls. In other words, the second malloc()
call is not guaranteed to return a pointer that starts right where the previous malloc()
's data ended.
在...
上面的是规模i
,我们希望日的数据。它可能是一个常数,也可能是一个变量,取决于i
、月相、随机数或其他任何东西。需要注意的要点是,您在循环中有对 的CR_MULTIBULK_SIZE
调用malloc()
,虽然每个调用malloc()
都将返回一个连续的内存块,但不能保证跨malloc()
调用的连续性。换句话说,第二次malloc()
调用不能保证返回一个指针,该指针从前malloc()
一个数据结束的地方开始。
To make things more concrete, let's assume CR_MULTIBULK_SIZE
is 3. In pictures, your data might look like this:
为了让事情更具体,让我们假设CR_MULTIBULK_SIZE
是 3。 在图片中,您的数据可能如下所示:
+------+ +---+---+
tmp: | |--------+ +----->| a | 0 |
+------+ | | +---+---+
| |
| |
| +------+------+------+
+-------->| 0 | 1 | 2 |
+------+------+------+
| |
| | +---+---+---+---+---+
| +--->| t | e | s | t | 0 |
+------+ +---+---+---+---+---+
|
|
| +---+---+---+
+--->| h | i | 0 |
+---+---+---+
tmp
points to a contiguous block of 3 char *
values. The first of the pointers, tmp[0]
, points to a contiguous block of 3 char
values. Similarly, tmp[1]
and tmp[2]
point to 5 and 2 char
s respectively. But the memory pointed to by tmp[0]
to tmp[2]
is not contiguous as a whole.
tmp
指向一个连续的 3 个char *
值块。第一个指针 指向tmp[0]
一个连续的 3 个char
值块。类似地, tmp[1]
和tmp[2]
指向5和2char
分别秒。但内存指向tmp[0]
到tmp[2]
是不连续的整体。
Since memcpy()
copies contiguous memory, what you want to do can't be done by one memcpy()
. Further, you need to know how each tmp[i]
was allocated. So, in general, what you want to do needs a loop:
由于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);
}
As above, you can call memcpy()
inside the loop, so you don't need nested loop in your code. (Most likely memcpy()
is implemented with a loop, so the effect is as if you had nested loops.)
如上所述,您可以memcpy()
在循环内部调用,因此您的代码中不需要嵌套循环。(很可能memcpy()
是用循环实现的,所以效果就像嵌套循环一样。)
Now, if you had code like:
现在,如果你有这样的代码:
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;
I.e., you allocated contiguous space for all the pointers in one malloc()
call, then you can copy all the data without a loop in your code:
即,您在一次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;
From the above, the simple answer is, if you had more than one malloc()
to allocate memory for tmp[i]
, then you will need a loop to copy all the data.
从上面,简单的答案是,如果您有多个malloc()
内存分配给tmp[i]
,那么您将需要一个循环来复制所有数据。
回答by John Knoeller
You can just calculate the overall size of the array and then use memcpyto copy it.
您可以只计算数组的整体大小,然后使用memcpy复制它。
int cb = sizeof(char) * rows * columns;
memcpy (toArray, fromArray, cb);
Edit: new information in the question indicates that the number of rows and cols of the array is not known, and that the array may be ragged, so memcpy may not be a solution.
编辑:问题中的新信息表明数组的行数和列数未知,并且数组可能参差不齐,因此 memcpy 可能不是解决方案。
回答by dmckee --- ex-moderator kitten
Lets explore some possibilities for what's going on here:
让我们探索这里发生的事情的一些可能性:
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 (i.e. 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 (i.e. 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;
}
The moral of the story is: you must to know what is on the other side of your pointers. Or else.
这个故事的寓意是:你必须知道指针的另一边是什么。要不然。
The secondmoral of the story is: just because you can access a double pointer using the [][]
notation does not make it is the same as two-dimensional array. Really.
这个故事的第二个寓意是:仅仅因为您可以使用[][]
符号访问双指针并不能使它与二维数组相同。真的。
Let me clarify the second moral a little bit.
让我稍微澄清一下第二个道德。
An array(be it one dimensional, two dimensional, whatever) is an allocated piece of memory, and the compiler knows how big it is (but never does any range checking for you), and a what address it starts. You declare arrays with
一个数组(无论是一维、二维还是其他)是一块分配的内存,编译器知道它有多大(但从不为你做任何范围检查),以及它的起始地址。你声明数组
char string1[32];
unsigned int histo2[10][20];
and similar things;
和类似的东西;
A pointeris a variable that can hold a memory address. You declare pointers with
甲指针是能够保存的存储器地址的变量。你声明指针
char *sting_ptr1;
double *matrix_ptr = NULL;
They are two different things.
他们是两个不同的东西。
But:
但:
- If you use the
[]
syntax with a pointer, the compiler will do pointer arithmetic for you. - In almost any place you use an array without dereferencing it, the compiler treats it as a pointer to the arrays start location.
- 如果您将
[]
语法与指针一起使用,编译器将为您进行指针运算。 - 在几乎任何使用数组而不取消引用的地方,编译器都将其视为指向数组起始位置的指针。
So, I can do
所以,我可以做到
strcpy(string1,"dmckee");
because rule 2 says that string1 (an array) is treated as a char*
). Likewise, I can fllow that with:
因为规则 2 说 string1(一个数组)被视为 a char*
)。同样,我可以使用:
char *string_ptr2 = string1;
Finally,
最后,
if (string_ptr[3] == 'k') {
prinf("OK\n");
}
will print "OK" because of rule 1.
由于规则 1,将打印“OK”。
回答by Mateen Ulhaq
Why are you not using C++?
你为什么不使用C++?
class C
{
std::vector<std::string> 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);
}
Or something similar? Also, do you needto use C-strings? I doubt it.
或者类似的东西?另外,你需要使用C字符串吗?我对此表示怀疑。
回答by matt
As others suggested, it looks like this is an array of pointers rather than a multi demetional array.
正如其他人所建议的那样,这看起来像是一个指针数组,而不是一个多维度数组。
so instead of it being
所以而不是它
char mdArray[10][10];
字符 mdArray[10][10];
it is:
这是:
char* pArray[10];
字符* pArray[10];
if that is the case the only thing you can do is loop through with the one length value you get, if there are meant to be strings (which it looks like it is) then use strlen in which case it would be:
如果是这种情况,您唯一可以做的就是循环使用您获得的一个长度值,如果有字符串(看起来是这样),则使用 strlen 在这种情况下:
char **tmp;
int length = getlengthfromwhereever;
char** copy = new char*[length];
for(int i=0; i<length; i++)
{
int slen = strlen(tmp[i]);
copy[i] = new char[slen+1]; //+1 for null terminator
memcpy(copy[i],tmp[i],slen);
copy[i][slen] = 0; // you could just copy slen+1 to copy the null terminator, but there might not be one...
}
回答by asveikau
Note that in the following example:
请注意,在以下示例中:
char **a;
a[i]
is char*
. So if you do a memcpy()
of a
, you're doing a shallow copy of that pointer.
a[i]
是char*
。所以如果你做 a memcpy()
of a
,你就是在做那个指针的浅拷贝。
I would ditch the multi-dimensional aspect and go with a flat buffer of size nn
. You can simulate A[i][j]
with A[i + j
width]
. Then you can memcpy(newBuffer, oldBuffer, width * height * sizeof(*NewBuffer))
.
我会放弃多维方面并使用 size 的平面缓冲区。你可以用. 那么你可以。nn
A[i][j]
A[i + j
width]
memcpy(newBuffer, oldBuffer, width * height * sizeof(*NewBuffer))