source

C에서 새로운 변수를 선언할 수 있는 경우와 선언할 수 없는 경우

goodcode 2022. 7. 23. 14:16
반응형

C에서 새로운 변수를 선언할 수 있는 경우와 선언할 수 없는 경우

프로그램/기능 위에 모든 변수를 선언해야 하고, 스테이트먼트 중에 새로운 변수를 선언하면 문제가 생길 수 있다고 (선생님으로부터) 들었습니다.

그런데 K&R을 읽다가 우연히 이 문장을 발견했습니다. "변수 선언(초기화 포함)은 함수를 시작하는 명령뿐만 아니라 복합문을 도입하는 왼쪽 괄호 뒤에 있을 수 있습니다."그는 예를 들어 다음과 같이 말한다.

if (n > 0){
    int i;
    for (i=0;i<n;i++)
    ...
}

컨셉을 조금 가지고 놀았더니 어레이에서도 동작합니다.예를 들어 다음과 같습니다.

int main(){
    int x = 0 ;

    while (x<10){
        if (x>5){
            int y[x];
            y[0] = 10;
            printf("%d %d\n",y[0],y[4]);
        }
        x++;
    }
}

정확히 언제 변수를 선언할 수 없는 거죠?예를 들어 변수 선언이 시작 괄호 바로 뒤에 있지 않으면 어떻게 해야 합니까?다음과 같은 경우:

int main(){
    int x = 10;

    x++;
    printf("%d\n",x);

    int z = 6;
    printf("%d\n",z);
}

프로그램/기계에 따라 문제가 발생할 수 있습니까?

또한 변수를 함수의 맨 위에 두는 것이 가장 좋은 방법이라는 말도 자주 듣지만, 저는 강하게 반대합니다.변수를 가능한 한 작은 범위로 제한하여 오용될 가능성이 적기 때문에 프로그램의 각 줄에 내 머릿속을 가득 채우는 내용이 적습니다.

C의 모든 버전은 어휘 블록 범위를 허용하지만 변수를 선언할 수 있는 위치는 대상 C 표준의 버전에 따라 달라집니다.

C99 이후 또는 C++

gcc 및 clang 등의 최신 C 컴파일러는 C99C11 표준을 지원합니다.이것에 의해, 스테이트먼트가 들어갈 수 있는 임의의 장소에서 변수를 선언할 수 있습니다.변수의 범위는 선언 지점에서 블록 끝(다음 닫힘 괄호)까지 시작합니다.

if( x < 10 ){
   printf("%d", 17);  // z is not in scope in this line
   int z = 42;
   printf("%d", z);   // z is in scope in this line
}

루프 이니셜라이저의 변수를 내부에 선언할 수도 있습니다.변수는 루프 내부에만 존재합니다.

for(int i=0; i<10; i++){
    printf("%d", i);
}

ANSI C(C90)

오래된 ANSI C 표준을 대상으로 하는 경우 변수를 여는1 중괄호 직후에 선언하는 것으로 제한됩니다.

그렇다고 해서 모든 변수를 함수 맨 위에 선언해야 하는 것은 아닙니다.수 있는 에 괄호로 수 ).if ★★★★★★★★★★★★★★★★★」for 스코프를 할 수 를 사용하여 새로운 변수 범위를 도입할 수 있습니다.ANSI C를 사용하다

if( x < 10 ){
   printf("%d", 17);  // z is not in scope in this line

   {
       int z = 42;
       printf("%d", z);   // z is in scope in this line
   }
}

{int i; for(i=0; i<10; i++){
    printf("%d", i);
}}

1 gcc를 사용하고 있는 경우는, 다음의 명령어를 통과시킬 필요가 있습니다.--pedantic플래그를 사용하여 C90 표준을 실제로 적용하고 변수가 잘못된 위치에 선언되었다고 불평합니다.그냥 사용하면-std=c90 때문에 을 할 수 있습니다.gcc C90은 C99를 C99로 합니다.

missingno는 ANSI C가 허용하는 것을 포함하지만, 왜 선생님께서 변수를 함수 맨 위에 선언하라고 하셨는지는 설명하지 않습니다.변수를 홀수 위치에 선언하면 코드를 읽기 어려워지고 버그가 발생할 수 있습니다.

다음 코드를 예로 들어 보겠습니다.

#include <stdio.h>

int main() {
    int i, j;
    i = 20;
    j = 30;

    printf("(1) i: %d, j: %d\n", i, j);

    {
        int i;
        i = 88;
        j = 99;
        printf("(2) i: %d, j: %d\n", i, j);
    }

    printf("(3) i: %d, j: %d\n", i, j);

    return 0;
}

제가 선언을 i 번, 두 변수를 두 모두 '두 가지 변수.i날 하실 수도 두 은 틀리지 않습니다.i변수가 다른 범위에 있습니다.이 기능의 출력을 보면, 이것을 보다 명확하게 알 수 있습니다.

(1) i: 20, j: 30
(2) i: 88, j: 99
(3) i: 20, j: 99

을 20과 30에 합니다.i ★★★★★★★★★★★★★★★★★」j각각 다음과 같다.88세 99세 왜 이 문장은 되어 있을까요?j은 유지하다i20분 정도요?두 가지가 다르기 입니다.i수입니니다다

의 곱슬머리 세트 에는, 「」가 붙어 .i 인 할 수 만, 「20」이 않기 에, 「20」은 사용할 수 없습니다.j도 계속 j외부 스코프로부터.괄호 , 「」가 .i을 유지하면 88에 할 수 .i20달러

이 동작이 좋을 때도 있고 아닐 때도 있지만 C의 이 기능을 무분별하게 사용하면 코드를 혼란스럽게 하고 이해하기 어려울 수 있습니다.

컴파일러가 허가하고 있는 경우는, 임의의 장소에 선언할 수 있습니다.실제로 변수의 초기화를 잊어버리거나 실수로 변수를 숨기는 등의 오류를 쉽게 발견할 수 있기 때문에 함수의 맨 위가 아니라 사용하는 변수를 선언할 때 코드가 더 읽기 쉬워집니다(IMHO).

투고에는, 다음의 코드가 표시됩니다.

//C99
printf("%d", 17);
int z=42;
printf("%d", z);

//ANSI C
printf("%d", 17);
{
    int z=42;
    printf("%d", z);
}

그리고 그 의미는 이 두 가지가 동등하다는 것입니다.그들은 그렇지 않다.이 코드 스니펫의 맨 아래에 int z가 배치되면 첫 번째 z 정의에서는 재정의 오류가 발생하지만 두 번째 정의에서는 그렇지 않습니다.

단, 다음 행이 여러 개 있습니다.

//C99
for(int i=0; i<10; i++){}

동작합니다.이 C99 규칙의 미묘한 점을 나타냅니다.

개인적으로 이 C99 기능은 피하고 싶습니다.

변수 범위를 좁힌다는 주장은 다음 예에서 알 수 있듯이 거짓입니다.새로운 규칙에서는 블록 전체를 스캔할 때까지 변수를 안전하게 선언할 수 없습니다.이전에는 각 블록의 선두에서 무슨 일이 일어나고 있는지 이해하기만 하면 되었습니다.

The C Programming Language by K&R에 따르면 -

C에서는 모든 변수를 사용하기 전에 선언해야 합니다.일반적으로 실행 가능한 문 앞에 함수가 시작될 때 선언됩니다.

여기서 단어를 볼 수 있습니다. 보통은 그렇지 않습니다.

clang과 gcc에서 다음과 같은 큰 문제가 발생했습니다.gcc 버전 8.2.1 20181011 clang 버전 6.0.1

  {
    char f1[]="This_is_part1 This_is_part2";
    char f2[64]; char f3[64];
    sscanf(f1,"%s %s",f2,f3);      //split part1 to f2, part2 to f3 
  }

어느 컴파일러도 f1, f2, f3가 블록 내에 있는 것을 좋아하지 않습니다.f1, f2, f3를 함수 정의 영역으로 재배치해야 했습니다.컴파일러는 블록의 정수의 정의에 개의치 않았습니다.

내부적으로는 함수에 로컬한 모든 변수가 스택 또는 CPU 레지스터 내부에 할당되며, 컴파일러가 불량하거나 CPU에 모든 볼이 공중에 저글링할 수 있는 충분한 레지스터가 없는 경우 생성된 머신 코드가 레지스터와 스택(레지스터 스필이라고 불립니다) 간에 스왑됩니다.

스택에 데이터를 할당하기 위해 CPU에는 Stack Pointer(SP; 스택 포인터)라고 불리는 레지스터와 Base Pointer(BP; 베이스 포인터) 또는 Frame Pointer(현재 기능 범위에 로컬 스택프레임을 의미합니다)라는2개의 특별한 레지스터가 있습니다.SP는 스택의 현재 위치 내부를 가리키고 BP는 작업 데이터 세트(위)와 함수 인수(아래)를 가리킵니다.함수가 호출되면 발신자/부모 함수의 BP를 스택(SP가 가리키는 것)에 푸시하고 현재 SP를 새로운 BP로 설정한 후 레지스터에서 스택으로 흘린 바이트 수만큼 SP를 증가시켜 계산을 수행하고 응답으로 스택에서 팝하여 부모 BP를 복원합니다.

일반적으로 변수 내부에 유지{}컴파일러가 어떤 변수를 어디에 어떻게 사용할지 결정하기 위해 걸어가야 하는 그래프의 크기를 줄임으로써 컴파일러의 속도를 높이고 생성된 코드를 개선할 수 있습니다.어떤 경우(특히 goto와 관련된 경우) 컴파일러는 사용자가 컴파일러의 사용범위를 명시적으로 알리지 않는 한 변수가 더 이상 사용되지 않는다는 사실을 놓칠 수 있습니다.컴파일러는 프로그램 그래프를 검색하는 데 시간/심도 제한이 있을 수 있습니다.

컴파일러는 서로 가까이에서 선언된 변수를 동일한 스택 영역에 배치할 수 있습니다. 즉, 하나를 로드하면 다른 모든 변수가 캐시에 미리 로드됩니다.로 변수 register는 컴파일러가 어떤 대가를 치르더라도 해당 변수가 스택에 유출되는 것을 피하고 싶다는 힌트를 줄 수 있습니다.

엄격한 C99 표준에는 명시적 필요{선언 전에 C++와 GCC에 의해 도입된 확장으로 인해 바(var)가 본문에 더 많이 선언될 수 있으며, 이는 복잡하다.goto그리고.case진술들.또한 C++는 루프 초기화를 위해 내부 정보를 선언할 수 있으며, 이는 루프 범위로 제한됩니다.

마지막으로 중요한 것은 다른 사람이 당신의 코드를 읽는다는 것입니다.사용처에 현지화된 변수 선언이 아니라 50개의 변수 선언이 흩어져 있는 함수 상단을 보면 압도적일 것입니다.또, 그 사용법을 코멘트하기 쉬워집니다.

TLDR: 사용{}변수 범위를 명시적으로 기술하는 것은 컴파일러와 인간 판독기 모두에 도움이 됩니다.

언급URL : https://stackoverflow.com/questions/8474100/where-you-can-and-cannot-declare-new-variables-in-c

반응형