문제

실행 파일의 어떤 세그먼트(.BSS, .DATA, 기타)에 이름 충돌이 발생하지 않도록 정적 변수가 저장되어 있습니까?예를 들어:


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 툴체인을 사용한다고 가정해 보겠습니다.

도움이 되었습니까?

해결책

정적이 어디로 가는지는 그것이 무엇인지에 따라 달라집니다. 0으로 초기화됨 아니면. 0으로 초기화됨 정적 데이터가 들어갑니다 .BSS(기호로 시작되는 블록), 비 0으로 초기화됨 데이터가 들어간다 .데이터

다른 팁

프로그램이 메모리에 로드되면 여러 세그먼트로 구성됩니다.세그먼트 중 하나는 데이터 세그먼트.데이터 세그먼트는 두 부분으로 더 세분화됩니다.

초기화된 데이터 세그먼트: 모든 전역, 정적 및 상수 데이터가 여기에 저장됩니다.
초기화되지 않은 데이터 세그먼트(BSS): 초기화되지 않은 모든 데이터는 이 세그먼트에 저장됩니다.

다음은 이 개념을 설명하는 다이어그램입니다.

enter image description here


다음은 이러한 개념을 설명하는 매우 좋은 링크입니다.

http://www.inf.udec.cl/~leo/teoX.pdf

실제로 변수는 튜플(저장소, 범위, 유형, 주소, 값)입니다.

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 영역에 있어야 합니다(각각 명시적으로 초기화되었는지 여부에 따라 다름).그런 다음 소스 파일 내의 모든 함수 또는 함수에 따라 범위가 지정됩니다.

데이터의 저장 위치는 구현에 따라 다릅니다.

그러나 공전 "내부 연결"입니다.따라서 기호는 내부 컴파일 단위(foo.c, bar.c)에 추가되며 해당 컴파일 단위 외부에서 참조될 수 없습니다.따라서 이름 충돌이 있을 수 없습니다.

충돌이 일어날 것이라고는 믿지 않습니다.파일 수준(외부 함수)에서 static을 사용하면 변수가 현재 컴파일 단위(파일)에 대해 로컬로 표시됩니다.현재 파일 외부에는 표시되지 않으므로 이름을 가질 필요가 없습니다.

함수 내에서 static을 사용하는 것은 다릅니다. 변수는 함수에만 표시되며 해당 함수에 대한 호출 전체에서 해당 값이 보존됩니다.

실제로 static은 위치에 따라 두 가지 다른 작업을 수행합니다.그러나 다른 경우에는 네임스페이스 충돌을 방지하기 위해 변수의 가시성을 제한합니다.

그렇긴 하지만 초기화된 변수가 있는 경향이 있는 DATA에 저장될 것이라고 생각합니다.BSS는 원래 초기화되지 않은 변수를 보유하는 byte-set-<something>을 나타냈습니다.

스스로 찾는 방법 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

"전역 및 정적" 영역에서 :)

C++에는 여러 메모리 영역이 있습니다.

  • 더미
  • 무료 매장
  • 스택
  • 전역 및 정적
  • const

보다 여기 귀하의 질문에 대한 자세한 답변을 원하시면

사용 중인 플랫폼과 컴파일러에 따라 다릅니다.일부 컴파일러는 코드 세그먼트에 직접 저장합니다.정적 변수는 항상 현재 번역 단위에만 액세스할 수 있으며 이름은 내보내지지 않으므로 이름 충돌이 발생하지 않습니다.

컴파일 단위에 선언된 데이터는 .BSS 또는 해당 파일 출력의 .Data로 이동됩니다.BSS에서 초기화된 데이터, DATA에서 초기화되지 않은 데이터.

정적 데이터와 전역 데이터의 차이점은 파일에 기호 정보가 포함된다는 점입니다.컴파일러는 기호 정보를 포함하는 경향이 있지만 전역 정보만 표시합니다.

링커는 이 정보를 존중합니다.정적 변수에 대한 기호 정보는 삭제되거나 맹글링되어 정적 변수가 어떤 방식(디버그 또는 기호 옵션 사용)으로 계속 참조될 수 있도록 합니다.두 경우 모두 링커가 로컬 참조를 먼저 확인하므로 컴파일 단위는 영향을 받지 않습니다.

앞서 언급한 것처럼 데이터 세그먼트나 코드 세그먼트에 저장된 정적 변수입니다.
스택이나 힙에 할당되지 않을 것임을 확신할 수 있습니다.
때문에 충돌 위험이 없습니다. static 키워드는 변수의 범위를 파일이나 함수로 정의합니다. 충돌이 발생할 경우 경고할 컴파일러/링커가 있습니다.
좋은

글쎄, 이 질문은 너무 오래되었지만 아무도 유용한 정보를 지적하지 않기 때문에:기호 테이블에서 동일한 이름을 가진 정적 변수의 저장소를 설명하는 'mohit12379'의 게시물을 확인하세요.http://www.geekinterview.com/question_details/24745

나는 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개의 변수가 이름은 같지만 오프셋이 다른 데이터 섹션 이벤트에 위치합니다.

방법은 다음과 같습니다(이해하기 쉽습니다).

stack, heap and static data

답변은 컴파일러에 따라 크게 달라질 수 있으므로 질문을 편집하고 싶을 수도 있습니다(즉, ISO C나 ISO C++에서는 세그먼트 개념조차 의무화하지 않습니다).예를 들어 Windows에서는 실행 파일에 기호 이름이 포함되지 않습니다.하나의 'foo'는 오프셋 0x100이고 다른 하나는 아마도 0x2B0일 것이며 두 번역 단위의 코드는 "그들의" foo에 대한 오프셋을 알고 컴파일됩니다.

둘 다 독립적으로 저장되지만 다른 개발자에게 명확하게 알리고 싶다면 네임스페이스에 래핑하는 것이 좋습니다.

초기화되지 않은 데이터 세그먼트라고도 하는 bss(기호로 블록 시작)에 저장되거나 초기화된 데이터 세그먼트에 저장된다는 것을 이미 알고 있습니다.

간단한 예를 들어보자

void main(void)
{
static int i;
}

위의 정적 변수는 초기화되지 않았으므로 초기화되지 않은 데이터 세그먼트(bss)로 이동합니다.

void main(void)
{
static int i=10;
}

물론 10으로 초기화되었으므로 초기화된 데이터 세그먼트로 이동합니다.

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