문제

C ++에서 로컬 변수는 항상 스택에 할당됩니다. 스택은 응용 프로그램이 차지할 수있는 허용 메모리의 일부입니다. 그 메모리는 당신의 RAM에 보관됩니다 (디스크로 교체하지 않은 경우). 이제 C ++ 컴파일러가 항상 스택에 로컬 변수를 저장하는 어셈블러 코드를 생성합니까?

예를 들어 다음 간단한 코드를 사용하십시오.

int foo( int n ) {
   return ++n;
}

MIPS 어셈블러 코드에서는 다음과 같습니다.

foo:
addi $v0, $a0, 1
jr $ra

보시다시피, 나는 n에 스택을 전혀 사용할 필요가 없었습니다. C ++ 컴파일러가이를 인식하고 CPU의 레지스터를 직접 사용합니까?

편집하다: 와우, 거의 즉각적이고 광범위한 답변에 감사드립니다! Foo의 기능 본문은 물론이어야합니다 return ++n;, 아니다 return n++;. :)

도움이 되었습니까?

해결책

면책 조항 : 나는 MIP를 모르지만 X86을 알고 있으며 원칙이 동일해야한다고 생각합니다.

일반적인 기능 호출 규칙에서 컴파일러는 n 스택에 기능으로 전달합니다 foo. 그러나 fastcall GCC에게 레지스터를 통해 값을 전달하도록 지시하는 데 사용할 수있는 협약. (MSVC는이 옵션도 있지만 구문이 무엇인지 잘 모르겠습니다.)

test.cpp:

int foo1 (int n) { return ++n; }
int foo2 (int n) __attribute__((fastcall));
int foo2 (int n) {
    return ++n;
}

위를 컴파일합니다 g++ -O3 -fomit-frame-pointer -c test.cpp, 나는 간다 foo1:

mov eax,DWORD PTR [esp+0x4]
add eax,0x1
ret

보시다시피, 그것은 스택에서 값을 읽습니다.

그리고 여기 있습니다 foo2:

lea eax,[ecx+0x1]
ret

이제 레지스터에서 직접 값을 가져옵니다.

물론, 기능을 인라인으로 인라인하면 컴파일러가 지정한 통화 규칙에 관계없이 더 큰 기능의 본문에 간단한 추가 기능을 수행합니다. 그러나 당신이 그것을 내려 놓을 수 없을 때, 이것은 일어날 것입니다.

면책 조항 2 : 나는 당신이 컴파일러를 지속적으로 두 번째로 추측해야한다고 말하지 않습니다. 대부분의 경우에는 실용적이지 않고 필요하지 않을 것입니다. 그러나 완벽한 코드를 생성한다고 가정하지 마십시오.

편집 1 : 평범한 로컬 변수 (함수 인수가 아님)에 대해 이야기하는 경우, 그렇습니다. 컴파일러는 레지스터 나 스택에 맞는 것으로 보일 것입니다.

편집 2 : Richard Pennington이 그의 대답에서 언급했듯이, Calling Convention은 건축 양식이며 MIPS는 스택에 처음 네 가지 논쟁을 통과 할 것입니다. 따라서 귀하의 경우 추가 속성을 지정할 필요가 없습니다 (실제로 x86 별 속성입니다.)

다른 팁

예. "변수가 항상 스택에 할당된다"는 규칙은 없습니다. C ++ 표준은 스택에 대해 아무 말도하지 않습니다. 스택이 존재하거나 레지스터가 존재한다고 가정하지 않습니다. 코드가 어떻게 구현되어야하는지가 아니라 어떻게 행동 해야하는지 말해줍니다.

컴파일러는 스택에 변수를 스택에 저장해야합니다. 예를 들어 함수 호출을 지나야하거나 주소를 사용하려고 할 때.

컴파일러는 바보가 아닙니다. ;)

예, C/C ++를 최적화하면 최적화됩니다. 그리고 심지어 많이 더: Felix von Leitners 컴파일러 설문 조사를 참조하십시오.

일반적인 C/C ++ 컴파일러가 모든 변수를 스택에 넣지는 않습니다. 당신의 문제 foo() 함수는 변수가 스택을 통해 함수로 전달 될 수 있다는 것일 수 있습니다 (시스템의 ABI (하드웨어/OS)는이를 정의합니다).

C와 함께 register 키워드 컴파일러를 제공 할 수 있습니다 힌트 레지스터에 변수를 저장하는 것이 좋을 것입니다. 견본:

register int x = 10;

그러나 기억하십시오 : 컴파일러는 무료입니다. x 원하는 경우 레지스터로!

대답은 그렇습니다. 컴파일러, 최적화 수준 및 대상 프로세서에 따라 다릅니다.

MIPS의 경우, 작은 경우 첫 4 개의 매개 변수가 레지스터로 전달되고 리턴 값이 레지스터에서 반환됩니다. 따라서 예제는 스택에 아무것도 할당 할 필요가 없습니다.

사실, 진실은 소설보다 낯선 사람입니다. 귀하의 경우 매개 변수는 변경되지 않은 채 반환됩니다. 반환 된 값은 ++ 연산자 이전의 N의 값입니다.

foo:
    .frame  $sp,0,$ra
    .mask   0x00000000,0
    .fmask  0x00000000,0

    addu    $2, $zero, $4
    jr      $ra
    nop

당신의 모범 이후 foo 함수는 ID 기능 (인수를 반환합니다)이며 C ++ 컴파일러 (VS 2008)는이 기능 호출을 완전히 제거합니다. 변경하면 :

int foo( int n ) {
   return ++n;
}

컴파일러는 이것을 상인합니다

lea edx, [eax+1] 

예, 레지스터는 C ++로 사용됩니다. MDR (메모리 데이터 레지스터)에는 페치 및 저장된 데이터가 포함되어 있습니다. 예를 들어, 셀 (123)의 내용을 검색하기 위해, 우리는 값 (123)을 3 월에로드하고 페치 작업을 수행합니다. 작업이 완료되면 셀 123의 내용 사본이 MDR에있을 것입니다. 값 98을 셀 4에 저장하려면 4를 3 월에, 98을 MDR에로드하고 매장을 수행합니다. 작업이 완료되면 셀 4의 내용물은 이전에있는 모든 것을 폐기하여 98로 설정되었습니다. 데이터 및 주소 레지스터는이를 달성하기 위해 그들과 함께 작동합니다. C ++에서도 값으로 Var를 초기화하거나 그 가치를 물으면 동일한 현상이 발생합니다.

그리고 한 가지 더, 최신 컴파일러는 메모리 할당보다 더 빠른 레지스터 할당을 수행합니다.

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