루프 외부에서 변수 선언과 정적 내부 루프 선언의 차이점은 무엇입니까?


9

이것들은 루프 (또는 함수) 외부에서 변수를 보유 할 수있는 두 가지 방법입니다.

먼저 루프 외부의 전역 범위로 선언 할 수 있습니다.

void setup()
{
    Serial.begin(9600);
}

int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    delay(250);
}

루프 내에서 정적으로 선언 할 수도 있습니다.

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

이것이 있다면 어떤 차이가 있습니까?

답변:


10

가장 기본적인 차이점은 범위입니다.

첫 번째 경우 전역 변수를 선언하는 것입니다. 정의 후 모든 범위에서 액세스 할 수있는 변수입니다.

void setup()
{
    Serial.begin(9600);
}

void inc();
int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

void inc() //Can edit the value of count
{
  count=count+1;
};

두 번째 경우에는 로컬 범위를 사용하여 정적 변수를 선언합니다. 이 변수는 전역 변수와 유사하게 전체 프로그램 실행에 대해 지속되지만 선언 된 코드 블록에서만 액세스 할 수 있습니다. 이는 한 번의 변경만으로 동일한 예입니다. count내부 정적 변수로 선언되었습니다 loop.

void inc();

void loop()
{
    static int count = 0;
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

함수 inc()가 액세스 할 수 없으므로 컴파일 되지 않습니다 count.

그러나 전역 변수는 유용하게 보이지만 몇 가지 함정이 있습니다. 물리적 환경과 상호 작용할 수있는 프로그램을 작성할 때 손상을 일으킬 수도 있습니다. 이것은 프로그램이 커지기 시작하자마자 일어날 수있는 매우 기본적인 예입니다. 함수가 실수로 전역 변수의 상태를 변경할 수 있습니다.

void setup()
{
    Serial.begin(9600);
}
void another_function();
int state=0;

void loop()
{
    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Inadvertently changes state
  state=1;

}

이러한 경우는 디버깅하기가 매우 어렵습니다. 그러나 이러한 유형의 문제는 정적 변수를 사용하여 쉽게 감지 할 수 있습니다.

void setup()
{
    Serial.begin(9600);
}
void another_function();

void loop()
{
    static int state=0;

    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Results in a compile time error. Saves time.
  state=1;

}

5

기능적 관점에서 두 경우의 값이되기 때문에, 두 버전은 동일한 결과를 생성 count의 처형 사이에 저장된다 loop()(이 전역 변수이기 때문에 있거나, 그것으로 표시되어 있기 static때문에, 그 값을 유지한다).

따라서 선택할 결정은 다음과 같은 주장으로 이어집니다.

  1. 일반적으로 컴퓨터 과학에서는 변수를 범위 측면에서 가능한 한 로컬로 유지하는 것이 좋습니다 . 이것은 일반적으로 부작용이 적고 훨씬 명확한 코드를 생성하고 다른 사람이 전역 변수를 사용하여 논리를 망칠 가능성을 줄입니다. 예를 들어 첫 번째 예에서는 다른 논리 영역이 count값을 변경할 수 있지만 두 번째 예에서는 해당 특정 기능 만 loop()그렇게 할 수 있습니다.
  2. 전역 변수와 정적 변수는 항상 메모리를 차지 합니다 . 지역 변수 는 범위 내에있을 때만 수행합니다. 위의 예제에서는 차이가 없지만 (하나는 전역 변수를 사용하고 다른 하나는 정적 변수를 사용하기 때문에) 더 크고 복잡한 프로그램에서는 정적이 아닌 로컬을 사용하여 메모리를 절약 할 수 있습니다. 그러나 로직 영역에 매우 자주 실행되는 변수가있는 경우 정적 또는 전역 변수로 설정하는 것이 좋습니다. 그렇지 않으면 로직 영역을 입력 할 때마다 약간의 성능이 느려집니다. 새 변수 인스턴스에 메모리를 할당하십시오. 메모리로드와 성능 사이의 균형을 찾아야합니다.
  3. 정적 분석을 위한 더 나은 레이아웃 또는 컴파일러의 최적화 와 같은 다른 점들 도 작용할 수 있습니다.
  4. 일부 특수 시나리오에서는 예측할 수없는 정적 요소의 초기화 순서에 문제가있을 수 있습니다 (그 시점에 대해서는 확실하지 않지만 이 링크를 비교하십시오 ).

출처 : arduino.cc의 비슷한 스레드


재진입은 동시성을 지원하지 않기 때문에 Arduino에서 결코 문제가되지 않아야합니다.
피터 블룸필드

진실. 그것은 더 일반적인 요점이지만 실제로 Arduino와 관련이 없습니다. 나는 그 비트를 제거했습니다.
Philip Allgaier

1
범위 내에서 선언 된 정적 변수는 항상 존재하며 전역 변수와 동일한 공간을 사용합니다! OP 코드에서 유일한 차이점은 코드가 변수에 액세스 할 수있는 것입니다. scipe에서 정적은 동일한 범위 내에서 액세스 할 수 있습니다.
jfpoilpret

1
@jfpoilpret 물론 그것은 사실이며, 내 대답의 각 부분이 약간 오도 된 것을 알았습니다. 고쳤다.
Philip Allgaier

2

두 변수는 정적이며 전체 실행 세션 동안 유지됩니다. 전역은 정의하지 않고 전역을 선언하거나 함수가 동일한 컴파일 단위 (파일 + 포함)의 정의를 따르는 경우 모든 함수에 표시됩니다.

정의를 count함수 내부로 옮기면 가시성 범위가 가장 가까운 엔 클로징 {}es 세트로 제한 되고 함수 호출 수명을 제공합니다 (함수가 입력 및 종료 될 때 작성 및 소멸됨). 또한 선언하면 static실행 세션의 시작부터 끝까지 존재하는 실행 세션 수명을 제공하여 함수 호출 전체에서 지속됩니다.

BTW : gnu 컴파일러의 일부 버전 에서이 문제가 발생하는 것을 보았으므로 함수 내에서 초기화 된 정적을 사용하는 것에주의하십시오. 이니셜 라이저가있는 자동 변수는 모든 기능 항목에서 생성되고 초기화되어야합니다. 이니셜 라이저가있는 정적은 실행 설정 중에 main ()에 제어가 부여되기 전에 (전역과 마찬가지로) 한 번만 초기화해야합니다. 각 함수 항목에서 로컬 정적이 자동 인 것처럼 다시 초기화되었습니다. 정확하지 않습니다. 자신의 컴파일러를 테스트하여 확인하십시오.


전역을 선언하는 함수에 대한 당신의 의미를 잘 모르겠습니다. 당신은 extern?
피터 블룸필드

@ PeterR.Bloomfield : 내 게시물의 어떤 부분에 대해 당신이 묻는 지 확실하지 않지만 OP의 두 가지 예-본질적으로 글로벌 정의와 두 번째 예는 로컬 정적을 언급하고 있습니다.
JRobert

-3

Atmel의 문서에 따르면 : "글로벌 변수가 선언되면 프로그램 링크 타임에 SRAM의 고유 주소가이 변수에 할당됩니다."

전체 문서는 여기에 있습니다 (전역 변수에 대한 팁 # 2) : http://www.atmel.com/images/doc8453.pdf


4
두 예제 모두 SRAM에서 고유 주소로 끝나지 않습니까? 둘 다 지속해야합니다.
Cybergibbons

2
예, 팁 6 번에서 같은 문서에서 해당 정보를 찾을 수 있습니다.
jfpoilpret
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.