정적 변수는 C 및 C++ 어디에 저장됩니까?
어떤 세그먼트(.실행 파일의 BSS, .DATA, other)는 이름 충돌을 방지하기 위해 저장된 정적 변수입니까?예를 들어 다음과 같습니다.
foo.c: bar.c:
static int foo = 1; static int foo = 10;
void fooTest() { void barTest() {
static int bar = 2; static int bar = 20;
foo++; foo++;
bar++; bar++;
printf("%d,%d", foo, bar); printf("%d, %d", foo, bar);
} }
양쪽 파일을 컴파일하여 fooTest()와 barTest를 반복적으로 호출하는 메인에 링크하면 printf 문이 독립적으로 증가합니다.foo 및 bar 변수는 변환 유닛에 대해 로컬이기 때문에 의미가 있습니다.
그러나 스토리지는 어디에 할당되어 있습니까?
명확하게 하기 위해서는 파일을 ELF 형식으로 출력하는 툴체인이 있다고 가정합니다.따라서 이러한 정적 변수를 위해 실행 파일에 공간이 예약되어 있어야 한다고 생각합니다.
설명을 위해 GCC 툴체인을 사용한다고 가정합니다.
통계정보가 제로로 초기화되어 있는지 여부에 따라 통계정보의 위치가 달라집니다.제로로 초기화되어 있는 스태틱데이터는 에 들어갑니다.BSS(Block Started by Symbol), 초기화되지 않은 데이터가 에 들어갑니다.데이터
프로그램이 메모리에 로드되면 다른 세그먼트로 구성됩니다.세그먼트 중 하나는 DATA 세그먼트입니다.데이터 세그먼트는 다음 두 부분으로 세분됩니다.
- 초기화된 데이터 세그먼트: 모든 글로벌, 정적 및 상수 데이터가 여기에 저장됩니다.
- 초기화되지 않은 데이터 세그먼트(BSS): 초기화되지 않은 모든 데이터가 이 세그먼트에 저장됩니다.
이 개념을 설명하는 그림은 다음과 같습니다.
다음은 이러한 개념을 설명하는 매우 좋은 링크입니다.C에서의 메모리 관리: 힙과 스택
"글로벌 및 정적" 영역:
C++에는 몇 가지 메모리 영역이 있습니다.
- 수북이 쌓다
- 프리 스토어
- 스택
- 글로벌 및 스태틱
- 컨스턴트
질문에 대한 자세한 답변은 여기를 참조하십시오.
다음은 C++ 프로그램의 주요 고유 메모리 영역을 정리한 것입니다.일부 이름(예: "heap")은 초안 [표준]에 나타나지 않습니다.
Memory Area Characteristics and Object Lifetimes
-------------- ------------------------------------------------
Const Data The const data area stores string literals and
other data whose values are known at compile
time. No objects of class type can exist in
this area. All data in this area is available
during the entire lifetime of the program.
Further, all of this data is read-only, and the
results of trying to modify it are undefined.
This is in part because even the underlying
storage format is subject to arbitrary
optimization by the implementation. For
example, a particular compiler may store string
literals in overlapping objects if it wants to.
Stack The stack stores automatic variables. Typically
allocation is much faster than for dynamic
storage (heap or free store) because a memory
allocation involves only pointer increment
rather than more complex management. Objects
are constructed immediately after memory is
allocated and destroyed immediately before
memory is deallocated, so there is no
opportunity for programmers to directly
manipulate allocated but uninitialized stack
space (barring willful tampering using explicit
dtors and placement new).
Free Store The free store is one of the two dynamic memory
areas, allocated/freed by new/delete. Object
lifetime can be less than the time the storage
is allocated; that is, free store objects can
have memory allocated without being immediately
initialized, and can be destroyed without the
memory being immediately deallocated. During
the period when the storage is allocated but
outside the object's lifetime, the storage may
be accessed and manipulated through a void* but
none of the proto-object's nonstatic members or
member functions may be accessed, have their
addresses taken, or be otherwise manipulated.
Heap The heap is the other dynamic memory area,
allocated/freed by malloc/free and their
variants. Note that while the default global
new and delete might be implemented in terms of
malloc and free by a particular compiler, the
heap is not the same as free store and memory
allocated in one area cannot be safely
deallocated in the other. Memory allocated from
the heap can be used for objects of class type
by placement-new construction and explicit
destruction. If so used, the notes about free
store object lifetime apply similarly here.
Global/Static Global or static variables and objects have
their storage allocated at program startup, but
may not be initialized until after the program
has begun executing. For instance, a static
variable in a function is initialized only the
first time program execution passes through its
definition. The order of initialization of
global variables across translation units is not
defined, and special care is needed to manage
dependencies between global objects (including
class statics). As always, uninitialized proto-
objects' storage may be accessed and manipulated
through a void* but no nonstatic members or
member functions may be used or referenced
outside the object's actual lifetime.
(이해하기 쉬운) 방법은 다음과 같습니다.
실제로 변수는 태플(스토리지, 범위, 유형, 주소, 값)입니다.
storage : where is it stored, for example data, stack, heap...
scope : who can see us, for example global, local...
type : what is our type, for example int, int*...
address : where are we located
value : what is our value
로컬 스코프는 정의된 위치에 따라 변환 단위(소스 파일), 함수 또는 블록 중 하나에 대해 로컬을 의미합니다.변수가 여러 함수에 표시되도록 하려면 반드시 DATA 또는 BSS 영역 중 하나에 있어야 합니다(각각 명시적으로 초기화되었는지 여부에 따라 다름).그런 다음 소스 파일 내의 모든 함수 또는 함수 중 하나에 따라 범위가 지정됩니다.
objdump -Sr
실제로 무슨 일이 일어나고 있는지 이해하려면 링커 재배치를 이해해야 합니다.만약 당신이 그것을 한번도 만져본 적이 없다면, 이 글을 먼저 읽어보는 것을 고려해보세요.
Linux x86-64 ELF의 예를 분석하여 직접 보겠습니다.
#include <stdio.h>
int f() {
static int i = 1;
i++;
return i;
}
int main() {
printf("%d\n", f());
printf("%d\n", f());
return 0;
}
컴파일 대상:
gcc -ggdb -c main.c
다음을 사용하여 코드를 디컴파일합니다.
objdump -Sr main.o
-S
는, 가 혼재하고 있는 합니다.-r
재배치 를 나타냅니다.
「 」의 f
뭇매를 맞다
static int i = 1;
i++;
4: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # a <f+0xa>
6: R_X86_64_PC32 .data-0x4
및.data-0x4
의 첫 번째 바이트로 이동한다고 합니다..data
★★★★★★ 。
-0x4
상대에, RIP 의 상대 어드레싱이 사용되고 있습니다.%rip
R_X86_64_PC32
.
이는 RIP가 다음 명령을 가리키기 때문에 필요합니다.이 명령어는 4바이트 후에 시작됩니다.00 00 00 00
그게 바로 이전할 장소입니다.상세한 것에 대하여는, https://stackoverflow.com/a/30515926/895245 를 참조해 주세요.
를 ' 낫다'로 요.i = 1
같은 분석을 실시하면 다음과 같은 결론을 얻을 수 있습니다.
static int i = 0
.bss
static int i = 1
.data
데이터의 저장 위치는 구현에 따라 달라집니다.
단, 스태틱의 의미는 "내부 링크"입니다.따라서 기호는 컴파일 유닛(foo.c, bar.c)의 내부이며 해당 컴파일 유닛의 외부에서는 참조할 수 없습니다.따라서 이름 충돌은 없어야 합니다.
충돌은 없을 것 같아요.파일 수준(외부 함수)에서 static을 사용하면 변수가 현재 컴파일 단위(파일)에 대해 로컬로 표시됩니다.현재 파일 외부에 표시되지 않으므로 외부에서 사용할 수 있는 이름을 가질 필요가 없습니다.
함수 내부에서 static을 사용하는 것은 다릅니다.변수는 함수에만 표시됩니다(정적이든 아니든). 변수는 해당 함수에 대한 호출 전체에 걸쳐 값이 유지됩니다.
실제로 스태틱은 위치에 따라 두 가지 다른 작업을 수행합니다.그러나 두 경우 모두 링크 시 네임스페이스 충돌을 쉽게 방지할 수 있도록 변수 가시성이 제한됩니다.
그렇긴 하지만, 나는 그것이 저장될 것이라고 믿는다.DATA
섹션: 변수가 0 이외의 값으로 초기화되는 경향이 있습니다.물론 이것은 구현 세부 사항이지 표준에 의해 강제된 것은 아닙니다.그것은 행동에만 관심이 있을 뿐, 은폐된 작업 방식은 신경 쓰지 않습니다.
objdump와 gdb로 시도했는데 결과는 다음과 같습니다.
(gdb) disas fooTest
Dump of assembler code for function fooTest:
0x000000000040052d <+0>: push %rbp
0x000000000040052e <+1>: mov %rsp,%rbp
0x0000000000400531 <+4>: mov 0x200b09(%rip),%eax # 0x601040 <foo>
0x0000000000400537 <+10>: add $0x1,%eax
0x000000000040053a <+13>: mov %eax,0x200b00(%rip) # 0x601040 <foo>
0x0000000000400540 <+19>: mov 0x200afe(%rip),%eax # 0x601044 <bar.2180>
0x0000000000400546 <+25>: add $0x1,%eax
0x0000000000400549 <+28>: mov %eax,0x200af5(%rip) # 0x601044 <bar.2180>
0x000000000040054f <+34>: mov 0x200aef(%rip),%edx # 0x601044 <bar.2180>
0x0000000000400555 <+40>: mov 0x200ae5(%rip),%eax # 0x601040 <foo>
0x000000000040055b <+46>: mov %eax,%esi
0x000000000040055d <+48>: mov $0x400654,%edi
0x0000000000400562 <+53>: mov $0x0,%eax
0x0000000000400567 <+58>: callq 0x400410 <printf@plt>
0x000000000040056c <+63>: pop %rbp
0x000000000040056d <+64>: retq
End of assembler dump.
(gdb) disas barTest
Dump of assembler code for function barTest:
0x000000000040056e <+0>: push %rbp
0x000000000040056f <+1>: mov %rsp,%rbp
0x0000000000400572 <+4>: mov 0x200ad0(%rip),%eax # 0x601048 <foo>
0x0000000000400578 <+10>: add $0x1,%eax
0x000000000040057b <+13>: mov %eax,0x200ac7(%rip) # 0x601048 <foo>
0x0000000000400581 <+19>: mov 0x200ac5(%rip),%eax # 0x60104c <bar.2180>
0x0000000000400587 <+25>: add $0x1,%eax
0x000000000040058a <+28>: mov %eax,0x200abc(%rip) # 0x60104c <bar.2180>
0x0000000000400590 <+34>: mov 0x200ab6(%rip),%edx # 0x60104c <bar.2180>
0x0000000000400596 <+40>: mov 0x200aac(%rip),%eax # 0x601048 <foo>
0x000000000040059c <+46>: mov %eax,%esi
0x000000000040059e <+48>: mov $0x40065c,%edi
0x00000000004005a3 <+53>: mov $0x0,%eax
0x00000000004005a8 <+58>: callq 0x400410 <printf@plt>
0x00000000004005ad <+63>: pop %rbp
0x00000000004005ae <+64>: retq
End of assembler dump.
다음은 objdump 결과입니다.
Disassembly of section .data:
0000000000601030 <__data_start>:
...
0000000000601038 <__dso_handle>:
...
0000000000601040 <foo>:
601040: 01 00 add %eax,(%rax)
...
0000000000601044 <bar.2180>:
601044: 02 00 add (%rax),%al
...
0000000000601048 <foo>:
601048: 0a 00 or (%rax),%al
...
000000000060104c <bar.2180>:
60104c: 14 00 adc $0x0,%al
즉, 4개의 변수가 data section event에 같은 이름이지만 오프셋은 다릅니다.
답변은 컴파일러에 따라 다를 수 있으므로 질문을 편집해야 합니다(ISO C나 ISO C++에 의해 세그먼트의 개념도 의무화되어 있지 않습니다).예를 들어 윈도우즈에서는 실행 파일에 기호 이름이 없습니다.한쪽 'foo'는 오프셋 0x100, 다른 한쪽 'foo'는 아마도 0x2B0으로 양쪽 변환 유닛의 코드가 "they" foo의 오프셋을 인식하여 컴파일됩니다.
이 질문은 너무 오래되었지만, 아무도 유용한 정보를 지적하지 않았기 때문에:mohit12379'에 의해 게시물을 체크하여 기호 테이블에 같은 이름의 정적 변수 저장소를 설명합니다.http://www.geekinterview.com/question_details/24745
컴파일 단위로 선언된 데이터는 에 들어갑니다.BSS 또는해당 파일의 출력 데이터입니다.BSS에서 초기화된 데이터, DATA에서 초기화되지 않은 데이터.
정적 데이터와 전역 데이터의 차이는 파일에 기호 정보가 포함되어 있다는 데 있습니다.컴파일러는 기호 정보를 포함하는 경향이 있지만 글로벌 정보만 표시합니다.
링커는 이 정보를 존중합니다.스태틱 변수의 심볼 정보는 폐기되거나 뭉개져 스태틱 변수를 (debug 옵션 또는 symbol 옵션을 사용하여) 참조할 수 있습니다.어느 경우든 링커가 로컬 참조를 먼저 해결하므로 컴파일 유닛은 영향을 받지 않습니다.
사용하는 플랫폼과 컴파일러에 따라 다릅니다.일부 컴파일러는 코드 세그먼트에 직접 저장됩니다.스태틱 변수는 항상 현재 변환 유닛에서만 액세스할 수 있으며 이름은 내보내지 않으므로 이름 충돌이 발생하지 않습니다.
static 변수는 앞에서 설명한 바와 같이 데이터 세그먼트 또는 코드 세그먼트에 저장됩니다.
스택 또는 힙에 할당되지 않음을 확인할 수 있습니다.
이후 충돌 위험은 없습니다.static
키워드는 파일 또는 함수가 되는 변수의 범위를 정의합니다.충돌 시 경고하는 컴파일러/링커가 있습니다.
좋은 예
둘 다 개별적으로 저장되지만 다른 개발자에게 명확하게 하고 싶다면 네임스페이스로 정리하는 것이 좋습니다.
초기화되지 않은 데이터 세그먼트 또는 초기화되지 않은 데이터 세그먼트라고도 하는 bss(기호에 의한 블록 시작)에 저장되는 것을 이미 알고 있습니다.
간단한 예를 들어보자
void main(void)
{
static int i;
}
위의 정적 변수는 초기화되지 않았기 때문에 초기화되지 않은 데이터 세그먼트(bss)로 이동합니다.
void main(void)
{
static int i=10;
}
물론 10분부터 초기화되기 때문에 초기화된 데이터 세그먼트로 이동합니다.
언급URL : https://stackoverflow.com/questions/93039/where-are-static-variables-stored-in-c-and-c
'programing' 카테고리의 다른 글
Vuex 스토어에서 상수 가져오기 (0) | 2022.08.14 |
---|---|
Gradle - 툴체인을 사용한 플랫폼 'JDK 7 (1.7)'을 타깃으로 하지 못했습니다. (0) | 2022.08.14 |
사용하지 않는 기능에 대해 경고를 받을 수 있는 방법이 있습니까? (0) | 2022.08.14 |
실행 시 하나의 Java 클래스가 다른 클래스를 확장하는지 테스트하려면 어떻게 해야 합니까? (0) | 2022.08.14 |
onSnapshot에서 검출되지 않은 오류: 오류: signOut()에 대한 권한이 없거나 부족합니다. (0) | 2022.08.14 |