Ich bin sehr verwirrt über das, was passiert. Ich dachte immer char *
und char []
waren austauschbar, aber nach einem Blick auf die Speicheradressen scheint es char *
weist Platz im Heap zu, wohingegen char []
reserviert Speicher auf dem Stack.
char stack[] = "hello";
char *heap = "hello";
char *heap_string_malloc = malloc(5);
heap_string_malloc = "hello";
printf("Address of stack[0]: %p\n", stack);
printf("Address of heap[0]: %p\n", heap);
printf("Address of heap_string_malloc[0]: %p\n", heap_string_malloc);
Gibt Folgendes aus:
Address of stack[0]: 0x7fff8b0b85b0
Address of heap[0]: 0x400760
Address of heap_string_malloc[0]: 0x400760
Bedeutet dies das char *
wird dynamisch zugewiesen?
Weiter zu meiner Verwirrung, wie kommt es malloc
weist dieselbe Speicheradresse wie what zu char *heap
schon zugeteilt? Ich führe keine Optimierung durch (einfach gcc file.c
).
Arrays sind keine Zeiger. Was Ihr Programm Zeile für Zeile tut, ist
// Allocate 6 bytes in the stack and store "hello" in them
char stack[] = "hello";
// Allocate pointer on the stack and point it to a static, read-only buffer
// containing "hello"
char *heap = "hello";
// Malloc 5 bytes (which isn't enough to hold "hello" due to the NUL byte)
char *heap_string_malloc = malloc(5);
// Reset heap_string_malloc to point to a static buffer; memory leak!
heap_string_malloc = "hello";
Der Grund, warum Sie denselben Zeiger zweimal sehen, liegt darin, dass der Compiler den zweiten statischen Puffer wegoptimiert hat "hello"
.
Wenn Sie z
char *heap = "hello";
der Zeiger heap
zeigt eigentlich nicht auf den Heap, sondern auf statische Daten, die zusammen mit dem Rest des Programms vom Ladeprogramm des Betriebssystems geladen werden. Eigentlich sollte es richtig sein
const char *heap = "hello";
wie heap
zeigt auf a Konstante und schreibgeschützt Stück Erinnerung.
Auch wenn Arrays zu Zeigern zerfallen (und als solche verwendet werden können) und Zeiger mit Array-Syntax verwendet werden können, sind sie es nicht das Gleiche. Der größte Unterschied besteht darin, dass Sie für ein Array z sizeof
um die Größe in Bytes des tatsächlichen Arrays zu erhalten, während dies für Zeiger nicht möglich ist.
Und als Drittes, wenn du es tust
char *heap_string_malloc = malloc(5);
heap_string_malloc = "hello";
du hast ein Speicherleckwenn Sie zuerst etwas zuweisen heap_string_malloc
aber dann direkt danach neu zuweisen heap_string_malloc
auf etwas ganz anderes hinweisen.
Aus dem Grund erhalten Sie für beide die gleiche Adresse heap
und heap_string_malloc
Das liegt daran, dass beide auf dieselbe Literalzeichenfolge zeigen.
Zeichenfolgenliterale wie z "hello"
werden so gespeichert, dass sie über die Lebensdauer des Programms erhalten bleiben. Sie werden häufig in einem separaten Datensegment (das sich vom Stack oder Heap unterscheidet) gespeichert, das möglicherweise schreibgeschützt ist.
Wenn du schreibst
char stack[] = "hello";
du erschaffst ein neues auto
(“Stack”)-Variable vom Typ “6-element array of char
” (Größe wird aus der Länge des String-Literals genommen), und die Inhalt des String-Literals "hello"
werden darauf kopiert.
Wenn du schreibst
char *heap = "hello";
du erschaffst ein neues auto
(“Stack”)-Variable vom Typ “Zeiger auf char
“, und die die Anschrift des String-Literals "hello"
wird darauf kopiert.
So sieht es auf meinem System aus:
Item Address 00 01 02 03
---- ------- -- -- -- --
"hello" 0x400b70 68 65 6c 6c hell
0x400b74 6f 00 22 68 o."h
stack 0x7fffb00c7620 68 65 6c 6c hell
0x7fffb00c7624 6f 00 00 00 o...
heap 0x7fffb00c7618 70 0b 40 00 [email protected]
0x7fffb00c761c 00 00 00 00 ....
*heap 0x400b70 68 65 6c 6c hell
0x400b74 6f 00 22 68 o."h
Wie Sie sehen können, ist das String-Literal "hello"
hat einen eigenen Speicher, beginnend bei Adresse 0x400b70. Beide stack
ahd heap
Variablen sind erstellt als auto
(“Stack”)-Variablen. stack
enthält ein Kopieren des Inhalts des String-Literals, while heap
enthält die die Anschrift des String-Literals.
Angenommen, ich verwende malloc
den Speicher für die Zeichenfolge zuzuweisen und das Ergebnis zuzuweisen heap
:
heap = malloc( sizeof *heap * strlen( "hello" + 1 ));
strcpy( heap, "hello" );
Jetzt sieht meine Memory Map wie folgt aus:
Item Address 00 01 02 03
---- ------- -- -- -- --
"hello" 0x400b70 68 65 6c 6c hell
0x400b74 6f 00 22 68 o."h
stack 0x7fffb00c7620 68 65 6c 6c hell
0x7fffb00c7624 6f 00 00 00 o...
heap 0x7fffb00c7618 10 10 50 00 ..P.
0x7fffb00c761c 00 00 00 00 ....
*heap 0x501010 68 65 6c 6c hell
0x501014 6f 00 00 00 o...
Das heap
Variable enthält jetzt eine andere Adresse, die auf zeigt noch ein anderer 6-Byte-Speicherabschnitt, der die Zeichenfolge „hello“ enthält.
BEARBEITEN
Für byteofthat ist hier der Code, den ich verwende, um die obige Karte zu generieren:
dumper.h:
#ifndef DUMPER_H
#define DUMPER_H
/**
* Dumps a memory map to the specified output stream
*
* Inputs:
*
* names - list of item names
* addrs - list of addresses to different items
* lengths - length of each item
* count - number of items being dumped
* stream - output destination
*
* Outputs: none
* Returns: none
*/
void dumper(char **names, void **addrs, size_t *lengths, size_t count, FILE *stream);
#endif
Dumper.c:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "dumper.h"
/**
* Dumps a memory map to the specified output stream
*
* Inputs:
*
* names - list of item names
* addrs - list of addresses to different items
* lengths - length of each item
* count - number of items being dumped
* stream - output destination
*
* Outputs: none
* Returns: none
*/
void dumper(char **names, void **addrs, size_t *lengths, size_t count, FILE *stream)
{
size_t i;
int maxlen = 15;
for ( size_t j = 0; j < count; j++ )
{
if (strlen(names[j]) > maxlen && strlen(names[j]) < 50)
maxlen = strlen(names[j]);
}
fprintf(stream,"%*s%15s%5s%5s%5s%5s\n", maxlen, "Item", "Address", "00", "01",
"02", "03");
fprintf(stream,"%*s%15s%5s%5s%5s%5s\n", maxlen, "----", "-------", "--", "--",
"--", "--");
for (i = 0; i < count; i++)
{
size_t j;
char *namefield = names[i];
unsigned char *p = (unsigned char *) addrs[i];
for (j = 0; j < lengths[i]; j+=4)
{
size_t k;
fprintf(stream,"%*.*s", maxlen, maxlen, namefield);
fprintf(stream,"%15p", (void *) p);
for (k = 0; k < 4; k++)
{
fprintf(stream,"%3s%02x", " ", p[k]);
}
fprintf(stream, " ");
for ( k = 0; k < 4; k++)
{
if (isgraph(p[k]))
fprintf(stream,"%c", p[k]);
else
fprintf(stream, ".");
}
fputc('\n', stream);
namefield = " ";
p += 4;
}
fputc('\n', stream);
}
}
Und ein Beispiel für die Verwendung:
#include <stdio.h>
#include "dumper.h"
int main(void)
{
int x = 0;
double y = 3.14159;
char foo[] = "This is a test";
void *addrs[] = {&x, &y, foo, "This is a test"};
char *names[] = {"x", "y", "foo", "\"This is a test\""};
size_t lengths[] = {sizeof x, sizeof y, sizeof foo, sizeof "This is a test"};
dumper(names, addrs, lengths, 4, stdout);
return 0;
}
Dadurch wird ein Array auf dem Stack erstellt, das eine Kopie der statischen Zeichenfolge „hello“ enthält:
char stack[] = "hello";
Dadurch wird ein Zeiger auf dem Stack erstellt, der die Adresse der statischen Zeichenfolge „hello“ enthält:
char *heap = "hello";
Dadurch wird ein Zeiger auf dem Stack erstellt, der die Adresse eines dynamisch zugewiesenen Puffers von 5 Bytes enthält:
char *heap_string_malloc = malloc(5);
Aber in allen drei Fällen legen Sie etwas auf den Stapel. char*
ist nicht “auf dem Haufen”. Es ist ein Zeiger (auf dem Stapel), der irgendwo auf etwas zeigt.
“Stack” ist ein statisches Array von Zeichen, sodass es im Stack zugewiesen und automatisch freigegeben wird, sobald die Funktion endet, da seine Größe seit seiner Definition bekannt ist. Während “heap” und “heap_string_malloc” beide als Zeiger auf char-Puffer deklariert sind und dynamisch mit malloc zugewiesen werden müssen, um die Größe ihres Inhalts zu definieren, befinden sie sich deshalb im Heap-Speicher. Indem Sie Folgendes tun:
char *heap = "hello";
und:
heap_string_malloc = "hello";
Sie ändern die Zeiger selbst (mit einem statischen Pufferwert), nicht den Inhalt, auf den sie zeigen. Sie sollten lieber memcpy verwenden, um den Speicher, auf den der Zeiger “heap_string_malloc” zeigt, mit Ihren Daten zu modifizieren:
memcpy(heap_string_malloc, "hello", 5);
char *heap_string_malloc = malloc(5); heap_string_malloc = "hello";
ist ein Insta-Speicherleck. Das ist kein Java. Bedenken Sie:int a=5; a=6;
Wo sind die 5 geblieben?– WhozCraig
29. Oktober 2013 um 10:48 Uhr
Arrays und Zeiger sind NICHT das gleiche.
– Blagovest Buyukliev
29. Oktober 2013 um 10:49 Uhr
"hello"
auch tippenchar []
. Aber einchar *
kann sehr gut darauf hinweisen.– Sadik
29. Oktober 2013 um 10:50 Uhr
@EliasVanOotegem Nicht im globalen Raum, nein, das wird es nicht. In einem lokalen Raum einer Funktion als Automatik wird sie sicher gut kompiliert (und Speicher wie beschrieben verlieren). Die Warnung, die Sie erhalten haben, stammt wahrscheinlich von der Zuweisung von a
const char[]
Adresse an eine nicht-constchar*
-Zeiger, vor dem neuere C-Compiler Sie warnen werden (z. B. mein Clang).– WhozCraig
29. Oktober 2013 um 11:32 Uhr
@WhozCraig: Mein Fehler … Ich habe meinen Code überprüft, der Fehler, den ich bekam, war noch alberner (in einer Funktion
MyStruct **str_params
Ich habe versucht, etwas in der Art von zuzuweisenstr_params->some_char_arr = some_char_ptr
), was offensichtlich nicht funktioniert hat– Elias Van Ootegem
29. Oktober 2013 um 11:46 Uhr