2012-05-13 4 views
48

오늘 나는 C 코드로 제 친구를 돕고 있었고, 왜 그런 일이 일어 났는지 설명 할 수없는 이상한 행동을 발견했습니다. 우리는 TSV 파일에 정수가있는 줄을 int로 붙였습니다. 첫 번째 줄은 목록의 줄 수입니다.배열 타입과 배열이 malloc으로 할당 된 것의 차이

매우 간단한 "readfile"을 가진 c 파일도 있습니다. 제 N 라인으로 판독하고, 라인의 수는, 다음의 초기화가이었다

int list[n] 

최종적 A A fscanf와 N의 루프.

작은 n (~ 100.000까지)이면 모든 것이 좋았습니다. 그러나 n이 클 때 (10^6), segfault가 발생한다는 것을 알았습니다.

마지막으로, 우리는 심지어 매우 큰 N으로,

int *list = malloc(n*sizeof(int)) 

과 모든 것을 목록 초기화를 변경하면 잘.

왜 이런 일이 발생했는지 설명 할 수 있습니까? 우리가 list = malloc (n * sizeof (int))를 사용하기 시작할 때 중지 된 int list [n]과 함께 segfault를 일으키는 것은 무엇입니까?

답변

109

여기에는 여러 가지 작품이 있습니다.

제는

int array[n]; 

상기 제 버전에서

int* array = malloc(n * sizeof(int)); 

같은 어레이 선언의 차이이며,는 자동 저장 기간을 가진 오브젝트를 선언한다. 즉, 배열을 호출하는 함수가 존재하는 동안에 만 배열이 존재 함을 의미합니다. 두 번째 버전에서는 동적 저장 기간이있는 메모리를 얻게됩니다. 즉, free으로 명시 적으로 할당 해제 될 때까지 존재할 것입니다.

여기서 두 번째 버전이 작동하는 이유는 C가 일반적으로 컴파일되는 방법에 대한 구현 세부 사항입니다. 일반적으로 C 메모리는 스택 (함수 호출 및 로컬 변수 용)과 힙 (malloc ed 객체 용)을 포함하여 여러 영역으로 분할됩니다. 스택은 일반적으로 힙보다 크기가 훨씬 작습니다. 대개 8MB와 비슷합니다. 그 결과, 당신은 segfault의 원인, 그럼 당신은 스택의 저장 공간을 초과 할 수

int array[n]; 

와 함께 거대한 배열을 할당하려고합니다. 반면에 힙은 일반적으로 크기가 큽니다 (예 : 시스템에서 사용 가능한만큼의 공간). 따라서 큰 객체를 사용하더라도 메모리 부족 오류가 발생하지 않습니다.

일반적으로 C의 가변 길이 배열에는주의해야합니다. 스택 크기를 쉽게 초과 할 수 있습니다. 크기가 작거나 실제로 짧은 기간 동안 만 배열을 원한다는 것을 알지 않는 한 malloc을 선호하십시오.

희망이 도움이됩니다.

+1

매우 정답답 ... 고마워요! –

+1

좋은 답변입니다! 속도면에서도 차이가 있는지 궁금합니다. –

+1

참조 지역의 영향으로 스택에 할당 된 배열의 액세스가 더 빠르며 'malloc' 자체가 스택 포인터를 범핑하는 것보다 훨씬 느린 것으로 생각됩니다. 그러나 실제로, 현재 진행중인 작업에 더 적합한 어떤 접근 방식을 사용하는 것이 가장 좋습니다. – templatetypedef

2

int list [n]은 스택에 데이터를 저장하고 malloc은 스택에 데이터를 저장합니다.

스택이 제한되어 있고 많은 공간이 없지만 힙은 훨씬 큽니다.

1

int list[n]은 힙 대신 스택에 할당하는 VLA입니다. 이 함수를 해제 할 필요는 없습니다 (함수 호출이 끝나면 자동으로 해제됩니다). 빠르게 할당되지만 저장 공간은 매우 제한적입니다. 힙에 더 큰 값을 할당해야합니다.

1

이 선언

int list[n] 

의 malloc 힙에 할당 스택에 메모리를 할당한다.

스택 크기는 일반적으로 힙보다 작기 때문에 스택에 너무 많은 메모리를 할당하면 스택 오버 플로우가 발생합니다.

this answer for further information

0

당신이 malloc를 사용하여 할당, 메모리가 훨씬 더 제한된 크기에 스택에서 힙에서하지 할당을 참조하십시오. n 정수 일반적으로 아주 작은 스택에 대한

8
int list[n] 

를 할당 공간. 스택에서 메모리를 사용하는 것은 대안보다 훨씬 빠르지 만 거대한 배열을 할당하거나 재귀를 너무 심하게 수행하면 스택이 오버플로 (즉, 너무 많은 메모리 할당)하기 쉽습니다. 이 방법으로 할당 된 메모리를 수동으로 할당 해제 할 필요는 없으며 배열이 범위를 벗어날 때 컴파일러가 수행합니다. 한편

malloc 스택에 비해 일반적으로 매우 큰, 공간을 할당한다.다량의 메모리를 할당하려면 힙에 많은 양의 메모리를 할당해야하지만 스택에있는 것보다 힙에 메모리를 할당하는 것이 훨씬 느립니다. 사용을 마치면 free을 통해 수동으로 할당을 해제해야합니다 그것.

+1

"스택의 메모리를 사용하는 것이 대안보다 훨씬 빠릅니다."여기에서 "할당"또는 "액세스"를 의미합니까? AFAIK, 스택 할당은 훨씬 빠르지 만 액세스 (읽기/쓰기)에도 적용됩니까? 감사합니다 – dragonxlwang

1

가 가장 가능성이 구현 전형적인 구현을 가정하면 : 당신의 스택에

int list[n] 

할당 목록, 어디에 : 힙에

int *list = malloc(n*sizeof(int)) 

할당 된 메모리.

스택의 경우 일반적으로 이들이 커질 수있는 크기에 제한이 있습니다 (전혀 커질 수있는 경우). 힙의 경우에는 여전히 한계가 있지만 RAM + 스왑 + 주소 공간 (일반적으로 크기가 더 크지 않은 경우)보다 크고 (광범위하게) 제한됩니다.

0

Linux를 사용하는 경우 ulimit -s를 더 큰 값으로 설정할 수 있으며 스택 할당에도 사용할 수 있습니다. 스택에 메모리를 할당하면 해당 메모리는 함수 실행이 끝날 때까지 남아 있습니다. 힙에 메모리를 할당하면 (malloc 사용) 언제든지 (함수 실행이 끝나기 전에) 메모리를 해제 할 수 있습니다.

일반적으로 큰 메모리 할당에는 힙을 사용해야합니다.

0
int array[n]; 

정적으로 할당 된 배열의 예이며 컴파일 타임에 배열의 크기를 알 수 있습니다. 배열은 스택에 할당됩니다.

int *array(malloc(sizeof(int)*n); 

동적으로 할당 된 배열의 예이며 배열의 크기는 런타임에 사용자에게 알려집니다. 배열은 힙에 할당됩니다.