32

何が起こっているのかとても混乱しています。私はいつも交換可能だと思っchar *char []いましたが、メモリアドレスを見ると、スタックにメモリを割り当てているのchar *に対し、ヒープにスペースを割り当てているようです。char []

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

以下を出力します。

Address of stack[0]: 0x7fff8b0b85b0
Address of heap[0]: 0x400760
Address of heap_string_malloc[0]: 0x400760

char *これは、動的に割り当てられることを意味しますか?

私の混乱に加えて、すでに割り当てられているmallocものと同じメモリアドレスを割り当てるのはなぜですか? char *heap最適化は実行していません (単にgcc file.c)。

4

5 に答える 5

40

配列はポインターではありません。あなたのプログラムが行ごとに行っていることは、

// 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";

同じポインターが 2 回表示される理由は、コンパイラーが を含む 2 番目の静的バッファーを最適化したためです"hello"

于 2013-10-29T10:50:23.470 に答える
15

あなたがするとき、例えば

char *heap = "hello";

ポインターheapは実際にはヒープを指すのではなく、オペレーティング システム ローダーによって残りのプログラムと共にロードされる静的データを指します。実際、正しいためには、

const char *heap = "hello";

asは、一定読み取り専用のメモリheapを指しています。


また、配列はポインターに減衰し (そしてポインターとして使用できます)、ポインターは配列構文で使用できますが、それらは同じではありません。最大の違いは、配列の場合、たとえばsizeof実際の配列のバイト単位のサイズを取得するために使用できますが、ポインターでは使用できないことです。


そして第三に、あなたがやっているとき

char *heap_string_malloc = malloc(5);
heap_string_malloc = "hello";

最初に何かを割り当てた後、すぐに再割り当てしてまったく異なるものを指すため、メモリリークが発生します。heap_string_mallocheap_string_malloc


heap両方で同じアドレスを取得する理由は、heap_string_malloc両方が同じリテラル文字列を指しているためです。

于 2013-10-29T10:50:19.873 に答える
12

などの文字列リテラル"hello"は、プログラムの存続期間にわたって保持されるような方法で格納されます。多くの場合、それらは読み取り専用の別のデータ セグメント (スタックまたはヒープとは異なる) に格納されます。

あなたが書くとき

char stack[] = "hello";

タイプ「6要素配列」の新しいauto(「スタック」)変数を作成しchar(サイズは文字列リテラルの長さから取得されます)、文字列リテラルの内容がそれ"hello"にコピーされます。

あなたが書くとき

char *heap = "hello";

タイプ「ポインター」の新しいauto(「スタック」)変数を作成しており、文字列リテラルcharアドレス"hello"がそれにコピーされます。

これが私のシステムでどのように見えるかです:

       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    p.@.
            0x7fffb00c761c   00   00   00   00    ....

      *heap       0x400b70   68   65   6c   6c    hell
                  0x400b74   6f   00   22   68    o."h

ご覧のとおり、文字列リテラル"hello"にはアドレス 0x400b70 から始まる独自のストレージがあります。stackahdheap 変数は両方ともauto(「スタック」) 変数 として作成されます。文字列リテラルの内容のコピーstackを含み、文字列リテラルのアドレスを含みます。 heap

ここでmalloc、文字列にメモリを割り当て、結果を に割り当てるために使用するとしますheap

heap = malloc( sizeof *heap * strlen( "hello" + 1 ));
strcpy( heap, "hello" );

これで、メモリ マップは次のようになります。

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

この変数には、文字列「hello」を含む さらに別のheap6 バイトのメモリ チャンクを指す別のアドレスが含まれています。

編集

byteofthat の場合、上記のマップを生成するために使用するコードは次のとおりです。

ダンパー.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

ダンパー.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);
  }
}

そしてそれを使用する方法の例:

#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;
}
于 2013-10-29T15:57:27.173 に答える
1

これにより、静的文字列「hello」のコピーを含む配列がスタック上に作成されます。

char stack[] = "hello";

これにより、静的文字列「hello」のアドレスを含むポインターがスタック上に作成されます。

char *heap = "hello";

これにより、動的に割り当てられた 5 バイトのバッファーのアドレスを含むポインターがスタック上に作成されます。

char *heap_string_malloc = malloc(5);

しかし、3 つのケースすべてで、何かをスタックに置きます。char*「ヒープ上」ではありません。これは、何か、どこかを指す (スタック上の) ポインターです。

于 2013-10-29T11:38:04.007 に答える