스택은 위쪽으로 증가합니까, 아니면 아래로 증가합니까?
c에는 다음과 같은 코드가 있습니다.
int q = 10;
int s = 5;
int a[3];
printf("Address of a: %d\n", (int)a);
printf("Address of a[1]: %d\n", (int)&a[1]);
printf("Address of a[2]: %d\n", (int)&a[2]);
printf("Address of q: %d\n", (int)&q);
printf("Address of s: %d\n", (int)&s);
출력은 다음과 같습니다.
Address of a: 2293584
Address of a[1]: 2293588
Address of a[2]: 2293592
Address of q: 2293612
Address of s: 2293608
그래서 저는 그걸a로.a[2]메모리 주소는 각각4바이트씩 증가합니다.하지만 부터q로.s메모리 주소는 4바이트 감소합니다.
두 가지 궁금한 점이 있습니다.
- 스택은 위아래로 늘어나나요? (이 경우는 양쪽 모두로 보입니다)
- 사이에 무슨 일이 일어나다
a[2]그리고.q메모리 주소?왜 거기엔 기억력이 많이 차이가 나죠?(20 바이트).
주의: 이것은 숙제 질문이 아닙니다.스택이 어떻게 동작하는지 궁금해요.도와주셔서 감사합니다.
스택의 동작(성장 또는 성장)은 Application Binary Interface(ABI; 응용 프로그램바이너리 인터페이스) 및 콜스택(액티베이션레코드라고도 불립니다)의 편성 방법에 따라 달라집니다.
그 수명 동안 프로그램은 OS와 같은 다른 프로그램과 통신하게 되어 있습니다.ABI는 프로그램이 다른 프로그램과 통신하는 방법을 결정합니다.
서로 다른 아키텍처의 스택은 어느 쪽이든 확장할 수 있지만 아키텍처의 경우 일관성이 있습니다.이 Wiki 링크를 확인하십시오.그러나 스택의 성장은 해당 아키텍처의 ABI에 의해 결정됩니다.
예를 들어 MIPS ABI를 사용하는 경우 콜스택은 다음과 같이 정의됩니다.
함수 'fn1'이 'fn2'를 호출하는 것을 생각해 봅시다.다음으로 'fn2'로 표시되는 스택프레임은 다음과 같습니다.
direction of | |
growth of +---------------------------------+
stack | Parameters passed by fn1(caller)|
from higher addr.| |
to lower addr. | Direction of growth is opposite |
| | to direction of stack growth |
| +---------------------------------+ <-- SP on entry to fn2
| | Return address from fn2(callee) |
V +---------------------------------+
| Callee saved registers being |
| used in the callee function |
+---------------------------------+
| Local variables of fn2 |
|(Direction of growth of frame is |
| same as direction of growth of |
| stack) |
+---------------------------------+
| Arguments to functions called |
| by fn2 |
+---------------------------------+ <- Current SP after stack
frame is allocated
이제 스택이 아래로 늘어나는 것을 볼 수 있습니다.따라서 변수가 함수의 로컬프레임에 할당되면 변수의 주소는 실제로 아래로 커집니다.컴파일러는 메모리 할당을 위한 변수 순서를 결정할 수 있습니다.(이 경우 최초로 스택메모리를 할당하는 것은 'q' 또는 's' 중 하나입니다.그러나 일반적으로 컴파일러는 변수 선언 순서에 따라 스택 메모리 할당을 수행합니다).
단, 어레이의 경우 할당 포인터는 1개뿐이며 메모리를 할당해야 하는 포인터는 1개입니다.어레이의 메모리는 연속해 둘 필요가 있습니다.따라서 스택은 하향으로 증가하지만 어레이의 경우 스택이 증가합니다.
이건 사실 두 가지 질문입니다.하나는 어떤 함수가 다른 함수를 호출할 때(새로운 프레임이 할당되었을 때) 스택이 커지는 방법에 대한 것이고, 다른 하나는 특정 함수의 프레임에 변수가 배치되는 방법에 대한 것입니다.
둘 다 C 표준으로 지정되어 있지 않지만 답은 조금 다릅니다.
- 새로운 프레임이 할당되었을 때 스택은 어느 방향으로 커집니까?함수 f()가 함수 g()를 호출하면의 프레임 포인터는 의 프레임 포인터보다 커집니까?이것은 어느 쪽이든 가능합니다.특정 컴파일러와 아키텍처에 따라 다르지만('호출 규칙'을 참조), 특정 플랫폼 내에서 항상 일관성이 있습니다(몇 가지 이상한 예외는 있지만 코멘트 참조).하향은 x86, PowerPC, MIPS, SPARC, EE 및 Cell SPU에서 흔히 볼 수 있습니다.
- 함수의 로컬 변수는 스택 프레임 내에 어떻게 배치됩니까?이것은 미지정이며 전혀 예측할 수 없습니다.컴파일러는 로컬 변수를 자유롭게 배열할 수 있지만 가장 효율적인 결과를 얻기를 원합니다.
아키텍처에 따라 다릅니다.자신의 시스템을 확인하려면 GeeksForGeeks의 다음 코드를 사용합니다.
// C program to check whether stack grows
// downward or upward.
#include<stdio.h>
void fun(int *main_local_addr)
{
int fun_local;
if (main_local_addr < &fun_local)
printf("Stack grows upward\n");
else
printf("Stack grows downward\n");
}
int main()
{
// fun's local variable
int main_local;
fun(&main_local);
return 0;
}
이는 메모리 내의 데이터 세트에 관한 little endian byte order standard에 의한 것입니다.
메모리를 위에서 0부터, 아래에서 최대까지 보면 스택이 위쪽으로 늘어납니다.
스택이 아래로 커지는 이유는 스택 또는 베이스 포인터의 관점에서 참조를 해제할 수 있기 때문입니다.
모든 유형의 비참조는 가장 낮은 주소에서 가장 높은 주소로 증가합니다.스택은 하향(가장 높은 주소에서 가장 낮은 주소까지) 증가하므로 스택을 동적 메모리와 같이 취급할 수 있습니다.
이것이 많은 프로그래밍 및 스크립팅 언어가 레지스터 기반이 아닌 스택 기반 가상 시스템을 사용하는 이유 중 하나입니다.
x86에서 스택프레임의 메모리 "할당"은 단순히 스택 포인터에서 필요한 바이트 수를 빼는 것으로 구성됩니다(다른 아키텍처도 비슷하다고 생각합니다).이 점에서 스택은 「다운」이라고 생각됩니다.스택을 깊게 호출할수록 주소는 점점 작아집니다(그러나, 메모리 상단의 왼쪽 상단에서0부터 시작하여 오른쪽에서 랩 다운할수록 큰 주소를 얻을 수 있기 때문에, 제 머릿속 이미지에서는 스택이 커집니다.선언된 변수의 순서는 주소와는 관계가 없을 수 있습니다.부작용을 일으키지 않는 한 컴파일러가 그것들을 재정렬할 수 있다고 생각합니다(틀렸다면 정정해 주세요).스택 포인터에서 바이트 수를 뺄 때 생성된 사용된 주소의 갭 어딘가에 끼어 있을 뿐입니다.
배열 주위에 있는 틈새들은 일종의 패딩일 수도 있지만, 나에게는 불가사의하다.
나는 그것이 그렇게 결정적이라고 생각하지 않는다.어레이는 메모리가 연속적으로 할당되어야 하기 때문에 "확장"하는 것처럼 보입니다.단, q와 s는 서로 전혀 관련되지 않기 때문에 컴파일러는 스택 내의 임의의 빈 메모리 위치(아마도 정수 사이즈에 가장 적합한 메모리 위치)에 각각 고정합니다.
a[2]와 q 사이에 일어난 일은 q의 위치 주변의 공간이 3개의 정수 배열을 할당하기에 충분히 크지 않았다는 것입니다(즉, 12바이트를 넘지 않았습니다.
어떤 스택이 확장되는지는 아키텍처에 따라 다릅니다.단, 스택을 확장할 수 있는 하드웨어 아키텍처는 극히 일부에 불과하다고 생각합니다.
스택이 커지는 방향은 개별 객체의 레이아웃과는 무관합니다.따라서 스택은 축소될 수 있지만 어레이는 축소되지 않습니다(즉, &array[n]는 항상 < &array[n+1]).
컴파일러는 로컬(자동) 변수를 로컬스택 프레임의 임의의 위치에 자유롭게 할당할 수 있습니다.이것만으로 스택의 성장 방향을 확실하게 추론할 수 없습니다.스택 확장 방향은 네스트된 스택프레임의 주소를 비교하는 것, 즉 함수의 스택프레임 내의 로컬 변수 주소를 콜처와 비교함으로써 추론할 수 있습니다.
#include <stdio.h>
int f(int *x)
{
int a;
return x == NULL ? f(&a) : &a - x;
}
int main(void)
{
printf("stack grows %s!\n", f(NULL) < 0 ? "down" : "up");
return 0;
}
운영 체제와 컴파일러에 따라 다릅니다.
표준에는 스택상의 구성 방식을 규정하는 것은 전혀 없습니다.실제로 어레이 요소를 스택의 인접 요소에 전혀 저장하지 않는 컴파일러를 구축할 수 있습니다.이 컴파일러가 어레이 요소 연산을 제대로 수행할 수 있는 스마트 기능을 가지고 있다면 (예를 들어 a1이 a[0]에서 1K 떨어져 있고 이를 조정할 수 있다는 것을 알 수 있습니다).
다른 결과를 얻을 수 있는 이유는 스택이 축소되어 "개체"를 추가할 수 있는 반면 어레이는 단일 "개체"이며 반대 순서로 배열 요소가 있을 수 있기 때문입니다.그러나 이러한 동작에 의존하는 것은 안전하지 않습니다. 왜냐하면 다음과 같은 다양한 이유로 인해 방향이 바뀌고 변수가 바뀔 수 있기 때문입니다.
- 최적화
- 얼라인먼트
- 컴파일러의 스택 관리 파트 담당자의 변덕.
스택 방향에 대한 뛰어난 내용은 여기를 참조하십시오:-)
구체적인 질문에 대한 답변:
- 스택은 상승 또는 하강 중 어느 쪽입니까?
(표준에 관해서는) 전혀 문제가 되지 않습니다만, 고객의 요구에 따라서, 실장에 따라서는 메모리의 증설과 축소가 가능합니다. - a [2]와 q 메모리주소는 어떻게 됩니까?왜 거기엔 기억력이 많이 차이가 나죠?(20바이트)
그것은 전혀 중요하지 않다.)생각할 수 있는 이유에 대해서는, 상기를 참조해 주세요.
우선 메모리 내 미사용 공간 8바이트(12바이트가 아님, 스택을 기억하라)는 것입니다.따라서 할당되지 않은 공간은 604~597입니다.그리고 왜?왜냐하면 모든 데이터 타입은 그 크기에 따라 나누어질 수 있는 주소부터 메모리의 공간을 차지하기 때문입니다.이 경우 3개의 정수로 구성된 배열은 12바이트의 메모리 공간을 사용하며 604는 12로 나눌 수 없습니다.따라서 메모리 주소가 12로 나누어져 596이 될 때까지 빈칸을 남깁니다.
따라서 어레이에 할당된 메모리 용량은 596 ~584입니다그러나 어레이 할당이 계속되기 때문에 어레이의 첫 번째 요소는 596이 아니라 584 주소에서 시작됩니다.
스택이 번호가 작은 주소로 확장되어 있는 것 같습니다.
다른 컴퓨터나 다른 컴파일러 호출을 사용하는 경우 내 컴퓨터에서는 다를 수 있습니다.또는 컴파일러는 스택을 전혀 사용하지 않기로 선택했습니다(모든 것을 인라인(주소를 받지 않은 경우 함수 및 변수).
$ cat stack.c
#include <stdio.h>
int stack(int x) {
printf("level %d: x is at %p\n", x, (void*)&x);
if (x == 0) return 0;
return stack(x - 1);
}
int main(void) {
stack(4);
return 0;
}
$ /usr / bin / gcc - Wall - Wextra - std = c89 - pedantic stack 。c
$/a. 출력레벨 4: x는 0x7ff7781190c에 있습니다.레벨 3: x는 0x7ff778118ec에 있습니다.레벨 2: x는 0x7ff778118cc레벨 1: x는 0x7ff778118ac에 있습니다.레벨 0: x는 0x7ff778118c에 있습니다.
스택이 다운됩니다(x86).단, 함수가 로드될 때 스택은 1개의 블록에 할당되며 스택 상의 항목이 어떤 순서로 정렬되는지 보증할 수 없습니다.
이 경우 스택에 2개의 int와1개의 3개의 int 배열을 위한 공간을 할당했습니다.또한 어레이 뒤에 12바이트가 더 할당되어 있기 때문에 다음과 같습니다.
a [12 바이트]
padding(?) [12바이트]
s [4바이트]
q [4바이트]
어떤 이유로든 컴파일러는 이 함수에 32바이트를 할당할 필요가 있다고 판단했습니다.또, 그 이상의 경우도 있습니다.C프로그래머로서 불명확한 일입니다만, 그 이유는 알 수 없습니다.
그 이유를 알고 싶다면 어셈블리 언어로 코드를 컴파일해 주세요.저는 MS의 C 컴파일러에서는 -S, gcc에서는 -S라고 생각합니다.해당 함수의 시작 지침을 보면 이전 스택 포인터가 저장되고 32(또는 다른 것!)가 제거되는 것을 볼 수 있습니다.거기서, 코드가 32 바이트의 메모리 블록에 어떻게 액세스 해, 컴파일러가 무엇을 하고 있는지를 알 수 있습니다.기능의 마지막에, 복원중의 스택 포인터를 확인할 수 있습니다.
스택이 다운된다.따라서 f(g(h)), h에 할당된 스택은 g보다 낮은 주소에서 시작되며 g와 g는 f보다 낮아집니다.그러나 스택 내의 변수는 C 사양을 따라야 합니다.
http://c0x.coding-guidelines.com/6.5.8.html
1206 가리키는 객체가 동일한 집약 객체의 멤버인 경우 구조체 멤버에 대한 포인터는 구조체 내에서 이전에 선언된 멤버에 대한 포인터보다 더 큰 비교이며, 더 큰 서브스크립트 값을 가진 배열 요소에 대한 포인터는 더 큰 포인터보다 낮은 서브스크립트 값을 가진 동일한 배열의 요소를 비교합니다.
&a[0] < &a[1]는 'a'의 할당 방법에 관계없이 항상 참이어야 합니다.
언급URL : https://stackoverflow.com/questions/1677415/does-stack-grow-upward-or-downward
'source' 카테고리의 다른 글
| C는 과부하를 지원합니까? (0) | 2022.07.30 |
|---|---|
| 자바에서 소수점 2자리 플로트를 인쇄하려면 어떻게 해야 하나요? (0) | 2022.07.30 |
| 플랫폼에 의존하지 않는 size_t 형식 지정자(c) (0) | 2022.07.30 |
| Eclipse에서 Javadoc 의견을 생성하려면 어떻게 해야 합니까? (0) | 2022.07.30 |
| VueJS 딥워처 - 여러 객체의 특정 속성 (0) | 2022.07.28 |