C 함수 내부의 정적 변수


119

무엇을 인쇄합니까? 6 6 또는 6 7? 그리고 왜?

void foo()
{
    static int x = 5;
    x++;
    printf("%d", x);
}

int main()
{
    foo();
    foo();
    return 0;
}

54
시도 할 문제는 무엇입니까?
Andrew

12
이것을 입력하고 직접 보려고 했습니까?
wilhelmtell

21
이유를 이해하고 싶습니다.
Vadiklk

7
@Vadiklk 그래서 "왜"로 시작하는 질문
Andrey

1
ideone.com/t9Bbe 무엇을 기대하십니까? 결과가 귀하의 기대와 일치하지 않습니까? 결과를 기대 한 이유는 무엇입니까?
eckes

답변:


187

여기에는 수명과 범위라는 두 가지 문제가 있습니다.

변수의 범위는 변수 이름을 볼 수있는 곳입니다. 여기서 x는 foo () 함수 내에서만 볼 수 있습니다.

변수의 수명은 변수가 존재하는 기간입니다. x가 static 키워드없이 정의 된 경우 수명은 foo ()의 항목에서 foo ()의 반환까지입니다. 따라서 모든 호출에서 5로 다시 초기화됩니다.

static 키워드는 변수의 수명을 프로그램의 수명으로 연장하는 역할을합니다. 예를 들어 초기화는 한 번만 발생하고 변수는 foo ()에 대한 모든 향후 호출에 대해 값을 유지합니다.


15
@devanl, 예, 그렇습니다.
orion elenzil

1
간단하고 논리적 :)
디미타르 Vukman

어떤 시나리오에서 우리는 함수 내에서 변수를 정적으로 선언해야합니까?, 이전에 이것을 사용하지 않았기 때문에 궁금한 점이 있습니까?
Akay

고맙다고 말하고 싶지만,이 모든 것이 페이지 맨 위에 답변되었습니다. 사람들이 자신의 코드를 실행하지 않는다는 사실이 저를 웃게 만듭니다. xD
Puddle

이 대답은 틀 렸습니다. 재귀 함수에 대해 생각하는 순간 여기에 설명 된 정의는 동작을 설명하지 않습니다!
Philip Couling 2019

53

출력 : 6 7

이유 : 정적 변수는 자동 변수와 달리 한 번만 초기화되며 정적 변수의 추가 정의는 런타임 중에 무시됩니다. 수동으로 초기화하지 않으면 자동으로 0 값으로 초기화됩니다. 그래서,

void foo() {
    static int x = 5; // assigns value of 5 only once
    x++;
    printf("%d", x);
}

int main() {
    foo(); // x = 6
    foo(); // x = 7
    return 0;
}

10

6 7

컴파일러는 함수가 입력 될 때마다 정적 변수 초기화가 발생하지 않도록 배열합니다.


10

다음 프로그램이있는 것과 같습니다.

static int x = 5;

void foo()
{
    x++;
    printf("%d", x);
}

int main()
{
     foo();
     foo();
     return 0;
}

이 프로그램에서 static 키워드가하는 일은 컴파일러에게 (본질적으로) '이봐, 다른 사람이 액세스하지 않기를 바라는 변수가 있습니다. 다른 사람에게 그것이 존재한다고 말하지 마십시오'라고 말하는 것입니다.

메서드 내에서 static 키워드는 컴파일러에게 위와 동일하지만 '이 함수 외부에 존재한다는 사실을 아무에게도 말하지 마십시오.이 함수 내에서만 액세스 할 수 있어야합니다'라고 말합니다.

이게 도움이 되길 바란다


13
글쎄, 그것은 실제로 동일하지 않습니다. X에는 ​​여전히 스코프 문제가 있습니다.이 예에서는 xmain을 사용 하여 찌르고 풀 수 있습니다 . 글로벌입니다. 원래 예제에서는 xfoo에 로컬이었고 해당 블록 내부에서만 볼 수 있었으며 일반적으로 바람직합니다. foo가 x예측 가능하고 가시적 인 방식으로 유지하기 위해 존재한다면 다른 사람들이 찌르는 것은 일반적으로 위험합니다. 범위 내에서 유지하는 또 다른 이점으로 휴대 foo() 성도 유지합니다 foo().
user2149140

2
@ user2149140 '는 단지이 함수 내에서 액세스 할 수 있어야이이 기능의 외부에 존재하는 것을 아무에게도 말하지 않는다'
DCShannon

3
변수가 선언 된 위치로 인해 범위 문제를 해결했지만 수명이 아닌 범위에 영향을 미치는 정적 설명은 잘못된 것 같습니다.
DCShannon

1
@Chameleon 질문에으로 태그가 지정되어 있으므로이 c컨텍스트에서 귀하의 예제는 전역 범위에서 불법입니다. (C는 전역에 대한 상수 이니셜 라이저가 필요하지만 C ++는 그렇지 않습니다.)
Richard J. Ross III

5

함수 내부의 정적 변수는 프로그램이 실행되는 한 수명이 있습니다. 함수가 호출 될 때마다 할당되고 함수가 반환 될 때 할당이 취소되는 것은 아닙니다.


이것이 "전역"변수와 같다고 말하고 EXCEPT 당신이 그것에 접근 할 수 없다고 말하는 것은 모순입니다. 글로벌은 어디서나 액세스 할 수 있음을 의미합니다. 이 경우 함수 내부 정적의 경우 모든 곳에서 액세스 할 수 없습니다. 다른 사람들이 지적했듯이 OP의 문제는 범위와 수명에 관한 것입니다. 사람들을 '글로벌'이라는 용어를 사용하는 것과 혼동하여 변수의 범위에 대해 오해하지 마십시오.
ChuckB 2017-09-23

@ChuckB : 맞아요. 고쳤다. 6 년이 지났습니다. 내 이전 답변은 6 년 전의 인식이었습니다!
Donotalo

5

출력 : 6,7

이유

의 선언 x은 내부에 foo있지만 x=5초기화는 foo!

여기서 이해해야 할 것은

static int x = 5;

다음과 같지 않다

static int x;
x = 5;

다른 답변은 여기에서 중요한 단어, 범위 및 수명을 사용했으며의 범위 x는 함수의 선언 지점에서 함수 foo끝까지 임을 지적했습니다 foo. 예를 들어 선언을 함수의 끝으로 이동하여 확인한 결과 문 x에서 선언되지 x++;않습니다.

따라서 static int x명령문 의 (범위) 부분은 실제로 읽은 위치, 함수 내부 어딘가에 적용되며 함수 내부의 위가 아닌 그 이후에만 적용됩니다 .

그러나 x = 5명령문 의 (수명) 부분은 변수의 초기화이며 프로그램로드의 일부로 함수 외부 에서 발생 합니다. 변수 x5프로그램이로드 될 때의 값으로 생성 됩니다.

나는 주석 중 하나에서 이것을 읽었습니다. " 또한 이것은 정말로 혼란스러운 부분을 다루지 않습니다. 이것은 이니셜 라이저가 후속 호출에서 생략된다는 사실입니다. "모든 호출에서 생략됩니다. 변수 초기화는 적절한 기능 코드 밖에 있습니다.

5의 값은 foo 호출 여부에 관계없이 이론적으로 설정되지만 컴파일러는 아무데도 호출하지 않으면 함수를 최적화 할 수 있습니다. 5의 값은 foo가 호출되기 전에 변수에 있어야합니다.

내부 foo에서 명령문 static int x = 5;은 코드를 전혀 생성하지 않을 것입니다.

내 프로그램에 x함수 foo를 넣을 때 주소가 사용 하는 것을 발견 한 다음 프로그램을 다시 실행하면 동일한 위치가 사용될 것이라고 (올바르게) 추측했습니다. 아래의 부분 화면 캡처는에 대한 첫 번째 호출 이전에도 x값이 있음을 보여줍니다 .5foo

foo를 처음 호출하기 전 중단 점


2

출력은 6 7. 정적 변수 (함수 내부 여부에 관계없이)는 해당 번역 단위의 함수가 실행되기 전에 정확히 한 번 초기화됩니다. 그 후에는 수정 될 때까지 값이 유지됩니다.


1
함수를 처음 호출 할 때가 아니라 함수가 호출되기 전에 정적이 초기화 되었습니까?
제시 페퍼

@JessePepper : 적어도 메모리가 서비스를 제공한다면 이것은 C ++ 98 / 03 또는 C ++ 11에 대해 이야기하고 있는지에 따라 다릅니다. C ++ 98 / 03에서는 위에서 설명한 것과 같다고 생각합니다. C ++ 11에서 스레딩은 본질적으로 불가능하게하므로 초기화는 함수의 첫 번째 항목에서 수행됩니다.
Jerry Coffin 2012

2
나는 당신이 실제로 틀렸다고 생각합니다. C ++ 11 이전에도 함수가 호출 될 때만 초기화되었다고 생각합니다. 이것은 정적 초기화 종속성 문제에 대한 일반적인 솔루션에 중요합니다.
Jesse Pepper

2

바 딕크,

왜 ...? 이유는 정적 변수는 한 번만 초기화되고 프로그램 전체에서 그 값을 유지하기 때문입니다. 즉, 함수 호출 사이에 정적 변수를 사용할 수 있습니다. 또한 "함수가 호출되는 횟수"를 계산하는 데 사용할 수도 있습니다.

main()
{
   static int var = 5;
   printf("%d ",var--);
   if(var)
      main();
} 

대답은 5 4 3 2 1이고 5 5 5 5 5 5 .... (무한 루프) 예상대로입니다. 다시 말하지만, 정적 변수는 한 번 초기화되고 다음에 main ()이 호출 될 때 프로그램에서 이미 초기화 되었기 때문에 5로 초기화되지 않으므로 값을 변경할 수 있지만 다시 초기화 할 수는 없습니다. 이것이 정적 변수가 작동하는 방식입니다.

또는 스토리지별로 고려할 수 있습니다. 정적 변수는 프로그램의 데이터 섹션에 저장되고 데이터 섹션에 저장된 변수는 한 번 초기화됩니다. 초기화 전에 BSS 섹션에 보관됩니다.

차례로 Auto (로컬) 변수는 Stack에 저장되고 스택의 모든 변수는 함수가 새로운 FAR (함수 활성화 레코드)로 호출 될 때 항상 다시 초기화됩니다.

이해를 돕기 위해 위의 예를 "정적"없이 수행하고 출력이 무엇인지 알려주십시오. 그러면이 둘의 차이점을 이해할 수 있습니다.

감사합니다 Javed


1

정적 변수에 대한 Wikipedia 기사를 읽어 보겠습니다 .

정적 지역 변수 : 함수 내에서 정적로 선언 된 변수는 자동 지역 변수와 동일한 범위를 가지면서 정적으로 할당됩니다. 따라서 함수가 한 번의 호출 중에 정적 지역 변수에 입력하는 값은 함수가 다시 호출 될 때 여전히 존재합니다.


5
끔찍 해요! "함수 내에서 정적으로 선언 된 변수는 정적으로 할당됩니다."-그것이 의미하는 바를 이미 알고 있지 않는 한 아무것도 설명하지 않습니다!

@Blank : 글쎄요, 그것이 제가 두 번째 문장을위한 것이라고 생각했던 것입니다. 나는 당신이 옳다고 생각하지만 더 나은 말로해야합니다.
Andrew White

또한 이것은 이니셜 라이저가 후속 호출에서 생략된다는 사실 인 정말 혼란스러운 부분을 다루지 않습니다.
Tom Auger 2017

정적으로 할당 됨은 스택이나 힙이 없음을 의미합니다.
Chameleon

1

쉽게 테스트 할 수있는 것처럼 6 7이 인쇄되고 그 이유는 다음과 같습니다. foo처음 호출되면 정적 변수 x가 5로 초기화됩니다. 그런 다음 6으로 증가하여 인쇄됩니다.

이제에 대한 다음 호출 foo입니다. 프로그램은 정적 변수 초기화를 건너 뛰고 대신 마지막으로 x에 할당 된 값 6을 사용합니다. 실행은 정상적으로 진행되어 7 값을 제공합니다.


1
6 7

x는 foo ()에서만 볼 수있는 전역 변수입니다. 5는 코드의 .data 섹션에 저장된 초기 값입니다. 이후의 수정은 이전 값을 덮어 씁니다. 함수 본문에 생성 된 할당 코드가 없습니다.


1

6 and 7 정적 변수는 한 번만 초기화되기 때문에 5 ++는 첫 번째 호출에서 6이됩니다. 6 ++는 두 번째 호출에서 7이됩니다. 참고-두 번째 호출이 발생하면 x가 정적 변수이므로 5 대신 x 값이 6이됩니다.


0

적어도 C ++ 11에서 로컬 정적 변수를 초기화하는 데 사용 된 표현식이 'constexpr'이 아닌 경우 (컴파일러에서 평가할 수 없음) 함수를 처음 호출하는 동안 초기화가 발생해야합니다. 가장 간단한 예는 매개 변수를 직접 사용하여 로컬 정적 변수를 초기화하는 것입니다. 따라서 컴파일러는 호출이 첫 번째 호출인지 여부를 추측하기 위해 코드를 내 보내야하며, 이는 차례로 로컬 부울 변수가 필요합니다. 이러한 예제를 컴파일하고 어셈블리 코드를보고 이것이 사실인지 확인했습니다. 예는 다음과 같을 수 있습니다.

void f( int p )
{
  static const int first_p = p ;
  cout << "first p == " << p << endl ;
}

void main()
{
   f(1); f(2); f(3);
}

물론 expresion이 'constexpr'인 경우에는 필요하지 않으며 컴파일러가 출력 어셈블리 코드에 저장 한 값을 사용하여 프로그램로드시 변수를 초기화 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.