2010-02-09 24 views
19

Sto cercando un modo intelligente per copiare un array di char multidimensionale in una nuova destinazione. Voglio duplicare il char array perché voglio modificare il contenuto senza modificare l'array sorgente.C/C++ Come copiare un array di char multidimensionale senza loop nidificati?

Potrei costruire cicli annidati per copiare ogni carattere a mano ma spero che ci sia un modo migliore.

Aggiornamento:

non ho la dimensione della dimensione 2. Livello. Dato è solo la lunghezza (righe).

Il codice simile a questo:

char **tmp; 
char **realDest; 

int length = someFunctionThatFillsTmp(&tmp); 

//now I want to copy tmp to realDest 

Sto cercando un metodo che copia tutta la memoria di tmp in memoria libera e punto realDest ad esso.

Aggiornamento 2:

someFunctionThatFillsTmp() è la funzione credis_lrange() dal Redis C lib credis.c.

All'interno del tmp lib viene creato con:

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

Update 3:

Ho cercato di usare memcpy con queste righe:

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

prints: mystring 

Ma sono ottenendo un: Segnale ricevuto programma: EXC_BAD_ACCESS

+4

Dipende interamente da come viene costruito il "vostro array multidimensionale". Mostra il codice che lo crea. – caf

+2

se non si dispone delle dimensioni dell'array, non è possibile copiarlo neanche con un loop. –

+0

@ John Knoeller: Grazie. Ho aggiornato la descrizione. – dan

risposta

30

È possibile utilizzare memcpy.

Se la dimensione della matrice multidimensionale viene dato al momento della compilazione, cioè mytype myarray[1][2], allora solo una singola chiamata memcpy è necessario

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

Se, come avete indicato l'array viene assegnato dinamicamente, è necessario conoscere il dimensione di entrambe le dimensioni come quando allocate dinamicamente, la memoria utilizzata nell'array non si troverà in una posizione contigua, il che significa che la memcpy dovrà essere utilizzata più volte.

Dato un array 2D, il metodo per copiare sarebbe la seguente:

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

Dato che dalla tua domanda sembra che si tratta di un array di stringhe, è possibile utilizzare strlen per trovare il lunghezza della stringa (deve essere terminata con null).

Nel qual caso il ciclo sarebbe diventato

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

I secondo questo approccio ... –

+0

Con tutti i mezzi, usare 'memcpy', ma la domanda è una volta per un vero array multidimensionale o molte volte per un array ragged (che è suggerito dall'uso dell'OP di double indirection .. .)? – dmckee

+0

@dmckee la mia risposta originale è stata scritta per la domanda originale, non per la domanda aggiornata. La mia risposta ora si spera meglio adatta alla domanda aggiornata. – Yacoby

6

Si può solo calcolare la dimensione complessiva della matrice e quindi utilizzare memcpy per copiarlo.

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

Edit: nuove informazioni in questione indica che il numero di righe e colonne della matrice non è noto, e che la matrice può essere frastagliato, quindi memcpy non può essere una soluzione.

+1

sizeof (char) == 1 byte di definizione (se 1 byte è 8 bit o no è una domanda completamente diversa ...) – Jon

+1

@Jon: Sì, ma è innocuo, e aiuta a chiarire che si tratta di un conteggio di byte e non di un conteggio di elementi - e avrebbe bisogno di essere aggiornato se l'array era di grandi dimensioni. –

1

Consente di esplorare alcune possibilità per quello che sta succedendo qui:

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

La morale della storia è: si deve sapere che cosa è al di là delle vostre puntatori. O altro.

Il secondo morale della storia è: solo perché si può accedere ad un doppio puntatore usando la notazione [][] non rende è la stessa matrice bidimensionale. Veramente.


Vorrei chiarire la seconda morale un po '.

Un array (sia esso unidimensionale, bidimensionale, qualunque sia) è un pezzo di memoria allocato, e il compilatore sa quanto è grande (ma non esegue mai alcun controllo di intervallo per voi) e un indirizzo inizia. Dichiari le matrici con

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

e simili;

Un puntatore è una variabile che può contenere un indirizzo di memoria. Dichiari i puntatori con

char *sting_ptr1; 
double *matrix_ptr = NULL; 

Sono due cose diverse.

Ma:

  1. Se si utilizza la sintassi [] con un puntatore, il compilatore farà l'aritmetica dei puntatori per voi.
  2. In quasi tutti i punti in cui si utilizza un array senza dereferenziarlo, il compilatore lo considera come un puntatore al punto iniziale dell'array.

Quindi, posso fare

strcpy(string1,"dmckee"); 

perché regola 2 dice che string1 (un array) è considerato un char*). Allo stesso modo, posso fllow che con:

char *string_ptr2 = string1; 

Infine,

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

stamperà "OK" a causa della regola 1.

0

Si noti che nel seguente esempio:

char **a; 

a[i] è char*. Quindi se fai un memcpy() di a, stai facendo una copia superficiale di quel puntatore.

Vorrei abbandonare l'aspetto multidimensionale e andare con un buffer piatto di dimensioni nn. È possibile simulare A[i][j] con A[i + jwidth]. Quindi è possibile memcpy(newBuffer, oldBuffer, width * height * sizeof(*NewBuffer)).

7

Quando si ha un puntatore a un puntatore in C, è necessario sapere come i dati verranno utilizzati e disposti nella memoria. Ora, il primo punto è ovvio e vero per qualsiasi variabile in generale: se non sai come verrà usata una variabile in un programma, perché farlo? :-). Il secondo punto è più interessante.

Al livello più elementare, un puntatore a tipo T punti uno oggetto di tipo T. Per esempio:

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

Ora, pi punti per una int. Se si desidera, è possibile fare un punto puntatore al primo di molti di questi oggetti:

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

pa ora punta alla prima di una sequenza di 10 (contigui) int valori, e supponendo che malloc() riesce, pb punti al primo di un altro set di 10 (di nuovo contigui) int s.

Lo stesso vale se si dispone di un puntatore a un puntatore:

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

Supponendo che malloc() riesce, ora avete ppa che punta al primo di una serie di 10 contigui int * valori.

Così, quando si esegue:

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

tmp punti al primo char * oggetto in una sequenza di CR_MULTIBULK_SIZE tali oggetti. Ciascuno dei puntatori di cui sopra non è inizializzato, quindi tmp[0] a tmp[CR_MULTIBULK_SIZE-1] contengono tutti elementi inutili. Un modo per inizializzare loro sarebbe di malloc() loro:

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

Il ... sopra è la dimensione dei dati i ° vogliamo. Potrebbe essere una costante, o potrebbe essere una variabile, a seconda di i, o della fase della luna, o un numero casuale, o qualsiasi altra cosa. Il punto principale da notare è che nel ciclo sono presenti le chiamate malloc() a malloc() e che mentre ogni malloc() restituisce un blocco contiguo di memoria, l'contiguità non è garantita per le chiamate malloc(). In altre parole, la seconda chiamata malloc() non è garantita per restituire un puntatore che inizia esattamente dove sono finiti i dati precedenti di malloc().

per rendere le cose più concreto, supponiamo CR_MULTIBULK_SIZE è 3. In foto, i tuoi dati potrebbero apparire così:

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

tmp punti ad un blocco contiguo di 3 char * valori. Il primo dei puntatori, tmp[0], punta a un blocco contiguo di 3 valori char.Allo stesso modo, tmp[1] e tmp[2] punto a 5 e 2 char s rispettivamente. Ma la memoria puntata da tmp[0] a tmp[2] non è contigua nel suo insieme.

Dato che memcpy() copia memoria contigua, ciò che si desidera fare non può essere eseguito da uno memcpy(). Inoltre, è necessario sapere come è stato assegnato ciascun tmp[i]. Quindi, in generale, ciò che si vuole fare ha bisogno di un ciclo:

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

Come sopra, è possibile chiamare memcpy() all'interno del ciclo, in modo da non avete bisogno di ciclo nidificato nel codice. (Molto probabilmente memcpy() è implementata con un ciclo, quindi l'effetto è come se si fosse annidato loop.)

Ora, se si ha un codice simile:

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; 

Vale a dire, si assegnato spazio contiguo per tutta la puntatori in una malloc() chiamata, è possibile copiare tutti i dati senza un ciclo nel codice:

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; 

da quanto sopra, la risposta più semplice è, se si ha più di un malloc() allocare memoria per tmp[i], quindi sarà necessario un ciclo per copiare tutti i dati.

0

Come altri hanno suggerito, sembra che questo sia un array di puntatori piuttosto che un array multi demetional.

così invece di essere

char mdArray [10] [10];

è:

char * Parray [10];

se questo è il caso, l'unica cosa che puoi fare è scorrere con il valore di una lunghezza che si ottiene, se ci sono destinate ad essere stringhe (che sembra che sia) quindi utilizzare strlen nel qual caso sarebbe :

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

Perché non usi 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); 
} 

O qualcosa di simile? Inoltre, è necessario utilizzare stringhe di tipo C? Ne dubito.

Problemi correlati