programing

char[]는 스택에 있지만 char *는 힙에 있는 이유는 무엇입니까?

javajsp 2023. 7. 23. 14:02

char[]는 스택에 있지만 char *는 힙에 있는 이유는 무엇입니까?

저는 무슨 일이 일어나고 있는지 매우 혼란스럽습니다.나는 항상 생각했습니다.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이미 할당되었습니까?하지 않습니다(단순히 단않다니습히실지하순행를최적화단(▁iply).gcc file.c).

배열은 포인터가 아닙니다.당신의 프로그램이 하고 있는 것은, 한 줄 한 줄,

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

예를 들면.

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완전히 다른 것을 가리키기 위해서입니다.


당신이 두 사람에 대해 같은 주소를 받는 이유에 대해서는.heap그리고.heap_string_malloc둘 다 동일한 문자 문자열을 가리키기 때문입니다.

예: 다과같문리터럴열)"hello"프로그램의 수명 동안 유지되는 방식으로 저장됩니다.이러한 데이터는 종종 읽기 전용인 별도의 데이터 세그먼트(스택 또는 힙과 구별됨)에 저장됩니다.

당신이 글을 쓸 때

char stack[] = "hello";

새을만중니다입는드를 만들고 있습니다."를 .auto의 변수 "("stack") "("의) " "6차원 배열"char(크기는 문자열 리터럴의 길이에서 따옴) 및 문자열 리터럴의 내용"hello"복사됩니다.

당신이 글을 쓸 때

char *heap = "hello";

새을만중니다입는드를 만들고 있습니다."를 .auto("은 "("stack") ~ ("stack")입니다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부터 시작하는 자체 스토리지가 있습니다.둘 다stack제1의heap 변수는 다음과 같이 생성됩니다.auto("스택") 변수입니다.stack문자열 리터럴의 내용 복사본을 포함하는 동안heap문자열 리터럴의 주소를 포함합니다.

제가 자, 럼그제를 사용한다고 해 보겠습니다.malloc를 문열에대메할당결하할고과당다합니를를모리자한▁the▁to▁allocate▁to에 할당하는 것입니다.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...

heapvariable에는 이제 "hello" 문자열이 포함된 다른 6바이트 메모리 청크를 가리키는 다른 주소가 포함되어 있습니다.

편집

위의 지도를 생성하는 데 사용하는 코드는 다음과 같습니다.

바보야.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);
  }
}

사용 방법에 대한 예:

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

이렇게 하면 스택에 정적 문자열 "hello"의 복사본을 포함하는 배열이 생성됩니다.

char stack[] = "hello";

이렇게 하면 정적 문자열 "hello"의 주소가 포함된 포인터가 스택에 생성됩니다.

char *heap = "hello";

이렇게 하면 동적으로 할당된 5바이트 버퍼의 주소를 포함하는 포인터가 스택에 생성됩니다.

char *heap_string_malloc = malloc(5);

하지만 세 가지 경우 모두, 여러분은 무언가를 쌓아 올립니다.char*가 "더미 위"가 아닙니다.이것은 (스택의) 포인터로, 어딘가를 가리킵니다.

"stack"은 정적인 문자 배열이므로 스택에 할당되고 함수가 종료되면 자동으로 해제됩니다. 이는 정의된 이후로 크기가 알려져 있기 때문입니다."heap"과 "heap_string_malloc"은 둘 다 char 버퍼에 대한 포인터로 선언되며 컨텐츠의 크기를 정의하기 위해 malloc과 함께 동적으로 할당되어야 하지만, 이것이 바로 힙 메모리에 상주하는 이유입니다.수행 방법:

char *heap = "hello";

그리고:

heap_string_malloc = "hello";

포인터가 가리키는 콘텐츠가 아니라 포인터 자체를(고정 버퍼 값으로) 수정하는 것입니다.차라리 memcpy를 사용하여 "heap_string_malloc" 포인터가 가리키는 메모리를 데이터로 수정해야 합니다.

memcpy(heap_string_malloc, "hello", 5);

언급URL : https://stackoverflow.com/questions/19656025/why-is-char-on-the-stack-but-char-on-the-heap