programing

정적 변수는 C 및 C++ 어디에 저장됩니까?

itsource 2022. 8. 14. 10:45
반응형

정적 변수는 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 의 상대 어드레싱이 사용되고 있습니다.%ripR_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

반응형