2010-02-09 46 views
19

我正在尋找一種聰明的方式將多維字符數組複製到新的目的地。我想複製char數組,因爲我想在不更改源數組的情況下編輯內容。C/C++如何複製沒有嵌套循環的多維char數組?

我可以構建嵌套循環來手工複製每個字符,但我希望有更好的方法。

更新:

我沒有2級尺寸的大小。給定的只是長度(行)。

的代碼看起來是這樣的:

char **tmp; 
char **realDest; 

int length = someFunctionThatFillsTmp(&tmp); 

//now I want to copy tmp to realDest 

我正在尋找一個方法拷貝TMP的所有內存爲可用內存,並指向realDest它。

更新2:

someFunctionThatFillsTmp()是函數credis_lrange()從Redis的ÇLIB credis.c

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 

但我

裏面的lib TMP與創建得到:編程接收信號:EXC_BAD_ACCESS

+4

這完全取決於你的「多維數組」是如何構建的。顯示創建它的代碼。 – caf 2010-02-09 00:17:52

+2

如果你沒有數組維度,那麼你也不能用循環複製它。 – 2010-02-09 00:30:29

+0

@John Knoeller:謝謝。我已經更新了描述。 – dan 2010-02-09 00:33:26

回答

30

您可以使用memcpy

如果多維數組的大小在編譯時給定的,即mytype myarray[1][2],那麼就只需要一個單一的memcpy調用

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

如果像您指定的數組是動態分配的,你需要知道的這兩個維度的大小與動態分配時相同,陣列中使用的內存不會位於連續位置,這意味着memcpy必須多次使用。

給出一個二維數組,複製這將是如下的方法:

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

我第二種方法...... – 2010-02-09 00:12:32

+0

盡一切手段,使用'memcpy',但問題是一個真正的多維數組或多次爲一個衣衫襤褸的數組(這是由OP使用雙重間接.. 。)? – dmckee 2010-02-09 00:39:14

+0

@dmckee我的原始答案是爲原始問題編寫的,而不是更新後的問題。我的答案現在有望更適合更新的問題。 – Yacoby 2010-02-09 08:35:41

6

你可以只計算陣列的整體尺寸,然後用memcpy複製。

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

編輯:在問題新信息指示行和所述陣列的COLS的數量是未知的,並且該陣列可以是粗糙的,所以memcpy的可能不是一個解決方案。

+1

sizeof(char)== 1字節by定義(1字節是否爲8位是一個完全不同的問題......) – Jon 2010-02-09 00:23:50

+1

@Jon:是的,但它是無害的,它有助於清楚說明這是一個字節數而不是元素數 - 而且將需要更新如果數組是寬字符。 – 2010-02-09 00:29:40

1

讓我們探討怎麼在這裏對一些可能性:

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

這個故事的寓意是:你必須知道什麼是對你的指針的另一邊。要不然。

這個故事的寓意是:只是因爲你可以使用[][]符號不讓它是一樣的二維數組訪問雙指針。真。


讓我澄清第二個道德一點。

數組(無論它是一維,二維,無論)是分配的一塊內存,編譯器知道它有多大(但從來沒有做任何範圍檢查你),以及它的一個地址開始。您聲明陣列與

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

和類似的東西;

A 指針是一個可以保存內存地址的變量。你聲明與指針

char *sting_ptr1; 
double *matrix_ptr = NULL; 

他們是兩個不同的東西。

但是:

  1. 如果使用[]語法的指針,編譯器會做指針算法爲您服務。
  2. 在幾乎任何你使用數組而不取消引用的地方,編譯器將它視爲指向數組開始位置的指針。

所以,我能做的

strcpy(string1,"dmckee"); 

,因爲第2條說,字符串1(數組)作爲char*進行處理)。同樣,我可以fllow與:

char *string_ptr2 = string1; 

最後,

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

將打印 「OK」,因爲規則1.

0

注意,在下面的例子:

char **a; 

a[i]char*。所以如果你做memcpy()a,你正在做一個該指針的淺表副本。

我會溝多維方面,並與一個大小爲nn大小的平緩衝區。您可以使用A[i + jwidth]來模擬A[i][j]。那麼你可以memcpy(newBuffer, oldBuffer, width * height * sizeof(*NewBuffer))

7

當你有一個指針指向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 s。

這同樣適用,如果你有一個指針的指針:

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(),並且雖然每個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 s。但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()爲分配內存3210,那麼你將需要一個循環來複制所有的數據。

0

正如其他人所建議的,它看起來像這是一個指針數組而不是多個demetional數組。

因此而不是它

char mdArray [10] [10];

,它是:

字符*粒子陣列[10];

如果是這種情況,你可以做的唯一事情就是用你得到的一個長度值進行循環,如果有意圖是字符串(它看起來是這樣),那麼使用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... 
} 
1

爲什麼你不使用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); 
} 

或者類似的東西?另外,你需要使用C字符串?我對此表示懷疑。