문제

나는 단지 몇 주 동안 만 C를 쓰고 있었고 너무 걱정하는 데 시간이 걸리지 않았습니다. malloc(). 그러나 최근에 내 프로그램은 내가 기대했던 참/거짓 가치 대신에 행복한 얼굴을 반환했습니다.

다음과 같은 구조물을 만들면 다음과 같습니다.

typedef struct Cell {
  struct Cell* subcells;
} 

그런 다음 나중에 이렇게 초기화하십시오

Cell makeCell(int dim) {
  Cell newCell;

  for(int i = 0; i < dim; i++) {
    newCell.subcells[i] = makeCell(dim -1);
  }

  return newCell; //ha ha ha, this is here in my program don't worry!
}

어딘가에 메모리에 저장된 행복한 얼굴에 접근하거나 이전에 기존의 셀에 대해 글을 쓸 것입니까? 내 질문은, 내가 실제로 malloc ()가 적절한 양의 메모리를 얻지 못했을 때 어떻게 메모리를 할당합니까? 기본값은 무엇입니까?

도움이 되었습니까?

해결책

포인터에는 기본값이 없습니다. 당신의 포인터는 현재 무엇이든 상점을 가리킬 것입니다. 초기화하지 않았으므로 라인

newCell.subcells[i] = ...

메모리의 불확실한 부분에 효과적으로 액세스합니다. 서브 셀은 [i]와 동일하다는 것을 기억하십시오

*(newCell.subcells + i)

왼쪽에 약간의 쓰레기가 포함되어 있으면 결국 추가됩니다. i 쓰레기 가치로, 그 불확실한 위치에서 메모리에 액세스하십시오. 올바르게 말했듯이, 유효한 메모리 영역을 가리키기 위해 포인터를 초기화해야합니다.

newCell.subcells = malloc(bytecount)

그 후 라인은 그 많은 바이트에 액세스 할 수 있습니다. 다른 메모리 소스와 관련하여, 모두 사용하는 다른 종류의 스토리지가 있습니다. 어떤 종류의 종류는 어떤 종류의 객체와 어떤 스토리지 클래스를 사용하도록 지시하는지에 달려 있습니다.

  • malloc 유형이없는 객체에 포인터를 반환합니다. 해당 메모리 영역을 포인터 포인트로 만들 수 있으며 객체의 유형은 효과적으로 객체 유형의 유형이됩니다. 메모리는 어떤 값으로도 초기화되지 않으며 일반적으로 액세스가 느려집니다. 획득 한 객체가 호출됩니다 allocated objects.
  • 전 세계적으로 개체를 배치 할 수 있습니다. 그들의 메모리는 0으로 초기화됩니다. 포인트의 경우, 당신은 null 포인터를 얻을 수 있습니다. 플로트는 적절한 0도 얻을 수 있습니다. 적절한 초기 값에 의존 할 수 있습니다.
  • 로컬 변수가 있지만 사용하십시오 static 스토리지 클래스 지정자는 글로벌 오브젝트와 동일한 초기 값 규칙을 갖습니다. 메모리는 일반적으로 글로벌 오브젝트와 같은 방식으로 할당되지만 필요하지 않습니다.
  • 스토리지 클래스 지정자가없는 로컬 변수가있는 경우 auto, 그러면 변수가 스택에 할당됩니다 (C에 의해 정의되지는 않더라도 컴파일러는 물론 실제로하는 일입니다). 주소를 취할 수 있습니다.이 경우 컴파일러는 물론 레지스터에 넣는 것과 같은 최적화를 생략해야합니다.
  • 스토리지 클래스 지정자와 함께 사용되는 로컬 변수 register, 특별한 저장소가있는 것으로 표시됩니다. 결과적으로 더 이상 주소를 가져갈 수 없습니다. 최근 컴파일러에서는 일반적으로 사용할 필요가 없습니다. register 더 이상, 정교한 최적화기 때문에. 당신이 정말로 전문가라면, 당신은 그것을 사용하면 그 성능을 얻을 수 있습니다.

객체에는 서로 다른 초기화 규칙을 보여주는 데 사용할 수있는 관련 저장 기간이 있습니다 (공식적으로는 객체가 살고있는 시간을 정의합니다). 그와 함께 선언 된 물체 auto 그리고 register 자동 저장 시간이 있으며 ~ 아니다 초기화. 값을 포함하려면 명시 적으로 초기화해야합니다. 그렇지 않은 경우, 평생 시작하기 전에 스택에 남은 컴파일러가 포함됩니다. 할당 된 개체 malloc (또는 그 가족의 또 다른 기능 calloc) 저장 시간을 할당했습니다. 그들의 스토리지는 ~ 아니다 초기화. 사용시 예외입니다 calloc,이 경우 메모리는 0으로 초기화됩니다 ( "real"Zero. alull 포인터 표현에 관계없이 모든 바이트 0x00). 선언 된 개체 static 글로벌 변수는 정적 저장 시간이 있습니다. 그들의 저장 ~이다 해당 유형에 적합한 0으로 초기화되었습니다. 객체에는 유형이 없어야하지만 유형이없는 객체를 얻는 유일한 방법은 할당 된 스토리지를 사용하는 것입니다. (C의 객체는 "스토리지 영역"입니다).

그래서 무엇입니까? 고정 코드는 다음과 같습니다. 메모리 블록을 할당 한 후에는 더 이상 할당 된 항목 수를 더 이상 다시 얻을 수 없으므로 항상 어딘가에 계산하는 것이 가장 좋습니다. Variale을 소개했습니다 dim 카운트를 저장하는 구조물에.

Cell makeCell(int dim) {
  /* automatic storage duration => need to init manually */
  Cell newCell;

  /* note that in case dim is zero, we can either get NULL or a 
   * unique non-null value back from malloc. This depends on the
   * implementation. */
  newCell.subcells = malloc(dim * sizeof(*newCell.subcells));
  newCell.dim = dim;

  /* the following can be used as a check for an out-of-memory 
   * situation:
   * if(newCell.subcells == NULL && dim > 0) ... */
  for(int i = 0; i < dim; i++) {
    newCell.subcells[i] = makeCell(dim - 1);
  }

  return newCell;
}

이제 Dim = 2에 대한 것들이처럼 보입니다.

Cell { 
  subcells => { 
    Cell { 
      subcells => { 
        Cell { subcells => {}, dim = 0 }
      }, 
      dim = 1
    },
    Cell { 
      subcells => { 
        Cell { subcells => {}, dim = 0 }
      }, 
      dim = 1
    }
  },
  dim = 2
}

C에서는 함수의 반환 값이 객체 일 필요는 없습니다. 스토리지가 존재하지 않아야합니다. 결과적으로, 당신은 그것을 바꿀 수 없습니다. 예를 들어 다음은 불가능합니다.

makeCells(0).dim++

무료로 할당 된 메모리 인 "무료 기능"이 필요합니다. 할당 된 객체에 대한 저장은 자동으로 분해되지 않기 때문입니다. 당신은 전화해야합니다 free 모든 기억을 해방시키기 위해 subcells 당신의 나무의 포인터. 당신이 그것을 쓰는 것은 운동으로 남겨졌습니다 :)

다른 팁

짧은 대답: 그것은 당신에게 할당되지 않았습니다.

약간 더 긴 답변 : 그만큼 subcells 포인터는 초기화되지 않으며 가리킬 수 있습니다 어딘가에. 이것은 버그입니다 결코 일어나지 않아야합니다.

더 긴 답변 : 자동 변수는 스택에 할당되며, 글로벌 변수는 컴파일러에 의해 할당되며 종종 특수 세그먼트를 차지하거나 힙에있을 수 있습니다. 글로벌 변수는 기본적으로 0으로 초기화됩니다. 자동 변수에는 기본값이 없으며 (단순히 메모리에서 찾은 값을 얻음) 프로그래머는 좋은 시작 값을 갖도록 책임을집니다 (많은 컴파일러가 잊어 버릴 때 단서를 얻으려고 노력할 것입니다).

그만큼 newCell 귀하의 기능의 변수는 자동이며 초기화되지 않습니다. 그 프론토를 고쳐야합니다. 포인 newCell.subcells 의미있는 가치를 즉시 또는 가리 킵니다 NULL 당신이 그것을 위해 약간의 공간을 할당 할 때까지. 이렇게하면 메모리를 할당하기 전에 해석을 시도하면 세분화 위반이 발생합니다.

더 나쁜 것은, 당신은 a Cell 가치에 따라 a에 할당합니다 Cell * 당신이 채우려고 할 때 subcells 정렬. 포인터를 힙 할당 된 객체에 반환하거나 로컬로 할당 된 객체에 값을 할당하십시오.

이것에 대한 일반적인 관용구는 다음과 같은 형태를 가질 것입니다.

Cell* makeCell(dim){
  Cell *newCell = malloc(sizeof(Cell));
  // error checking here
  newCell->subcells = malloc(sizeof(Cell*)*dim); // what if dim=0?
  // more error checking
  for (int i=0; i<dim; ++i){
    newCell->subCells[i] = makeCell(dim-1);
    // what error checking do you need here? 
    // depends on your other error checking...
  }
  return newCell;
}

내가 당신에게 몇 가지 문제를 남겼지 만 ..

그리고 결국 거래해야 할 모든 메모리를 추적해야합니다 ...

힙에 할당되지 않은 것 (비아 malloc 대신 스택에 비슷한 통화)가 할당됩니다. 그로 인해 특정 기능에서 생성 된 것은 malloc'D는 함수가 끝나면 파괴됩니다. 여기에는 반환 된 개체가 포함됩니다. 함수 후에 스택이 풀리면 반환 된 객체는 발신자 함수에 의해 스택에 따로 설정된 공간에 복사됩니다.

경고: 포인터가있는 객체를 다른 객체로 반환하려면 힙에 가리키는 물체가 힙에 생성되었는지 확인하고, 기능에서 살아남을 의도하지 않는 한 힙에 해당 객체를 만듭니다. 그것이 생성된다.

내 질문은, 내가 실제로 malloc ()가 적절한 양의 메모리를 얻지 못했을 때 어떻게 메모리를 할당합니까? 기본값은 무엇입니까?

메모리를 할당하지 않습니다. 스택에서 또는 동적으로 설명해야합니다.

당신의 예에서, 서브 셀은 an을 가리킨다 한정되지 않은 위치, 버그입니다. 기능은 어느 시점에서 포인터를 세포 구조로 반환해야합니다.

어딘가에 메모리에 저장된 행복한 얼굴에 접근하거나 이전에 기존의 셀에 대해 글을 쓸 것입니까?

당신은 당신이 행복한 얼굴을 얻었다는 것이 운이 좋다. 불행한 날 중 하나에 시스템을 깨끗하게 닦을 수있었습니다.)

내 질문은, 내가 실제로 malloc ()가 적절한 양의 메모리를 얻지 못했을 때 어떻게 메모리를 할당합니까?

그렇지 않습니다. 그러나 셀 뉴스 셀을 정의 할 때, 서브 셀 포인터는 쓰레기 값으로 초기화됩니다. 0 (충돌이 발생할 경우) 또는 실제 메모리 주소처럼 보일 정도로 큰 정수 일 수 있습니다. 그러한 경우 컴파일러는 그곳에있는 모든 가치를 행복하게 가져 와서 다시 가져옵니다.

기본값은 무엇입니까?

이것은 그만큼 변수를 초기화하지 않으면 동작. 그리고 당신의 makeCell 기능은 약간 저개발 해 보입니다.

데이터, 스택 및 힙을 할당 할 수있는 세 부분이 실제로 있습니다.

당신이 언급 한 경우, 그것은 스택에 할당됩니다. 스택에 무언가를 할당하는 문제는 기능 기간에만 유효하다는 것입니다. 함수가 돌아 오면 해당 메모리가 회수됩니다. 따라서 스택에 할당 된 것에 대한 포인터를 반환하면 해당 포인터가 유효하지 않습니다. 그러나 실제 객체를 반환하면 (포인터가 아님), 호출 함수를 사용하기 위해 객체의 사본이 자동으로 만들어집니다.

글로벌 변수 (예 : 헤더 파일 또는 함수 외부)로 선언 한 경우 메모리의 데이터 섹션에 할당됩니다. 이 섹션의 메모리는 프로그램이 시작될 때 자동으로 거래 할 때 자동으로 할당됩니다.

Malloc ()을 사용하여 힙에 무언가를 할당하면 해당 메모리는 사용하려는 한 오랫동안 사용됩니다. 이를 통해 필요한 경우 메모리를 할당하고 처리 할 수있는 유연성을 제공합니다 (모든 것이 전면에 할당되고 프로그램이 종료 될 때만 해제되는 글로벌 사용과 달리).

로컬 변수는 스택에 "할당"됩니다. 스택은 해당 로컬 변수를 보유하기 위해 Preallocated Memory입니다. 함수가 종료 될 때 변수가 유효하지 않으며 다음에 오는 모든 것에 의해 덮어 씁니다.

귀하의 경우, 코드는 결과를 반환하지 않기 때문에 아무것도하지 않습니다. 또한 스코프의 객체에 대한 포인터도 스코프가 종료 될 때 유효하지 않으므로 정확한 경우 (링크 된 목록을 수행하는 것 같습니다) Malloc ()를 사용해야합니다.

이 코드를 읽고 여기 컴퓨터 인 척 할 것입니다 ...

typedef struct Cell {
  struct Cell* subcells;
}

이것은 나에게 다음과 같습니다.

  • 우리는 셀이라는 구조물 유형이 있습니다
  • 서브 셀이라는 포인터가 포함되어 있습니다
  • 포인터는 유형의 구조 셀에 있어야합니다.

포인터가 하나의 셀 또는 셀 어레이로 이동하는지 여부는 알려주지 않습니다. 새 셀이 만들어지면 해당 포인터의 값은 값이 할당 될 때까지 정의되지 않습니다. 포인터를 정의하기 전에 사용하는 것은 나쁜 소식입니다.

Cell makeCell(int dim) {
  Cell newCell;

정의되지 않은 서브 셀 포인터가있는 새로운 셀 구조. 이 모든 것은 셀 구조물의 크기 인 Newcell이라고 불리는 약간의 메모리 덩어리를 예약하는 것입니다. 그것은 그 기억에 있던 값을 바꾸지 않습니다. 그들은 무엇이든 될 수 있습니다.

  for(int i = 0; i < dim; i++) {
    newCell.subcells[i] = makeCell(dim -1);

NewCell.Subcells [i]를 얻기 위해 I의 하위 셀에서 계산이 이루어지면 불쾌한. 구체적으로, 이것은 해당 메모리 주소에서 값을 가져옵니다. 예를 들어, i == 0 ... 그러면 우리는 서브 셀 포인터 자체 (오프셋 없음)를 비교할 것입니다. 서브 셀은 정의되지 않았으므로 무엇이든 할 수 있습니다. 말 그대로 무엇이든! 따라서 이것은 메모리에서 완전히 무작위로 값을 요구할 것입니다. 결과와 함께 아무것도 보장 할 수 없습니다. 무언가를 인쇄 할 수 있습니다. 충돌 할 수 있습니다. 확실히 끝나지 않아야합니다.

  }

  return newCell;
}

포인터로 작업 할 때마다 파악하기 전에 값으로 설정되어 있는지 확인하는 것이 중요합니다. 컴파일러가 가능한 모든 경고를 제공하도록 장려하면 많은 현대 컴파일러가 이런 종류의 일을 잡을 수 있습니다. 또한 Pointers에게 0xdeadbeef와 같은 귀여운 기본값을 줄 수 있습니다 (예! 그것은 16 진수의 숫자 일 뿐이므로 단어 일 뿐이므로 재미있게 보입니다). (Printf의 %P 옵션은 조잡한 디버깅 형태로 포인터를 표시하는 데 도움이됩니다. 디버거 프로그램도 잘 보여줄 수 있습니다.)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top