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

问题描述 投票:22回答:7

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

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

更新:

我没有2.水平尺寸的尺寸。仅给出长度(行)。

代码看起来像这样:

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

c++ c arrays multidimensional-array char
7个回答
33
投票

您可以使用memcpy

如果多维数组的大小是在编译时给出的,即memcpy,则只需要一个memcpy调用

mytype myarray[1][2]

如果您像指示数组是动态分配的那样,则需要知道两个维度的大小,因为动态分配时,数组中使用的内存不会位于连续的位置,这意味着memcpy将必须多次使用。

给出一个二维数组,复制它的方法如下:

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

鉴于您的问题似乎是您正在处理字符串数组,因此可以使用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); } 查找字符串的长度(必须为null终止)。

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

strlen

8
投票

当您有一个指向C中指针的指针时,您必须知道如何使用数据并将其布置在内存中。现在,第一点很明显,并且通常对于任何变量都适用:如果您不知道某个变量在程序中将如何使用,为什么要使用它呢? :-)。第二点更有趣。

在最基本的级别上,指向类型为for ( int i = 0; i < length; ++i ){ int width = strlen(src[i]) + 1; dest[i] = malloc(width); memcpy(dest[i], src[i], width); } 的指针指向类型为Tone对象。例如:

T

现在,int i = 42; int *pi = &i; 指向一个pi。如果愿意,可以将指针指向许多此类对象中的第一个:

int

int arr[10]; int *pa = arr; int *pb = malloc(10 * sizeof *pb); 现在指向10个(连续)pa值的序列中的第一个,并且假设int成功,malloc()指向另一个10个(再次,连续)pb的集合中的第一个] s。

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

int

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

所以,当您这样做:

int *

char **tmp = malloc(sizeof(char *)*CR_MULTIBULK_SIZE); 指向tmp这样的对象序列中的第一个char *对象。上面的每个指针均未初始化,因此CR_MULTIBULK_SIZEtmp[0]都包含垃圾。初始化它们的一种方法是将它们tmp[CR_MULTIBULK_SIZE-1]

malloc()

上面的size_t i; for (i=0; i < CR_MULTIBULK_SIZE; ++i) tmp[i] = malloc(...); 是我们想要的第...个数据的大小。它可以是常数,也可以是变量,取决于i,月球的相位或随机数或其他任何东西。要注意的主要点是,您在循环中有对iCR_MULTIBULK_SIZE调用,并且尽管每个malloc()将返回给您连续的内存块,但无法保证malloc()调用之间的连续性。换句话说,不能保证第二个malloc()调用返回一个从上一个malloc()的数据结束处开始的指针。

为了使事情更具体,让我们假设malloc()为3。在图片中,您的数据可能看起来像这样:

CR_MULTIBULK_SIZE

+------+ +---+---+ tmp: | |--------+ +----->| a | 0 | +------+ | | +---+---+ | | | | | +------+------+------+ +-------->| 0 | 1 | 2 | +------+------+------+ | | | | +---+---+---+---+---+ | +--->| t | e | s | t | 0 | +------+ +---+---+---+---+---+ | | | +---+---+---+ +--->| h | i | 0 | +---+---+---+ 指向3个tmp值的连续块。指针的第一个指针char *指向3个tmp[0]值的连续块。同样,chartmp[1]分别指向5和2tmp[2]s。但是chartmp[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()分配内存,那么您将需要一个循环来复制所有数据。


6
投票

您可以只计算数组的整体大小,然后使用tmp[i]复制它。

memcpy

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


1
投票

让我们探索这里发生的情况的可能性:

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

故事的寓意是:您必须知道指针另一侧的内容。否则。

该故事的second的寓意是:仅因为您可以使用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; } 表示法访问一个双指针,并不使它与二维数组相同。真的。


让我稍微澄清一下第二种道德。

一个数组(无论是一维,二维,无论是什么)都是分配的内存,编译器知道内存有多大(但不会为您进行任何范围检查),以及如何寻址开始。您可以使用

声明数组
[][]

和类似的事物;

A pointer是可以保存内存地址的变量。您可以使用

声明指针
char string1[32];
unsigned int histo2[10][20];

它们是两种不同的东西。

但是:

  1. 如果您将char *sting_ptr1; double *matrix_ptr = NULL; 语法与指针一起使用,则编译器将为您执行指针算术。
  2. 在几乎不使用引用而使用数组的几乎任何地方,编译器都将其视为指向数组起始位置的指针。

所以,我可以做

[]

因为规则2认为string1(数组)被视为 strcpy(string1,"dmckee"); )。同样,我可以使用:

char*

最后,

    char *string_ptr2 = string1;

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


1
投票

您为什么不使用C ++?

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

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


0
投票

请注意以下示例:

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

char **a; a[i]。因此,如果您将char*设为memcpy(),则表示该指针的浅表副本。

我将放弃多维方面,并使用大小为a的平面缓冲区。您可以用nn模拟A[i][j]。然后您可以A[i + jwidth]


0
投票

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

所以而不是它

char mdArray [10] [10];

它是:

char * pArray [10];

如果是这种情况,您唯一可以做的就是用您获得的一个长度值循环遍历,如果要使用字符串(看起来像这样),则在这种情况下使用strlen:] >

memcpy(newBuffer, oldBuffer, width * height * sizeof(*NewBuffer))
© www.soinside.com 2019 - 2024. All rights reserved.