Warum ist char[] auf dem Stack, aber char * auf dem Heap?

Lesezeit: 9 Minuten

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).

  • 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 tippen char []. Aber ein char * 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-const char* -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 zuzuweisen str_params->some_char_arr = some_char_ptr), was offensichtlich nicht funktioniert hat

    – Elias Van Ootegem

    29. Oktober 2013 um 11:46 Uhr

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".

  • +1 Und natürlich ist es vollkommen sinnvoll, dass beide statische Puffer enthalten "hello" sind an der gleichen Adresse, also heap_string_malloc und heap könnte sehr gut auf die gleiche Stelle verweisen.

    – mjs

    29. Oktober 2013 um 10:54 Uhr


  • wenn er es nicht getan hätte heap_string_malloc auf “hello” zu zeigen, aber stattdessen auf eine andere Zeichenfolge “hola”, wäre es immer noch ein Speicherleck?

    – Benutzer2018675

    29. Oktober 2013 um 10:54 Uhr

  • +1 Dieser Artikel bringt sehr gut die einfache Tatsache nach Hause, dass Zeiger sind Variablen das halt Adressen; Arrays sind Variablen, die sind Adressen.

    – WhozCraig

    29. Oktober 2013 um 10:55 Uhr

  • @user2018675 Natürlich ist da nichts Magisches "hello" in c 🙂

    – Fred Foo

    29. Oktober 2013 um 10:55 Uhr

  • @ user2018675 Ja, weil er immer noch nichts hat, was auf den von malloc zugewiesenen Speicher hinweist. Ich erkläre es gerne mit der Analogie von Hunden. Ich kaufe einen Hund in einem Geschäft und bringe ihn mit einer Leine (Leine für Sie Amerikaner …) in den Park. Während ich im Park bin, lasse ich ihn von der Leine, verbinde die Leine mit dem Hund eines anderen und gehe nach Hause. Ich habe noch einen Hund, aber nicht meinen Hund. Der “Hund” ist das, was Malloc zurückgegeben hat. Der “Lead” ist heap_string_malloc. Der andere Hund ist “Hallo”.

    – mjs

    29. Oktober 2013 um 11:03 Uhr


Benutzer-Avatar
Irgendein Programmierer

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.

Benutzer-Avatar
Johannes Bode

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

  • Wie druckt man diese Informationen aus einem eigenen Programm heraus aus?

    Benutzer1422456

    1. September 2015 um 21:48 Uhr

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

1385900cookie-checkWarum ist char[] auf dem Stack, aber char * auf dem Heap?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy