내 응용 프로그램에서 다음 코드를 사용하고 있으며 정상적으로 작동합니다. 그러나 malloc으로 만들거나 그대로 두는 것이 더 좋은지 궁금합니다.
function (int len)
{
char result [len] = some chars;
send result over network
}
내 응용 프로그램에서 다음 코드를 사용하고 있으며 정상적으로 작동합니다. 그러나 malloc으로 만들거나 그대로 두는 것이 더 좋은지 궁금합니다.
function (int len)
{
char result [len] = some chars;
send result over network
}
답변:
주요 차이점은 VLA (가변 길이 배열)가 할당 실패를 감지하는 메커니즘을 제공하지 않는다는 것입니다.
선언하면
char result[len];
그리고 len
가능한 스택 공간의 양이 프로그램의 행동이 정의되지 초과합니다. 할당의 성공 여부를 미리 결정하거나 성공 여부를 결정한 후에는 언어 메커니즘이 없습니다.
반면에 다음과 같이 쓰면 :
char *result = malloc(len);
if (result == NULL) {
/* allocation failed, abort or take corrective action */
}
그런 다음 실패를 정상적으로 처리하거나 최소한 실패 후에도 프로그램이 계속 실행되지 않도록 보장 할 수 있습니다.
(주로 Linux 시스템에서는 malloc()
사용 가능한 해당 스토리지가없는 경우에도 주소 공간 청크를 할당 할 수 있습니다. 나중에 해당 공간을 사용하려고 시도하면 OOM Killer 가 호출 될 수 있습니다 . 그러나 malloc()
여전히 실패를 확인하는 것이 좋습니다.)
또 다른 문제는, 많은 시스템에 더 많은 공간 (가능성이 있다는 것입니다 많이 사용할 더 많은 공간) malloc()
블라스와 같은 자동 객체보다가.
필립의 답변에서 이미 언급했듯이 VLA는 C99에 추가되었습니다 (특히 Microsoft는이를 지원하지 않습니다).
그리고 VLA는 C11에서 선택 사항이되었습니다. 아마도 대부분의 C11 컴파일러가이를 지원할 것이지만 믿을 수는 없습니다.
C99에서 가변 길이 자동 배열이 C에 도입되었습니다.
이전 표준과의 하위 호환성에 대해 걱정하지 않는 한 괜찮습니다.
일반적으로 작동하면 만지지 마십시오. 미리 최적화하지 마십시오. 특별한 기능을 추가하거나 일을하는 영리한 방법을 추가하는 것에 대해 걱정하지 마십시오. 자주 사용하지 않기 때문입니다. 간단하게 유지하십시오.
컴파일러가 가변 길이 배열을 지원하면 len
엄청나게 큰 일부 시스템에서 스택이 오버플로되는 위험이 있습니다. 당신은 확실히 알고있는 경우 len
특정 숫자보다 큰 될 수 없습니다, 당신은 당신의 스택, 심지어 최대 길이 오버 플로우에가는대로 코드를 떠나지 않을 것을 알고; 그렇지 않으면 malloc
and로 다시 작성하십시오 free
.
char result [sizeof(char)]
는 크기 배열 1
이므로 sizeof(char)
1과 같으므로 할당이 잘립니다 some chars
.
str
포인터로 쇠퇴 하므로 sizeof
시스템의 포인터 크기에 따라 4-8 개가됩니다.
char* result = alloca(len);
경우 스택에 할당 할 수 있습니다 . 그것은 동일한 기본 효과 (그리고 동일한 기본 문제)를 가지고 있습니다
메모리 조각화, 매달려있는 포인터 등이없는 런타임 할당 된 배열을 가질 수 있다는 아이디어가 마음에 듭니다. 그러나 다른 사람들은이 런타임 할당이 자동으로 실패 할 수 있다고 지적했습니다. 그래서 Cygwin bash 환경에서 gcc 4.5.3을 사용하여 이것을 시도했습니다.
#include <stdio.h>
#include <string.h>
void testit (unsigned long len)
{
char result [len*2];
char marker[100];
memset(marker, 0, sizeof(marker));
printf("result's size: %lu\n", sizeof(result));
strcpy(result, "this is a test that should overflow if no allocation");
printf("marker's contents: '%s'\n", marker);
}
int main(int argc, char *argv[])
{
testit(100);
testit((unsigned long)-1); // probably too big
}
결과는 다음과 같습니다.
$ ./a.exe
result's size: 200
marker's contents: ''
result's size: 4294967294
marker's contents: 'should overflow if no allocation'
두 번째 호출에서 지나친 길이가 너무 길어서 오류가 발생했습니다 (marker []로 넘침). 그렇다고 이런 종류의 검사가 바보가 아니거나 (영리 할 수 있음) C99의 표준을 충족한다는 의미는 아니지만 그러한 우려가있는 경우 도움이 될 수 있습니다.
평소처럼 YMMV.
일반적으로 스택은 데이터를 저장하기에 가장 쉽고 가장 좋은 장소입니다.
나는 당신이 기대하는 가장 큰 배열을 할당함으로써 VLA의 문제를 피할 것입니다.
그러나 힙이 가장 좋고 malloc을 어지럽히는 것이 노력할 가치가있는 경우가 있습니다.
아무도 언급하지 않은 것은 VLA 할당이 스택 포인터를 조정하는 경우 (GCC에서) 가변 길이 배열 옵션이 malloc / free보다 훨씬 빠를 것입니다.
따라서이 함수가 자주 호출되는 기능인 경우 (물론 프로파일 링에 의해 결정됨) VLA는 좋은 최적화 옵션입니다.
이것은 내가 도움이 될 수있는 문제에 사용하는 매우 일반적인 C 솔루션입니다. VLA와 달리 병리학 적 사례에서 스택 오버플로의 실제 위험에 직면하지 않습니다.
/// Used for frequent allocations where the common case generally allocates
/// a small amount of memory, at which point a heap allocation can be
/// avoided, but rare cases also need to be handled which may allocate a
/// substantial amount. Note that this structure is not safe to copy as
/// it could potentially invalidate the 'data' pointer. Its primary use
/// is just to allow the stack to be used in common cases.
struct FastMem
{
/// Stores raw bytes for fast access.
char fast_mem[512];
/// Points to 'fast_mem' if the data fits. Otherwise, it will point to a
/// dynamically allocated memory address.
void* data;
};
/// @return A pointer to a newly allocated memory block of the specified size.
/// If the memory fits in the specified fast memory structure, it will use that
/// instead of the heap.
void* fm_malloc(struct FastMem* mem, int size)
{
// Utilize the stack if the memory fits, otherwise malloc.
mem->data = (size < sizeof mem->fast_mem) ? mem->fast_mem: malloc(size);
return mem->data;
}
/// Frees the specified memory block if it has been allocated on the heap.
void fm_free(struct FastMem* mem)
{
// Free the memory if it was allocated dynamically with 'malloc'.
if (mem->data != mem->fast_mem)
free(mem->data);
mem->data = 0;
}
귀하의 경우에 사용하려면 :
struct FastMem fm;
// `result` will be allocated on the stack if 'len <= 512'.
char* result = fm_malloc(&fm, len);
// send result over network.
...
// this function will only do a heap deallocation if 'len > 512'.
fm_free(&fm, result);
위의 경우 문자열이 512 바이트 이하인 경우 스택을 사용합니다. 그렇지 않으면 힙 할당을 사용합니다. 예를 들어, 시간의 99 %가 문자열이 512 바이트 이하인 경우 유용 할 수 있습니다. 그러나 때로는 문자열이 32 킬로바이트 인 곳에서 사용자가 키보드에서 잠들거나 그와 비슷한 것을 처리해야 할 수도있는 미친 이국적인 사례가 있다고 가정 해 봅시다. 이를 통해 두 상황을 모두 문제없이 처리 할 수 있습니다.
I는 제조에 사용되는 실제 버전은 자체 버전 갖는다 realloc
과 calloc
동일한 개념을 기반으로 표준 순응 C ++ 데이터 구조 등뿐만 아니라,하지만 개념을 설명하기 위해 필요한 최소한의 추출 하였다.
복사하는 것이 위험하다는 경고가 있으며, 그것을 통해 할당 된 포인터를 반환해서는 안됩니다 ( FastMem
인스턴스가 파괴 되면 무효화 될 수 있음 ). 로컬 함수의 범위 내에서 항상 스택 / VLA를 사용하려는 유혹을받는 간단한 경우에 사용하기위한 것입니다. 그렇지 않으면 드문 경우로 버퍼 / 스택 오버플로가 발생할 수 있습니다. 범용 할당자가 아니므로 그대로 사용해서는 안됩니다.
실제로 C89를 사용하는 레거시 코드베이스의 상황에 대한 응답으로 이전 팀은 사용자가 2047자를 초과하는 이름을 가진 항목의 이름을 지정할 수 없을 것이라고 생각했습니다 (키보드에서 잠들었을 수도 있음) ). 내 동료들은 실제로 다양한 장소에 할당 된 배열의 크기를 16,384로 늘리려 고 노력했습니다.이 시점에서 나는 어리석은 것으로 생각하고 버퍼 오버플로의 위험을 줄이면서 스택 오버플로의 위험을 높이는 것으로 생각했습니다. 이것은 단지 몇 줄의 코드를 추가함으로써 이러한 경우를 해결하기 위해 플러그인하기 매우 쉬운 솔루션을 제공했습니다. 이로 인해 일반적인 경우를 매우 효율적으로 처리 할 수 있었으며 소프트웨어를 충돌시키는 힙을 요구하는 드문 경우가 거의없이 스택을 계속 사용할 수있었습니다. 그러나 나는 VLA는 여전히 스택 오버플로로부터 우리를 보호 할 수 없기 때문에 C99 이후에도 유용하다는 것을 알게되었습니다. 이것은 작은 할당 요청을 위해 여전히 스택에서 풀링 할 수 있습니다.
호출 스택은 항상 제한됩니다. Linux 또는 Windows와 같은 주류 OS에서 한도는 몇 메가 바이트 (및이를 변경할 수있는 방법을 찾을 수 있음)입니다. 일부 멀티 스레드 응용 프로그램의 경우 스레드가 더 작은 스택으로 생성 될 수 있으므로 더 낮아질 수 있습니다. 임베디드 시스템에서는 몇 킬로바이트만큼 작을 수 있습니다. 일반적으로 몇 킬로바이트보다 큰 통화 프레임을 피하는 것이 좋습니다.
따라서 VLA 를 사용하는 len
것이 충분히 작은 경우 (최대 수십만)에 해당하는 경우에만 의미가 있습니다 . 그렇지 않으면 스택 오버플 로가 발생하며 정의되지 않은 동작 의 경우 매우 무서운 상황입니다.
그러나 수동 C 동적 메모리 할당 (예 : calloc
또는 malloc
&free
)을 사용하면 단점도 있습니다.
실패 할 수 있으며 항상 실패를 테스트 해야합니다 (예 : calloc
또는 malloc
리턴 NULL
).
VLA 할당에 성공 malloc
하려면 몇 나노초가 걸리고, 성공 하려면 몇 마이크로 초 (좋은 경우 마이크로 초의 몇 분의 1 초) 또는 더 많이 ( 쓰 레싱 과 관련된 병리학적인 경우 훨씬 더) 가 필요할 수 있습니다.
코딩하기가 훨씬 어렵습니다. free
뾰족한 영역이 더 이상 사용되지 않는 경우에만 가능 합니다. 귀하의 경우에 당신은 모두를 호출 할 수 calloc
와 free
같은 루틴입니다.
당신은 대부분의 시간이 알고 있다면 result
(매우 가난한 이름, 당신은 결코 의 주소를 반환해야합니다 자동 변수 VLA를, 내가 사용하므로 buf
대신 result
아래)이다 작은 그럴 수있어 특별한 경우, 예를 들어,
char tinybuf[256];
char *buf = (len<sizeof(tinybuf))?tinybuf:malloc(len);
if (!buf) { perror("malloc"); exit(EXIT_FAILURE); };
fill_buffer(buf, len);
send_buffer_on_network(buf, len);
if (buf != tinybuf)
free(buf);
그러나 위의 코드는 읽기 쉽지 않으며 아마도 조기 최적화 일 것입니다. 그러나 순수한 VLA 솔루션보다 강력합니다.
추신. 일부 시스템 (예 : 일부 Linux 배포판은 기본적으로 활성화되어 있음)에는 메모리 초과 커밋이 있습니다 ( 메모리malloc
가 충분하지 않더라도 포인터 를 제공함). 이것은 내가 싫어하는 기능이며 일반적으로 Linux 시스템에서 비활성화합니다.