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
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);
}
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) int
s.
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 i
th-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 char
s. 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.
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.
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:
[]
-Syntax mit einem Zeiger verwenden, führt der Compiler die Zeigerarithmetik für Sie aus.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".
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.
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...
}
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))
.