programing

C에서는 브래킷이 스택프레임으로 기능합니까?

copyandpastes 2022. 7. 30. 21:07
반응형

C에서는 브래킷이 스택프레임으로 기능합니까?

새로운 곱슬 괄호 세트 내에 변수를 작성하면 해당 변수는 닫힘 괄호 위의 스택에서 튀어나온 것입니까, 아니면 함수가 끝날 때까지 계속됩니까?예를 들어 다음과 같습니다.

void foo() {
   int c[100];
   {
       int d[200];
   }
   //code that takes a while
   return;
}

d을 더듬고 있다code that takes a while섹??

변수가 실제로 메모리를 사용하는 시간은 컴파일러에 따라 다릅니다(많은 컴파일러는 함수 내에서 내부 블록이 입력 및 종료될 때 스택포인터를 조정하지 않습니다).

그러나 밀접하게 관련되어 있지만 더 흥미로운 질문은 프로그램이 내부 범위 밖(그러나 포함 기능 내)에서 그러한 내부 개체에 접근하는 것이 허용되는지 여부이다.

void foo() {
   int c[100];
   int *p;

   {
       int d[200];
       p = d;
   }

   /* Can I access p[0] here? */

   return;
}

(즉, 컴파일러는 할당 해제를 허용합니까?d(이것들)

정답은 컴파일러가 할당 해제를 허용하는 것입니다.d에의 액세스p[0]여기서 코멘트는 정의되지 않은 동작을 나타냅니다(프로그램은 내부 스코프 외부의 내부 개체에 액세스할 수 없습니다).C 표준의 관련 부분은 6.2.4p5이다.

가변 길이 배열 유형을 가지지 않는 오브젝트[자동 저장 기간을 가지는 오브젝트]의 경우, 관련 붙여진 블록의 엔트리로부터 그 블록의 실행이 어떤 식으로든 종료될 때까지의 라이프 타임이 연장됩니다.(봉쇄된 블록에 들어가거나 함수를 호출하면 현재 블록의 실행이 중단되지만 종료되지는 않습니다.)블록이 재귀적으로 입력될 경우 객체의 새 인스턴스가 매번 생성됩니다.개체의 초기 값이 불확실합니다.오브젝트에 대해 초기화가 지정되어 있는 경우 블록 실행 시 선언에 도달할 때마다 값이 실행됩니다.그렇지 않으면 선언에 도달할 때마다 값이 미확정 상태가 됩니다.

아니요, 괄호는 스택프레임으로 동작하지 않습니다.C에서 중괄호는 명명 범위만 나타내지만 컨트롤이 스택에서 빠져나갔을 때 스택에서 삭제되거나 팝업되는 것은 없습니다.

코드를 작성하는 프로그래머로서 스택프레임인 것처럼 생각할 수 있습니다.대괄호 내에서 선언된 식별자는 대괄호 내에서만 액세스할 수 있으므로 프로그래머 입장에서는 선언된 대로 스택에 푸시되고 스코프가 종료될 때 팝되는 것과 같습니다.단, 컴파일러는 입력/종료 시 어떤 것도 푸시/팝하는 코드를 생성할 필요가 없습니다(또한 일반적으로는 생성되지 않습니다).

또한 로컬 변수는 스택스페이스를 전혀 사용하지 않을 수 있습니다.이러한 변수는 CPU 레지스터 또는 기타 보조 스토리지 위치에 보관되어 있거나 완전히 최적화되어 있을 수 있습니다.

그...d어레이는 이론적으로 기능 전체에 대해 메모리를 소비할 수 있습니다.그러나 컴파일러는 메모리를 최적화하거나 사용 수명이 겹치지 않는 다른 로컬 변수와 메모리를 공유할 수 있습니다.

당신의 질문은 명확하게 대답할 수 있을 만큼 명확하지 않다.

한편 컴파일러는 보통 네스트된 블록스코프에 대해 로컬메모리 할당 및 할당 해제를 하지 않습니다.로컬 메모리는 일반적으로 기능 시작 시 한 번만 할당되고 기능 종료 시 해제됩니다.

한편 로컬 객체의 수명이 끝나면 해당 객체가 사용한 메모리는 나중에 다른 로컬 객체에 재사용할 수 있습니다.예를 들어 이 코드에서는

void foo()
{
  {
    int d[100];
  }
  {
    double e[20];
  }
}

영역을 즉, 함수에 .foo두 어레이 중 가장 어레이에 필요한 모든 것을 동시에 사용할 수 없습니다.

가 「」로서 합니다.d질문의 맥락에서 기능이 끝날 때까지 메모리를 계속 점유하는 것은 당신이 결정할 일입니다.

그럴 수도 있어요.안 그럴 수도 있어요.당신에게 정말 필요한 대답은 아무것도 추측하지 말라는 것입니다.최신 컴파일러는 모든 종류의 아키텍처와 구현 고유의 마법을 수행합니다.코드를 간단하고 읽기 쉽게 작성하여 컴파일러가 좋은 일을 하게 합니다.컴파일러를 중심으로 코드를 작성하려고 하면 트러블이 발생합니다.이러한 상황에서 발생하는 트러블은 보통 매우 미묘하고 진단하기 어렵습니다.

" "d는 일반적으로 스택에서 팝업되지 않습니다.중괄호는 스택프레임을 나타내지 않습니다.그렇지 않으면 다음과 같은 작업을 수행할 수 없습니다.

char var = getch();
    {
        char next_var = var + 1;
        use_variable(next_char);
    }

(을 일으킨 , 는 컴파일 는 괄호 가 변수(''/')에 액세스 할 수 때문입니다.var(서브 함수가 호출 함수의 변수에 직접 액세스할 수 없는 것과 마찬가지로) 중괄호 외부에 존재합니다.우리는 이것이 사실이 아니라는 것을 안다.

곱슬괄호는 단순히 스코핑에 사용됩니다.컴파일러는 괄호 외부에서 "내부" 변수에 대한 모든 액세스를 무효로 간주하고 메모리를 다른 용도로 재사용할 수 있습니다(이는 구현에 의존합니다).단, 인클로징 함수가 반환될 때까지 스택에서 팝업되지 않을 수 있습니다.

업데이트: C사양에 대해 설명하겠습니다.자동 저장 기간이 있는 물체에 대하여(섹션 6.4.2):

가변 길이 배열 유형이 없는 객체의 경우 해당 블록의 실행이 종료될 때까지 해당 개체의 라이프타임이 연관된 블록의 엔트리부터 연장됩니다.

같은 섹션에서는 "라이프 타임"이라는 용어를 다음과 같이 정의합니다(강조).

개체의 수명이란 프로그램 실행 중 저장공간이 예약되는 부분을 말합니다.객체는 존재하며 주소가 일정하며 수명이 다할 때까지 마지막으로 저장된 값을 유지합니다.오브젝트가 라이프 타임을 벗어나 참조되는 경우 동작은 정의되지 않습니다.

여기서의 키워드는, 물론 「보증」입니다.내부 중괄호 집합의 범위를 벗어나면 어레이의 수명이 끝납니다.스토리지가 할당되어 있을 수도 있고 할당되어 있지 않을 수도 있습니다(컴파일러가 공간을 다른 용도로 재사용할 수도 있습니다). 그러나 어레이에 액세스하려고 하면 정의되지 않은 동작이 발생하여 예측할 수 없는 결과가 발생합니다.

C 사양에는 스택프레임의 개념이 없습니다.이 명령어는 결과 프로그램이 어떻게 동작할지에 대해서만 설명하며, 구현에 대한 자세한 내용은 컴파일러에게 맡깁니다(결국 스택리스 CPU에서의 구현은 하드웨어 스택이 있는 CPU에서의 구현과는 상당히 다릅니다).C 사양에는 스택프레임의 종료처와 종료하지 않는 장소를 규정하는 것은 없습니다.실제로 알 수 있는 유일한 방법은 특정 컴파일러/플랫폼에서 코드를 컴파일하여 결과 어셈블리를 조사하는 입니다.컴파일러의 현재 최적화 옵션 세트도 이에 영향을 미칠 수 있습니다.

가 "" " " " " 를 확실하게 d는, 코드.따라서, 할 수도 있고, 으로 변환할 수도 .따라서, 곱슬곱슬한 괄호 안의 코드를 다른 함수로 변환할 수도 있고, 명시적으로 변환할 수도 있습니다.malloc그리고.free자동 스토리지를 사용하는 대신 메모리를 사용합니다.

구현에 따라 다릅니다.gcc 4.3.4의 기능을 테스트하기 위해 짧은 프로그램을 작성했는데, 기능 시작 시 모든 스택 공간을 한 번에 할당해 줍니다.-S 플래그를 사용하여 gcc가 생성하는 어셈블리를 검사할 수 있습니다.

아니요, d[]는 나머지 루틴에서는 스택에 없습니다.그러나 alloca()는 다릅니다.

편집: Kristopher Johnson(그리고 Simon과 Daniel)은 옳고, 저의 초기 반응은 틀렸습니다.CYGWIN의 gcc 4.3.4.에서는 코드는 다음과 같습니다.

void foo(int[]);
void bar(void);
void foobar(int); 

void foobar(int flag) {
    if (flag) {
        int big[100000000];
        foo(big);
    }
    bar();
}

다음과 같은 기능이 있습니다.

_foobar:
    pushl   %ebp
    movl    %esp, %ebp
    movl    $400000008, %eax
    call    __alloca
    cmpl    $0, 8(%ebp)
    je      L2
    leal    -400000000(%ebp), %eax
    movl    %eax, (%esp)
    call    _foo
L2:
    call    _bar
    leave
    ret

살아서 배워라!간단한 테스트 결과, AndreyT도 여러 할당에 대해 올바른 것으로 나타났습니다.

나중에 추가:위의 테스트에서는 gcc 문서가 정확하지 않음을 알 수 있습니다.수년간 다음과 같이 말해 왔다(강조사항 추가):

"어레이 이름의 범위가 끝나는 즉시 가변 길이 어레이의 공간이 할당 해제됩니다."

범위를 벗어나지만 함수가 돌아올 때까지 스택에서 팝업되지 않습니다.따라서 함수가 완료될 때까지 스택의 메모리를 계속 사용하지만 첫 번째 닫는 컬리 브레이스 다운스트림에는 액세스할 수 없습니다.

실제로 구현에 특화되어 있음을 나타내는 표준에는 이미 많은 정보가 제공되고 있습니다.

한 가지 실험이 흥미로울 수 있습니다.다음 코드를 시도하면:

#include <stdio.h>
int main() {
    int* x;
    int* y;
    {
        int a;
        x = &a;
        printf("%p\n", (void*) x);
    }
    {
        int b;
        y = &b;
        printf("%p\n", (void*) y);
    }
}

gcc를 사용하면 여기서 같은 주소를 2배로 얻을 수 있습니다.코리로

하지만 다음 코드를 시도하면:

#include <stdio.h>
int main() {
    int* x;
    int* y;
    {
        int a;
        x = &a;
    }
    {
        int b;
        y = &b;
    }
    printf("%p\n", (void*) x);
    printf("%p\n", (void*) y);
}

gcc를 사용하면 여기서2개의 다른 주소를 얻을 수 있습니다.코리로

그래서 무슨 일이 일어나고 있는지 확신할 수 없다.

언급URL : https://stackoverflow.com/questions/2759371/in-c-do-braces-act-as-a-stack-frame

반응형