为什么堆栈上的char []而堆上的char *?

时间:2013-10-29 10:46:34

标签: c pointers memory

我对发生的事情感到非常困惑。我一直认为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)。

5 个答案:

答案 0 :(得分:34)

Arrays are not pointers。你的程序一行一行地做了什么

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

两次看同一指针的原因是编译器优化了包含"hello"的第二个静态缓冲区。

答案 1 :(得分:11)

当你这样做时。

char *heap = "hello";

指针heap实际上并不指向堆,它指向由操作系统加载程序与其余程序一起加载的静态数据。事实上,为了正确,它应该是

const char *heap = "hello";

因为heap指向常量只读内存。


此外,虽然数组衰减(并且可以用作)指针,并且指针可以与数组语法一起使用,但它们相同。最大的区别在于您可以使用例如阵列。 sizeof获取实际数组的大小(以字节为单位),而指针则不可能。


作为第三件事,当你做的时候

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

你有一个内存泄漏,因为你先给heap_string_malloc分配一些内容,然后直接重新分配heap_string_malloc以指向完全不同的内容。


至于你为heapheap_string_malloc获取相同地址的原因是因为它们都指向相同的文字字符串。

答案 2 :(得分:7)

"hello"之类的字符串文字以这样的方式存储,即它们在程序的生命周期内保留。它们通常存储在可能是只读的单独数据段(不同于堆栈或堆)中。

写作时

char stack[] = "hello";

您正在创建一个新的auto(“堆栈”)变量,类型为“{-1}}的6元素数组”(大小取自字符串文字的长度),并且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 有自己的存储空间,从地址0x400b70开始。 "hello" ahd stack 变量都创建为heap(“堆栈”)变量。 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”的另一个 6字节内存块。

编辑

对于byteofthat,这是我用来生成上述地图的代码:

dumper.h:

heap

dumper.c:

#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

以及如何使用它的一个例子:

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

答案 3 :(得分:1)

“stack”是一个静态的字符数组,因此它将在堆栈中分配,并在函数结束后自动释放,因为它的大小自定义以来就已知。 虽然“heap”和“heap_string_malloc”都被声明为char缓冲区的指针,并且需要动态分配malloc以定义其内容的大小,这就是它们将驻留在堆内存中的原因。 通过做:

char *heap = "hello";

heap_string_malloc = "hello";

你正在修改指针self-self(带有静态缓冲区值),而不是它们指向的内容。您应该使用memcpy来修改“heap_string_malloc”指针指向的内存和数据:

memcpy(heap_string_malloc, "hello", 5);

答案 4 :(得分:1)

这会在堆栈上创建一个数组,其中包含静态字符串“hello”的副本:

char stack[] = "hello";

这会在堆栈上创建一个指针,其中包含静态字符串“hello”的地址:

char *heap = "hello";

这会在堆栈上创建一个指针,其中包含一个动态分配的5字节缓冲区的地址:

char *heap_string_malloc = malloc(5);

但是在所有这三种情况下,你都会把东西放在堆栈上。 char*不是“在堆上”。它是指向某个地方的指针(在堆栈上)。