alloca () 사용이 왜 좋은 습관으로 간주되지 않습니까?
alloca()
의 경우와 같이 힙이 아닌 스택에 메모리를 할당합니다 malloc()
. 그래서 루틴에서 돌아올 때 메모리가 해제됩니다. 따라서 실제로 이것은 동적으로 할당 된 메모리를 확보하는 문제를 해결합니다. 할당 된 메모리를 해제 malloc()
하는 것은 큰 골치 거리이며, 놓친 경우 모든 종류의 메모리 문제를 유발합니다.
alloca()
위의 기능에도 불구하고 사용이 권장되지 않는 이유는 무엇 입니까?
그 대답은 man
페이지에 있습니다 (적어도 Linux에서는 ).
반환 값 alloca () 함수는 할당 된 공간의 시작에 대한 포인터를 반환합니다. 할당으로 인해 스택 오버플로가 발생하면 프로그램 동작이 정의되지 않습니다.
절대 사용해서는 안된다는 말은 없습니다. 내가 작업하는 OSS 프로젝트 중 하나는 광범위하게 사용되며, 당신이 그것을 학대하지 않는 한 ( alloca
'큰 가치 를 지니고있는 한) 괜찮습니다. "수백 바이트"표시를 지나면 malloc
대신 친구와 친구 를 사용할 차례 입니다. 여전히 할당 실패가 발생할 수 있지만 적어도 스택을 제거하는 대신 실패에 대한 표시가있을 것입니다.
내가 가진 가장 기억에 남는 버그 중 하나는 사용한 인라인 함수와 관련이있었습니다 alloca
. 프로그램 실행의 임의의 지점에서 스택 오버플로로 인해 스택 오버플로로 나타납니다.
헤더 파일에서 :
void DoSomething() {
wchar_t* pStr = alloca(100);
//......
}
구현 파일에서 :
void Process() {
for (i = 0; i < 1000000; i++) {
DoSomething();
}
}
따라서 컴파일러 인라인 DoSomething
함수가 발생했으며 모든 스택 할당이 Process()
함수 내부에서 발생 하여 스택을 폭파했습니다. 내 방어에서 (그리고 나는 문제를 발견 한 사람이 아니었고, 고칠 수 없을 때 고위 개발자 중 한 명에게 가서 울어야했다), 그것은 직선적이지 않았다 alloca
. 그것은 ATL 문자열 변환 중 하나였다. 매크로.
따라서 교훈은- alloca
인라인 될 것으로 생각되는 함수 에는 사용하지 마십시오 .
오래된 질문이지만 아무도 가변 길이 배열로 대체해야한다고 언급하지 않았습니다.
char arr[size];
대신에
char *arr=alloca(size);
표준 C99에 있으며 많은 컴파일러에서 컴파일러 확장으로 존재했습니다.
alloca ()는 런타임에 크기를 결정해야하기 때문에 표준 로컬 변수를 사용할 수 없고이 함수가 반환 된 후에 alloca ()에서 얻은 포인터가 절대 사용되지 않을 것을 절대 보장 할 수있는 경우 매우 유용 합니다 .
당신은 당신이 상당히 안전 할 수 있습니다
- 포인터 또는 포인터가 포함 된 것을 반환하지 마십시오.
- 힙에 할당 된 구조에 포인터를 저장하지 마십시오
- 다른 스레드가 포인터를 사용하지 못하게하십시오.
진짜 위험은 다른 사람이 나중에이 조건을 위반할 가능성에서 비롯됩니다. 그것을 염두에두고 텍스트를 형식화하는 함수에 버퍼를 전달하는 것이 좋습니다 :)
이 뉴스 그룹 게시 에서 언급했듯이 사용 alloca
이 어렵고 위험한 것으로 간주 될 수 있는 몇 가지 이유 가 있습니다.
- 모든 컴파일러가 지원하는 것은 아닙니다
alloca
. - 일부 컴파일러는 의도 된 동작을
alloca
다르게 해석 하므로이를 지원하는 컴파일러 간에도 이식성이 보장되지 않습니다. - 일부 구현은 버그가 있습니다.
한 가지 문제는 널리 지원되지만 표준이 아니라는 것입니다. 평등 한 다른 것들은 항상 일반적인 컴파일러 확장이 아닌 표준 함수를 사용합니다.
여전히 alloca 사용이 권장되지 않는 이유는 무엇입니까?
나는 그러한 합의를 인식하지 못한다. 많은 강력한 프로; 몇 가지 단점 :
- C99는 가변 길이 배열을 제공하며, 고정 길이 배열과보다 일관성있는 표기법 및 전체적으로 직관적 인 표기법으로 우선적으로 사용되는 가변 길이 배열
- 많은 시스템이 힙에 비해 스택에 사용할 수있는 전체 메모리 / 주소 공간이 적으므로 프로그램이 스택 오버플로를 통해 메모리 소모에 약간 더 취약 해집니다. 스택이 힙처럼 자동으로 확장되지 않는 이유는 제어 불가능한 프로그램이 전체 시스템에 많은 악영향을 미치지 않도록하기위한 것입니다.
- 더 많은 로컬 범위 (예 :
while
또는for
루프) 또는 여러 범위에서 사용되는 경우 메모리는 반복 / 범위 당 누적되며 함수가 종료 될 때까지 해제되지 않습니다. 이는 제어 구조의 범위에 정의 된 일반 변수와 대조됩니다 (예 : X에서 요청한 -ed 메모리를for {int i = 0; i < 2; ++i) { X }
누적alloca
하지만 고정 크기 배열의 메모리는 반복마다 재활용됩니다). - 현대 컴파일러는 일반적으로
inline
호출하는 함수를 호출 하지 않지만alloca
강제로 호출alloca
하면 호출자의 컨텍스트에서 발생합니다 (예 : 호출자가 반환 할 때까지 스택이 해제되지 않습니다) - 오래 전에
alloca
휴대 할 수없는 기능 / 해킹에서 표준화 된 확장으로 전환되었지만 일부 부정적인 인식이 지속될 수 있습니다. - 수명은 함수 범위에 구속되며, 이는 프로그래머
malloc
의 명시 적 제어 보다 더 적합하거나 그렇지 않을 수 있습니다. - 사용할 필요하면
malloc
할당 해제에 대한 생각을 장려 - 그 래퍼 함수 (예를 통해 관리 않다면WonderfulObject_DestructorFree(ptr)
), 다음 함수는 클라이언트에 명시 적으로 변경하지 않고 (파일 기술자를 닫는 내부 포인터를 자유롭게 또는 일부 로깅을하고 같은) 구현 정리 작업을위한 지점을 제공합니다 코드 : 때로는 일관성있게 채택하는 것이 좋은 모델입니다.- 이 의사 -OO 스타일의 프로그래밍
WonderfulObject* p = WonderfulObject_AllocConstructor();
에서 "constructor"가malloc
-ed 메모리를 반환하는 함수일 때 가능 합니다 (함수는 저장 될 값을 반환 한 후 메모리가 할당 된 상태p
이므로). "생성자"가alloca
- 매크로 버전은
WonderfulObject_AllocConstructor
이를 달성 할 수 있지만 "매크로는 악의적"입니다. 즉, 매크로와 코드가 아닌 코드가 서로 충돌하여 의도하지 않은 대체 및 결과적으로 진단하기 어려운 문제가 발생할 수 있다는 점에서
- 매크로 버전은
- 누락 된
free
작업은 ValGrind, Purify 등에서 감지 할 수 있지만 누락 된 "파괴자"호출을 항상 감지 할 수는 없습니다. 의도 된 사용을 시행한다는 측면에서 매우 작은 이점이 있습니다.alloca()
GCC와 같은 일부 구현은에 인라인 매크로를 사용alloca()
하므로 메모리 사용 진단 라이브러리의 런타임 대체는malloc
/realloc
/와free
같은 방식으로 불가능합니다 (예 : 전기 울타리)
- 이 의사 -OO 스타일의 프로그래밍
- 일부 구현에는 미묘한 문제가 있습니다 (예 : Linux 맨 페이지).
많은 시스템에서 alloca ()는 함수 호출의 인수 목록 내에서 사용될 수 없습니다. alloca ()에 의해 예약 된 스택 공간은 함수 인수를위한 공간의 중간에있는 스택에 나타날 것이기 때문입니다.
나는이 질문에 C 태그가 붙어 있음을 알고 있지만 C ++ 프로그래머로서 C ++을 사용하여 잠재적 유틸리티를 설명 할 것이라고 생각했습니다 alloca
. 아래 코드 (및 여기 ideone )는 스택 할당 된 힙 할당이 아닌 함수 반환에 연결된 수명).
#include <alloca.h>
#include <iostream>
#include <vector>
struct Base
{
virtual ~Base() { }
virtual int to_int() const = 0;
};
struct Integer : Base
{
Integer(int n) : n_(n) { }
int to_int() const { return n_; }
int n_;
};
struct Double : Base
{
Double(double n) : n_(n) { }
int to_int() const { return -n_; }
double n_;
};
inline Base* factory(double d) __attribute__((always_inline));
inline Base* factory(double d)
{
if ((double)(int)d != d)
return new (alloca(sizeof(Double))) Double(d);
else
return new (alloca(sizeof(Integer))) Integer(d);
}
int main()
{
std::vector<Base*> numbers;
numbers.push_back(factory(29.3));
numbers.push_back(factory(29));
numbers.push_back(factory(7.1));
numbers.push_back(factory(2));
numbers.push_back(factory(231.0));
for (std::vector<Base*>::const_iterator i = numbers.begin();
i != numbers.end(); ++i)
{
std::cout << *i << ' ' << (*i)->to_int() << '\n';
(*i)->~Base(); // optionally / else Undefined Behaviour iff the
// program depends on side effects of destructor
}
}
다른 답변은 모두 맞습니다. 그러나 할당하려는 alloca()
것이 합리적으로 작 으면 사용하는 것보다 빠르고 편리한 것이 좋은 기술이라고 생각합니다 malloc()
.
다시 말해서, alloca( 0x00ffffff )
위험하고 오버 플로우를 유발할 가능성이 높습니다 char hugeArray[ 0x00ffffff ];
. 조심하고 합리적으로하면 괜찮을 것입니다.
모든 사람들은 이미 스택 오버플로에서 정의되지 않은 동작이 될 수있는 큰 것을 지적했지만 Windows 환경에는 구조적 예외 (SEH)와 가드 페이지를 사용하여이를 포착 할 수있는 훌륭한 메커니즘이 있다고 언급해야합니다. 스택은 필요한만큼만 커지므로 이러한 보호 페이지는 할당되지 않은 영역에 있습니다. 스택을 오버플로하여 할당하면 예외가 발생합니다.
이 SEH 예외를 포착하고 _resetstkoflw를 호출하여 스택을 재설정하고 계속 진행할 수 있습니다. 이상적이지는 않지만 물건이 팬에 부딪 칠 때 무언가 잘못되었다는 것을 아는 또 다른 메커니즘입니다. * nix는 내가 모르는 것과 비슷한 것을 가지고있을 수 있습니다.
alloca를 래핑하고 내부에서 추적하여 최대 할당 크기를 제한하는 것이 좋습니다. 실제로 하드 코어라면 함수 상단에 일부 범위 센트리를 던져서 함수 범위의 모든 할당량을 추적하고 프로젝트에서 허용되는 최대량과 비교할 수 있습니다.
또한 메모리 누수를 허용하지 않는 것 외에도 alloca는 메모리 조각화를 일으키지 않으므로 매우 중요합니다. 나는 당신이 그것을 지능적으로 사용한다면 alloca가 나쁜 습관이라고 생각하지 않습니다. :-)
이 "오래된"질문에 대한 많은 흥미로운 답변들, 심지어 비교적 새로운 답변들도 있지만, 나는 이것을 언급하는 것을 찾지 못했습니다 ....
적절하고주의해서 사용하면
alloca()
작은 가변 길이 할당 (또는 사용 가능한 경우 C99 VLA)을 처리하기 위해 (어쩌면 응용 프로그램 전체에서) 일관성있게 사용 하면 고정 길이의 대형 로컬 어레이를 사용하는 동등한 구현보다 전체 스택 성장 이 낮아질 수 있습니다 . 그래서alloca()
수 있습니다 당신의 스택 좋은 당신은 신중하게 사용하는 경우.
나는 그 인용구를 발견했다 .... 좋아, 나는 그 인용구를 만들었다. 하지만 정말로 생각해보세요 ....
@j_random_hacker는 다른 답변 아래 그의 의견에 매우 옳습니다 : alloca()
대형 로컬 배열을 선호 하여 사용 하지 않는 것이 스택 오버플로에서 프로그램을 더 안전하게 만들지 않습니다 (컴파일러가 alloca()
어떤 경우에 사용해야하는 함수를 인라인 할 수있을 정도로 오래되지 않은 한) alloca()
내부 루프 를 사용하지 않는 경우, 내부 루프 를 사용 해서는 안됩니다 alloca()
).
나는 데스크탑 / 서버 환경과 임베디드 시스템에서 일했다. 많은 임베디드 시스템은 힙을 전혀 사용하지 않으며 (지원하지도 않습니다), 응용 프로그램에서 메모리 누수의 위험으로 인해 동적으로 할당 된 메모리가 악하다는 인식이 포함됩니다. 한 번에 몇 년 동안 재부팅하거나 동적 메모리가 위험하다는 더 합리적인 정당화는 응용 프로그램이 힙을 메모리 소진 지점으로 분할하지 않을 것이라는 점을 확실히 알 수 없기 때문에 위험합니다. 따라서 임베디드 프로그래머에게는 몇 가지 대안이 남아 있습니다.
alloca()
(또는 VLA)는 해당 직무에 적합한 도구 일 수 있습니다.
프로그래머가 스택 할당 버퍼를 "가능한 경우를 처리하기에 충분히 큰"시간과 시간을 다시 보았습니다. 깊게 중첩 된 호출 트리에서 해당 (anti-?) 패턴을 반복적으로 사용하면 스택 사용이 과장됩니다. (20 레벨 깊이의 콜 트리를 상상해보십시오. 여러 가지 이유로 각 레벨에서이 함수는 일반적으로 16 개 이하의 버퍼 만 사용할 때 "안전하게"1024 바이트의 버퍼를 맹목적으로 할당합니다. 드문 경우에는 더 많이 사용할 수 있습니다.) 대안은alloca()
또는 스택에 불필요하게 부담을주지 않도록 함수에 필요한만큼의 스택 공간 만 할당하십시오. 호출 트리의 한 함수가 정상보다 큰 할당을 필요로 할 때, 호출 트리의 다른 함수는 여전히 정상적인 작은 할당을 사용하고 있으며, 전체 함수 스택 사용량이 모든 함수가 로컬 버퍼에 맹목적으로 초과 할당 된 경우보다 훨씬 적습니다. .
그러나 사용하기로 선택하면 alloca()
...
이 페이지의 다른 답변에 따르면 VLA는 안전해야합니다 (루프 내에서 호출되면 스택 할당을 복합화하지 않음)하지만 alloca()
을 사용하는 경우 루프 내에서 사용하지 않도록주의하십시오. 다른 함수의 루프 내에서 호출 될 가능성이있는 경우 함수를 인라인 할 수 없는지 확인 하십시오.
alloca () 는 훌륭하고 효율적입니다 ...하지만 그것은 또한 크게 깨졌습니다.
- 끊어진 범위 동작 (블록 범위 대신 기능 범위)
- malloc에와 일관성이 사용 ( alloca 함수 () -ted 포인터가 해제되지해야한다, 이제부터 당신은 당신이 포인터가 어디에서 오는지 추적 할 수 있습니다 ) (무료 오직 당신이있어 이들 의 malloc () )
- 인라인을 사용할 때도 잘못된 동작 (수신자가 인라인인지 여부에 따라 범위가 발신자 기능으로 이동 함).
- 스택 경계 검사 없음
- 실패시 정의되지 않은 동작 (malloc과 같은 NULL을 반환하지 않습니다 ... 그리고 실패는 어쨌든 스택 경계를 확인하지 않기 때문에 의미합니다 ...)
- ANSI 표준이 아님
대부분의 경우 지역 변수와 주요 크기를 사용하여 바꿀 수 있습니다. 큰 객체에 사용되는 경우 힙에 배치하는 것이 일반적으로 더 안전합니다.
실제로 C가 필요한 경우 VLA를 사용할 수 있습니다 (C ++에는 vla가 없으며 너무 나쁩니다). 범위 동작 및 일관성과 관련하여 alloca ()보다 훨씬 낫습니다. 내가 알다시피 VLA 는 일종의 alloca () 가 올바르게 만들어졌습니다.
물론 필요한 공간을 많이 사용하는 로컬 구조 또는 배열이 여전히 더 좋으며, 일반 malloc ()을 사용하여 이러한 주요 힙 할당이 없으면 아마도 제정신입니다. alloca () 또는 VLA 가 정말로 필요한 정상적인 사용 사례는 없습니다 .
이유는 다음과 같습니다.
char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;
누구나이 코드를 작성하지는 않지만, 크기 인수는 alloca
거의 확실하게 일종의 입력에서 나옵니다. 이는 악의적으로 프로그램을 alloca
거대한 것으로 만들려고 할 수 있습니다 . 결국, 크기가 입력을 기반으로하지 않거나 크게 될 가능성이 없다면 왜 작은 고정 크기 로컬 버퍼를 선언하지 않았습니까?
사실상 모든 코드를 사용 alloca
하거나 C99 vlas를 사용하는 경우 심각한 버그가 발생하여 충돌 (운이 좋을 경우) 또는 권한 손상 (운이 좋지 않은 경우)이 발생할 수 있습니다.
커널 alloca()
보다 특히 위험한 장소 malloc()
-일반적인 운영 체제의 커널에는 고정 된 스택 공간이 헤더 중 하나에 하드 코딩되어 있습니다. 애플리케이션 스택만큼 유연하지 않습니다. alloca()
보증되지 않은 크기 로 호출 하면 커널이 중단 될 수 있습니다. 특정 컴파일러 alloca()
는 커널 코드를 컴파일하는 동안 켜져 야하는 특정 옵션에서 해당 문제에 대한 VLA 사용에 대해 경고합니다. 여기서 하드 코딩 된 한계로 수정되지 않은 힙에 메모리를 할당하는 것이 좋습니다.
실수로 할당 된 블록 이상으로 작성하는 경우 alloca
(때문에 예를 들어, 버퍼 오버 플로우에), 당신은 덮어 쓰게됩니다 반환 주소 하나가 스택에, 즉 "위에"위치하기 때문에, 당신의 기능을 한 후 귀하의 할당 된 블록.
이것의 결과는 두 가지입니다.
프로그램은 눈에 띄게 충돌하며 왜 또는 어디에서 충돌했는지 알 수 없습니다 (겹친 프레임 포인터로 인해 스택이 임의의 주소로 풀릴 수 있음).
악의적 인 사용자가 스택에 놓일 수있는 특수 페이로드를 만들 수 있기 때문에 버퍼 오버플로가 여러 번 더 위험 해집니다.
반대로, 힙에 블록을 넘어 쓰면 힙 손상이 발생합니다. 프로그램이 예기치 않게 종료되지만 스택을 올바르게 해제하여 악성 코드가 실행될 가능성을 줄입니다.
한 가지 함정 alloca
은 그것을 longjmp
되 감는 것입니다.
당신이 가진 컨텍스트를 저장하는 경우 즉, 말을하는 것입니다 setjmp
, 다음, alloca
다음, 일부 메모리를 longjmp
, 당신이 잃을 수있는 상황에 alloca
(통지의 어떤 종류없이) 메모리를. 스택 포인터는 원래 있던 위치로 돌아가므로 메모리가 더 이상 예약되지 않습니다. 함수를 호출하거나 다른 함수를 수행 alloca
하면 원본이 클로버됩니다 alloca
.
명확히하기 위해, 여기서 구체적으로 언급하는 것은 발생한 longjmp
위치에서 기능이 반환되지 않는 상황입니다 alloca
! 오히려 함수는 다음과 같이 컨텍스트를 저장합니다 setjmp
. 그런 다음 메모리를 할당 alloca
하고 마지막으로 longjmp가 해당 컨텍스트에 발생합니다. 이 함수의 alloca
메모리가 모두 해제되지는 않았습니다. 이후에 할당 한 모든 메모리 setjmp
. 물론 관찰 된 행동에 대해 이야기하고 있습니다. 그러한 요구 사항은 alloca
내가 아는 것으로 기록되어 있지 않습니다 .
문서의 초점은 일반적으로 alloca
메모리가 블록이 아닌 기능 활성화 와 관련되어 있다는 개념에 있습니다 . 여러 번 호출하면 alloca
더 많은 스택 메모리를 가져와 함수가 종료되면 모두 해제됩니다. 별로; 메모리는 실제로 프로 시저 컨텍스트와 연관됩니다. 로 컨텍스트가 복원 longjmp
되면 이전 alloca
상태도 복원됩니다 . 이는 스택 포인터 레지스터 자체가 할당에 사용되고 결과적으로에 저장 및 복원 된 결과 jmp_buf
입니다.
또한이 방법으로 작동하면에 할당 된 메모리를 고의적으로 해제하기위한 적절한 메커니즘을 제공합니다 alloca
.
나는 이것을 버그의 근본 원인으로 삼았습니다.
컴파일러가 함수의 스택 프레임 크기를 알 수 없기 때문에 함수에 alloca를 사용하면 함수에 적용될 수있는 일부 최적화가 방해되거나 비활성화됩니다.
예를 들어, C 컴파일러의 일반적인 최적화는 함수 내에서 프레임 포인터를 사용하지 않고 프레임 액세스가 스택 포인터를 기준으로하는 것입니다. 일반적인 사용을위한 레지스터가 하나 더 있습니다. 그러나 alloca가 함수 내에서 호출되면 sp와 fp의 차이를 함수의 일부에서 알 수 없으므로이 최적화를 수행 할 수 없습니다.
드물게 사용하고 표준 기능으로 그늘진 상태를 감안할 때, 컴파일러 디자이너 는 alloca와 함께 작동하도록 약간의 노력을 기울이면 alloca에 문제를 일으킬 수 있는 최적화를 비활성화 할 수 있습니다.
업데이트 : 가변 길이의 로컬 배열이 C 및 C ++에 추가되었으며 이러한 변수가 alloca와 같이 컴파일러에 매우 유사한 코드 생성 문제를 나타 내기 때문에 '사용의 희소성 및 그늘진 상태'가 기본 메커니즘에 적용되지 않습니다. ; 그러나 나는 여전히 alloca 또는 VLA를 사용하면 코드를 사용하는 함수 내에서 코드 생성을 손상시키는 경향이 있다고 생각합니다. 컴파일러 디자이너의 의견을 환영합니다.
슬프게도 정말 굉장한 alloca()
것이 거의 굉장한 tcc에서 빠져 있습니다. Gcc는 가지고 있습니다 alloca()
.
그것은 자신의 멸망의 씨를 뿌린다. 소멸자로 돌아온다.
마찬가지로
malloc()
MMU가있는 최신 시스템에서 segfault를 수행 할 때 잘못된 포인터를 반환합니다 (없으면 다시 시작하십시오).자동 변수와 달리 런타임시 크기를 지정할 수 있습니다.
재귀와 잘 작동합니다. 정적 변수를 사용하여 꼬리 재귀와 비슷한 것을 달성하고 다른 반복자를 사용하여 각 반복에 정보를 전달할 수 있습니다.
너무 세게 밀면 segfault가 보장됩니다 (MMU가있는 경우).
참고 malloc()
이벤트 더 이상이 시스템의 메모리가 부족하면 (할당 된 경우도 세그 폴트 것) NULL을 반환한다.하지 즉, 당신이 할 수있는 모든 보석 또는 그냥 방법을 할당하려고합니다.
사용하려면 malloc()
전역 을 사용 하고 NULL을 할당하십시오. 포인터가 NULL이 아닌 경우 사용하기 전에 해제합니다 malloc()
.
realloc()
기존 데이터를 복사하려는 경우 일반적인 경우로 사용할 수도 있습니다 . 를 복사하거나 연결하려는 경우 운동하기 전에 포인터를 확인해야합니다 realloc()
.
프로세스는 사용 가능한 메모리 양보다 훨씬 적은 제한된 양의 스택 공간 만 사용할 수 있습니다 malloc()
.
를 사용 alloca()
하면 스택 오버플로 오류가 발생할 가능성이 크게 높아집니다 (행운이 있거나 그렇지 않은 경우 설명 할 수없는 충돌).
아주 예쁘지는 않지만 성능이 정말로 중요하다면 스택의 일부 공간을 미리 할당 할 수 있습니다.
이미 메모리 블록의 최대 크기가 필요하고 오버플로 검사를 유지하려는 경우 다음과 같이 할 수 있습니다.
void f()
{
char array_on_stack[ MAX_BYTES_TO_ALLOCATE ];
SomeType *p = (SomeType *)array;
(...)
}
실제로 alloca는 스택 사용을 보장하지 않습니다. 실제로, alloca의 gcc-2.95 구현은 malloc 자체를 사용하여 힙에서 메모리를 할당합니다. 또한 구현이 버그가 있으므로 goto를 추가로 사용하여 블록 내에서 호출하면 메모리 누수가 발생하고 예기치 않은 동작이 발생할 수 있습니다. 당신이 그것을 사용해서는 안된다고 말하는 것은 아니지만, 때로는 alloca가 그것보다 더 많은 오버 헤드를 가져옵니다.
alloca 함수는 훌륭하고 모든 naysayer는 단순히 FUD를 퍼 뜨리고 있습니다.
void foo()
{
int x = 50000;
char array[x];
char *parray = (char *)alloca(x);
}
배열과 parray는 정확히 같은 위험과 동일합니다. 하나가 다른 것보다 낫다는 것은 기술적 인 선택이 아니라 구문상의 선택입니다.
스택 변수 대 힙 변수를 선택하는 경우 범위 내 수명이있는 변수에 대해 스택 오버 힙을 사용하는 장기 실행 프로그램에는 많은 이점이 있습니다. 힙 조각화를 피하고 사용되지 않는 (사용할 수없는) 힙 공간으로 프로세스 공간이 커지는 것을 피할 수 있습니다. 정리할 필요가 없습니다. 프로세스에서 스택 할당을 제어 할 수 있습니다.
왜 이것이 나쁜가요?
IMHO, alloca는 모두가 스택 크기 제한을 소진하는 것을 두려워하기 때문에 나쁜 습관으로 간주됩니다.
이 스레드와 다른 링크를 읽으면서 많은 것을 배웠습니다.
- https://unix.stackexchange.com/questions/63742/what-is-automatic-stack-expansion
- Linux 32 비트 시스템의 프로그램에 대한 스택 할당 한계
- ulimit -s
나는 alloca를 사용하여 변경없이 C89 스타일, #ifdef _MSC_VER 등이없는 평범한 C 파일을 msvc 및 gcc에서 컴파일 가능하게 만듭니다.
감사합니다 ! 이 스레드로 인해이 사이트에 가입하게되었습니다. :)
제 생각에 alloca ()는 가능한 경우 제한적인 방식으로 만 사용해야합니다. "goto"를 사용하는 것과 마찬가지로, 합리적으로 많은 사람들이 alloca ()를 사용하는 것뿐만 아니라 alloca ()의 존재를 강력하게 혐오합니다.
임베디드 사용의 경우 스택 크기가 알려져 있고 할당 크기에 대한 규칙 및 분석을 통해 제한을 적용 할 수 있으며 C99 +를 지원하도록 컴파일러를 업그레이드 할 수없는 경우 alloca () 사용이 좋습니다. 그것을 사용하는 것으로 알려져 있습니다.
사용 가능한 경우 VLA는 alloca ()에 비해 몇 가지 장점이 있습니다. 컴파일러는 배열 스타일 액세스가 사용될 때 범위를 벗어난 액세스를 잡을 스택 제한 검사를 생성 할 수 있습니다 (컴파일러가이 작업을 수행하는지는 모르겠지만 코드를 분석하면 배열 액세스 표현식이 올바르게 바인드되었는지 판별 할 수 있습니다. 자동차, 의료 장비 및 항공 전자 공학과 같은 일부 프로그래밍 환경에서이 분석은 자동 (스택) 및 정적 할당 (전역 또는 로컬)의 고정 크기 어레이에 대해서도 수행해야합니다.
스택에 데이터와 리턴 주소 / 프레임 포인터를 모두 저장하는 아키텍처에서 (내가 아는 바로는 전부입니다) 변수의 주소를 가져올 수 있기 때문에 스택 할당 변수는 위험 할 수 있으며 검사되지 않은 입력 값은 허용 될 수 있습니다 모든 종류의 장난.
이식성은 임베드 된 공간에서 큰 문제가되지 않지만 신중하게 제어되는 환경 이외의 alloca () 사용에 대한 좋은 주장입니다.
임베디드 공간 밖에서 나는 효율성을 위해 로깅 및 포맷 함수 내부에서 alloca ()를 사용했으며 비 재귀 어휘 스캐너에서 토큰 화 및 분류 중에 임시 구조 (alloca ()를 사용하여 할당 됨)가 사용되었습니다. 함수가 반환되기 전에 malloc ()을 통해 할당 된 객체가 채워지며, 더 작은 임시 구조에 alloca ()를 사용하면 영구 객체가 할당 될 때 조각화가 크게 줄어 듭니다.
여기에서 대부분의 대답은 크게 요점을 놓 _alloca()
칩니다. 스택에 큰 객체를 저장하는 것보다 사용 이 잠재적으로 더 나쁜 이유 가 있습니다.
자동 스토리지와의 주요 차이점 _alloca()
은 후자가 추가적인 (심각한) 문제를 겪고 있다는 것입니다. 할당 된 블록은 컴파일러에 의해 제어되지 않으므로 컴파일러가이를 최적화하거나 재활용 할 수있는 방법이 없습니다.
비교:
while (condition) {
char buffer[0x100]; // Chill.
/* ... */
}
와:
while (condition) {
char* buffer = _alloca(0x100); // Bad!
/* ... */
}
후자의 문제는 명백해야합니다.
나는 아무도 이것을 언급하지 않았다고 생각하지만, alloca는 malloc과 함께 반드시 존재하지 않는 심각한 보안 문제를 가지고 있습니다 (이러한 문제는 스택 기반 배열에서 발생하지만 동적이든 아니든 상관 없습니다). 메모리가 스택에 할당되므로 버퍼 오버플로 / 언더 플로는 malloc보다 훨씬 심각한 결과를 초래합니다.
특히 함수의 반환 주소는 스택에 저장됩니다. 이 값이 손상되면 코드가 실행 가능한 메모리 영역으로 이동하도록 만들 수 있습니다. 컴파일러는 특히 주소 레이아웃을 무작위 화하여이를 어렵게 만들기 위해 많은 노력을 기울입니다. 그러나 반환 값이 손상된 경우 가장 좋은 경우 SEGFAULT이기 때문에 스택 오버플로보다 분명히 나쁘지만 임의의 메모리 조각 또는 최악의 경우 프로그램의 보안을 손상시키는 메모리 영역이 실행될 수 있습니다. .
참고 URL : https://stackoverflow.com/questions/1018853/why-is-the-use-of-alloca-not-considered-good-practice
'Programing' 카테고리의 다른 글
CMake의 디버그 및 릴리스 (0) | 2020.02.26 |
---|---|
어떤 MVC 버전을 사용하고 있습니까? (0) | 2020.02.26 |
Oracle에서 AUTO_INCREMENT로 ID를 만드는 방법은 무엇입니까? (0) | 2020.02.26 |
Rails 3.1에서 자산 파이프 라인 (스프로킷) 메시지 로깅을 비활성화하는 방법은 무엇입니까? (0) | 2020.02.26 |
Android 앱의 릴리스 버전을 빌드하기 전에 모든 디버그 로깅 호출을 제거하는 방법은 무엇입니까? (0) | 2020.02.26 |