webentwicklung-frage-antwort-db.com.de

C/C++ Wie kopiere ich ein mehrdimensionales Zeichen-Array ohne verschachtelte Schleifen?

Ich suche nach einer intelligenten Möglichkeit, ein mehrdimensionales Zeichen-Array an ein neues Ziel zu kopieren. Ich möchte das Zeichen-Array duplizieren, da ich den Inhalt bearbeiten möchte, ohne das Quell-Array zu ändern.

Ich könnte geschachtelte Schleifen erstellen, um jedes Zeichen von Hand zu kopieren, aber ich hoffe, es gibt einen besseren Weg.

Update:

Ich habe nicht die Größe der 2. Ebene Dimension. Angegeben ist nur die Länge (Zeilen).

Der Code sieht folgendermaßen aus:

char **tmp;
char **realDest;

int length = someFunctionThatFillsTmp(&tmp);

//now I want to copy tmp to realDest

Ich suche nach einer Methode, die den gesamten Speicher von tmp in den freien Speicher kopiert und realDest darauf verweist.

Update 2:

someFunctionThatFillsTmp () ist die Funktion credis_lrange () aus der Redis C-Bibliothek credis.c .

Innerhalb der lib wird tmp erstellt mit:

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

Update 3:

Ich habe versucht, memcpy mit diesen Zeilen zu verwenden:

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

prints: mystring

Aber ich erhalte ein: Programm empfangenes Signal: EXC_BAD_ACCESS

21
dan

Sie könnten memcpy verwenden.

Wenn die Größe des mehrdimensionalen Arrays zur Kompilierzeit angegeben wird, d. H. mytype myarray[1][2], ist nur ein einziger Memcpy-Aufruf erforderlich

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

Wenn, wie Sie angegeben haben, dass das Array dynamisch zugewiesen wurde, Sie die Größe der beiden Dimensionen kennen müssen, da sie dynamisch zugewiesen wird, befindet sich der im Array verwendete Speicher nicht in einem zusammenhängenden Speicherort mehrfach verwendet werden.

Bei einem 2D-Array würde die Methode zum Kopieren folgendermaßen aussehen:

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

Da es sich bei Ihrer Frage um ein Array von Strings handelt, könnten Sie strlen verwenden, um die Länge des Strings zu ermitteln (er muss nullterminiert sein).

In diesem Fall würde die Schleife werden

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

Wenn Sie einen Zeiger auf einen Zeiger in C haben, müssen Sie wissen, wie die Daten verwendet und im Speicher abgelegt werden. Nun, der erste Punkt ist offensichtlich und gilt für jede Variable im Allgemeinen: Wenn Sie nicht wissen, wie eine Variable in einem Programm verwendet wird, warum haben Sie es? :-) Der zweite Punkt ist interessanter.

In der einfachsten Ebene zeigt ein Zeiger auf den Typ T auf one object vom Typ T. Zum Beispiel:

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

Nun zeigt pi auf eine int. Wenn Sie möchten, können Sie einen Zeiger auf das erste von vielen dieser Objekte setzen:

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

pa zeigt jetzt auf den ersten einer Folge von 10 (zusammenhängenden) int-Werten. Wenn malloc() erfolgreich ist, zeigt pb auf den ersten einer anderen Gruppe von 10 (wieder zusammenhängenden) ints.

Dasselbe gilt, wenn Sie einen Zeiger auf einen Zeiger haben:

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

Vorausgesetzt, dass malloc() erfolgreich ist, haben Sie jetzt ppa, die auf den ersten einer Folge von 10 aufeinanderfolgenden int *-Werten zeigt.

Also, wenn Sie es tun:

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

tmp zeigt auf das erste char *-Objekt in einer Folge von CR_MULTIBULK_SIZE-Objekten. Jeder der Zeiger oben ist nicht initialisiert, so dass tmp[0] bis tmp[CR_MULTIBULK_SIZE-1] alle Müll enthalten. Eine Möglichkeit, sie zu initialisieren, wäre die malloc():

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

Der ... oben ist die Größe der gewünschten ith-Daten. Es kann eine Konstante sein oder eine Variable, abhängig von i oder der Mondphase oder einer Zufallszahl oder irgendetwas anderem. Zu beachten ist vor allem, dass Sie CR_MULTIBULK_SIZE-Aufrufe von malloc() in der Schleife haben und dass zwar jede malloc() einen zusammenhängenden Speicherblock zurückgibt, die Verbindung jedoch bei malloc()-Aufrufen nicht gewährleistet ist. Mit anderen Worten, der zweite Aufruf malloc() kann nicht garantiert einen Zeiger zurückgeben, der genau dort beginnt, wo die Daten der vorherigen malloc() aufgehört haben.

Nehmen wir zur Verdeutlichung an, dass CR_MULTIBULK_SIZE 3 ist. In Bildern können Ihre Daten folgendermaßen aussehen:

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

tmp zeigt auf einen zusammenhängenden Block von 3 char *-Werten. Der erste der Zeiger, tmp[0], zeigt auf einen zusammenhängenden Block von 3 char-Werten. In ähnlicher Weise zeigen tmp[1] und tmp[2] auf 5 bzw. 2 chars. Der Speicher, auf den tmp[0] bis tmp[2] verwiesen wird, ist als Ganzes nicht zusammenhängend.

Da memcpy() zusammenhängenden Speicher kopiert, kann das, was Sie möchten, nicht von einer memcpy() ausgeführt werden. Außerdem müssen Sie wissen, wie jeder tmp[i] zugewiesen wurde. Im Allgemeinen erfordert das, was Sie tun möchten, eine Schleife:

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

Wie oben beschrieben, können Sie memcpy() innerhalb der Schleife aufrufen, sodass Sie keine verschachtelte Schleife in Ihrem Code benötigen. (Wahrscheinlich wird memcpy() mit einer Schleife implementiert, der Effekt ist also, als hätten Sie geschachtelte Schleifen.) 

Nun, wenn Sie Code wie:

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;

Das heißt, Sie haben zusammenhängenden Platz für alle Zeiger in einem Aufruf von malloc() zugewiesen. Dann können Sie alle Daten ohne Schleife in Ihrem Code kopieren:

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;

Die einfache Antwort lautet: Wenn Sie mehr als eine malloc() zum Zuordnen von Speicher für tmp[i] haben, benötigen Sie eine Schleife, um alle Daten zu kopieren.

8
Alok Singhal

Sie können nur die Gesamtgröße des Arrays berechnen und dann memcpy verwenden, um es zu kopieren. 

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

Bearbeiten: Neue Informationen in der Frage zeigen an, dass die Anzahl der Zeilen und Spalten des Arrays nicht bekannt ist und das Array möglicherweise unregelmäßig ist, sodass Memcpy möglicherweise keine Lösung darstellt. 

6
John Knoeller

Lasst uns einige Möglichkeiten für das erkunden, was hier vor sich geht:

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

Die Moral der Geschichte ist: Sie müssen wissen, was sich auf der anderen Seite Ihrer Zeiger befindet. Oder aber.

Die Sekunde Moral der Geschichte ist: Nur weil Sie mit der [][]-Notation auf einen Doppelzeiger zugreifen können, ist dies nicht das Gleiche wie ein zweidimensionales Array. Ja wirklich.


Lassen Sie mich die zweite Moral etwas klären.

Ein Array (sei es eindimensional, zweidimensional, was auch immer) ist ein zugewiesener Speicherplatz, und der Compiler weiß, wie groß er ist (aber er überprüft niemals einen Bereich für Sie) und welche Adresse er beginnt. Sie deklarieren Arrays mit

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

und ähnliche Dinge;

Ein Pointer ist eine Variable, die eine Speicheradresse enthalten kann. Sie erklären Zeiger mit

char *sting_ptr1;
double *matrix_ptr = NULL;

Sie sind zwei verschiedene Dinge.

Aber:

  1. Wenn Sie die []-Syntax mit einem Zeiger verwenden, führt der Compiler die Zeigerarithmetik für Sie aus.
  2. An fast jedem Ort verwenden Sie ein Array, ohne es zu dereferenzieren. Der Compiler behandelt es als Zeiger auf die Startposition des Arrays.

Also kann ich tun

    strcpy(string1,"dmckee");

weil Regel 2 besagt, dass string1 (ein Array) als char*) behandelt wird. Ebenso kann ich das fließen mit:

    char *string_ptr2 = string1;

Endlich, 

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

druckt wegen Regel 1 "OK".

1
dmckee

Warum verwenden Sie nicht 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);
}

Oder etwas ähnliches? Benötigen Sie brauchen Sie , um C-Strings zu verwenden? Ich bezweifle das.

1
Mateen Ulhaq

Wie von anderen vorgeschlagen, sieht es so aus, als sei dies ein Array von Zeigern und nicht ein multi-demetionales Array.

also anstatt es zu sein

char mdArray [10] [10];

es ist:

char * pArray [10];

wenn dies der Fall ist, können Sie nur mit dem einen Längenwert durchlaufen, den Sie erhalten. Wenn Strings (wie es aussieht) sind, verwenden Sie strlen. In diesem Fall wäre dies der Fall:

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...
}
0
matt

Beachten Sie das im folgenden Beispiel:

char **a;

a[i] ist char*. Wenn Sie also eine memcpy() von a ausführen, machen Sie eine flache Kopie dieses Zeigers.

Ich würde den mehrdimensionalen Aspekt aufgeben und mit einem flachen Puffer der Größe nn gehen. Sie können A[i][j] mit A[i + jwidth] simulieren. Dann können Sie memcpy(newBuffer, oldBuffer, width * height * sizeof(*NewBuffer)).

0
asveikau