itsource

C에서의 변수 선언 배치

mycopycode 2022. 8. 31. 22:49
반응형

C에서의 변수 선언 배치

C에서는 함수 시작 부분에서 모든 변수를 선언해야 한다고 오랫동안 생각했습니다.C99의 규칙은 C++와 동일하지만 C89/ANSI C의 변수 선언 배치 규칙은 무엇입니까?

다음 코드는 성공적으로 컴파일되었습니다.gcc -std=c89그리고.gcc -ansi:

#include <stdio.h>
int main() {
    int i;
    for (i = 0; i < 10; i++) {
        char c = (i % 95) + 32;
        printf("%i: %c\n", i, c);
        char *s;
        s = "some string";
        puts(s);
    }
    return 0;
}

의 선언이 있어야 하는 것이 좋지 않을까?c그리고.sC89/ANSI 모드에서 오류가 발생합니까?

GCC에 의해서, 다음의 선언이 가능하기 때문에, 정상적으로 컴파일 됩니다.sC89 또는 ANSI 규격의 일부가 아닌 경우에도 GNU 확장자로 사용할 수 있습니다.이러한 기준을 엄격히 준수하려면 다음 절차를 통과해야 합니다.-pedantic플래그를 설정합니다.

의 선언c을 시작할 때{ }블록은 C89 표준의 일부이므로 블록이 함수일 필요는 없습니다.

블록의 맨 위에 변수 선언을 그룹화하는 것은 오래된 원시 C 컴파일러의 제한으로 인해 발생할 수 있는 레거시입니다.모든 현대 언어는 지역 변수가 처음 초기화되는 최신 지점에서 지역 변수를 선언하는 것을 권장하고 강제하기도 합니다.이렇게 하면 실수로 랜덤 값을 사용할 위험이 없어지기 때문입니다.선언과 초기화를 분리하면 가능한 경우 "const"(또는 "final")를 사용할 수 없습니다.

C++는 유감스럽게도 C와의 하위 호환성을 위해 오래된 상위 선언 방법을 계속 받아들입니다(다른 많은 C 호환성 중에서1개의 호환성을 끌어냅니다). 그러나 C++는 이를 피하려고 합니다.

  • C++ 참조의 설계에서는 이러한 블록 그룹화가 허용되지 않습니다.
  • C++ 로컬오브젝트의 선언과 초기화를 분리하면 추가 컨스트럭터의 비용은 무료로 지불됩니다.no-arg 컨스트럭터가 존재하지 않는 경우에도 양쪽을 분리할 수 없습니다.

C99는 C를 같은 방향으로 이동시키기 시작합니다.

로컬 변수가 선언된 위치를 찾을 수 없는 경우, 이는 더 큰 문제가 있음을 의미합니다. 즉, 둘러싸는 블록이 너무 길기 때문에 분할해야 합니다.

https://wiki.sei.cmu.edu/confluence/display/c/DCL19-C.+Minimize+the+scope+of+variables+and+functions

C89의 경우 스코프 블록의 선두에 모든 변수를 선언해야 합니다.

그래서, 당신의char c선언은 for loop scope block의 선두에 있기 때문에 유효합니다.근데...char *s선언은 오류여야 합니다.

다른 사람들이 지적한 바와 같이, GCC는 'C89' 모드에 있는 경우에도 '유아성' 체크를 사용하지 않는 한 이 점에 대해 허용됩니다(또한 호출되는 인수에 따라 다른 컴파일러도 허용될 수 있습니다.솔직히 말해서 현학적 기능을 사용하지 않는 좋은 이유는 많지 않습니다.품질의 최신 코드는 항상 경고 없이 컴파일해야 합니다(또는 컴파일러에게 의심스러운 작업을 하고 있는 것을 알 수 있는 경우는 극히 일부입니다).그래서 현학적 설정으로 코드를 컴파일 할 수 없는 경우에는 주의가 필요합니다.

C89는 각 범위 내의 다른 문장에 앞서 변수를 선언할 것을 요구한다.나중의 표준에서는, 특히 「for」루프에서 루프 제어 변수의 동시 선언과 초기화를 보다 직관적이고 효율적으로 실시할 수 있다.

이미 언급한 바와 같이, 이에 대한 두 가지 생각의 학파가 있다.

1) 1987년이므로 모든 것을 최상위 기능으로 선언한다.

2) 최초 사용에 가장 가깝고 가능한 한 작은 범위에서 선언한다.

이에 대한 내 대답은 DO BOTH입니다! 설명하겠습니다.

긴 기능의 경우 1) 리팩터링을 매우 어렵게 합니다.개발자가 서브루틴에 반대하는 코드베이스에서 작업하는 경우 함수 시작 시 변수 선언이 50개이고 그 중 일부는 함수의 맨 아래에 있는 for-loop의 "i"일 수 있습니다.

그래서 나는 이것으로부터 최고 PTSD 선언을 개발했고, 옵션 2)를 열심히 하려고 노력했다.

제가 옵션 1로 돌아온 이유는 단 한 가지 때문입니다. 바로 짧은 기능 때문입니다.함수가 충분히 짧으면 로컬 변수가 거의 없고 함수가 짧기 때문에 함수의 맨 위에 배치하면 처음 사용 시에도 근접합니다.

또, 선두에 선언하고 싶지만, 초기화에 필요한 계산을 실시하지 않은 경우의 「declare and set to NULL」의 안티 패턴은, 초기화할 필요가 있는 것이 인수로 수신될 가능성이 있기 때문에 해결됩니다.

그래서 저는 가능한 한 먼저 사용하기에 가깝게 기능 상위에 선언하는 것이 좋다고 생각합니다.둘 다! 서브루틴을 잘 분할하는 방법이 있습니다.

하지만 긴 기능을 사용하는 경우에는 먼저 사용하는 것과 가장 가까운 것을 사용해야 합니다. 그래야 방법을 추출하기가 더 쉬울 수 있기 때문입니다.

제 레시피는 이렇습니다.모든 로컬 변수에 대해 변수를 가져와서 선언을 맨 아래로 이동하고 컴파일한 다음 선언을 컴파일 오류 직전까지 이동합니다.그게 첫 번째 용도입니다.모든 로컬 변수에 대해 이 작업을 수행합니다.

int foo = 0;
<code that uses foo>

int bar = 1;
<code that uses bar>

<code that uses foo>

이제 선언 전에 시작하는 스코프 블록을 정의하고 프로그램이 컴파일될 때까지 끝을 이동합니다.

{
    int foo = 0;
    <code that uses foo>
}

int bar = 1;
<code that uses bar>

>>> First compilation error here
<code that uses foo>

foo를 사용하는 코드가 더 있기 때문에 컴파일되지 않습니다.컴파일러는 foo를 사용하지 않기 때문에 bar를 사용하는 코드를 통과할 수 있었습니다.이 시점에서 두 가지 선택지가 있습니다.기계적인 방법은 컴파일될 때까지 "}"을 아래쪽으로 이동시키는 것입니다.또 다른 선택은 코드를 검사하여 순서를 다음과 같이 변경할 수 있는지 확인하는 것입니다.

{
    int foo = 0;
    <code that uses foo>
}

<code that uses foo>

int bar = 1;
<code that uses bar>

순서를 바꿀 수 있다면 일시적인 값의 수명을 단축하기 때문에 원하는 것이 바로 그 순서일 것입니다.

또 하나 주의할 점은 foo의 값이 그것을 사용하는 코드 블록 간에 유지되어야 하는지 아니면 둘 다 다른 foo일 수 있는지 여부입니다.예를들면

int i;

for(i = 0; i < 8; ++i){
    ...
}

<some stuff>

for(i = 3; i < 32; ++i){
    ...
}

이 상황들은 내 절차보다 더 많은 것을 필요로 한다.개발자는 코드를 분석하여 수행할 작업을 결정해야 합니다.

하지만 첫 번째 단계는 첫 번째 용도를 찾는 것입니다.시각적으로도 가능하지만 선언문을 삭제하고 컴파일을 시도하여 처음 사용 때보다 되돌리는 것이 더 쉬울 수 있습니다.첫 번째 용도가 if 문 안에 있는 경우, 그것을 거기에 두고 컴파일 여부를 확인합니다.그 후 컴파일러는 다른 용도를 식별합니다.두 가지 용도를 모두 포함하는 범위 블록을 만들어 보십시오.

이 기계 부품이 완성되면 데이터가 어디에 있는지 쉽게 분석할 수 있습니다.대형 범위 블록에서 변수를 사용하는 경우 상황을 분석하여 두 가지 다른 항목에 동일한 변수를 사용하는지 확인합니다(루프에 두 개에 사용되는 "i" 등).용도가 관련이 없는 경우 관련 없는 각 용도에 대해 새 변수를 만듭니다.

통사적 관점이 아닌 유지보수의 관점에서 적어도 세 가지 사고방식이 있습니다.

  1. 모든 변수를 함수의 시작 부분에 선언하여 한 곳에 배치하고 포괄적인 목록을 한 눈에 볼 수 있도록 합니다.

  2. 각 변수가 필요한 이유를 수 있도록 가능한 한 처음 사용된 위치에 모든 변수를 선언하십시오.

  3. 가장 안쪽 범위 블록의 선두에 모든 변수를 선언합니다.그러면 가능한 한 빨리 범위를 벗어나 컴파일러가 메모리를 최적화하고 의도하지 않은 곳에서 실수로 변수를 사용했는지 여부를 알 수 있습니다.

나는 일반적으로 첫 번째 옵션을 선호한다. 왜냐하면 다른 사람들은 종종 나에게 선언문을 찾기 위해 코드를 찾도록 강요하기 때문이다.모든 변수를 미리 정의하면 디버거에서 쉽게 초기화하고 감시할 수 있습니다.

더 작은 범위 내에서 변수를 선언하는 경우도 있지만, 좋은 이유 때문에 선언하는 경우가 있습니다. 이 이유는 제가 가진 변수가 거의 없기 때문입니다.예를 들면, 다음의 경우가 있습니다.fork()자 프로세스에만 필요한 변수를 선언합니다.나에게 이 시각적인 지표는 그들의 목적을 상기시키는 데 도움이 된다.

함수의 맨 위 또는 "로컬"에 모든 변수를 선언해야 합니다.답은 다음과 같습니다.

사용하고 있는 시스템의 종류에 따라 다릅니다.

1/ 임베디드 시스템(특히 비행기나 자동차와 같은 생활과 관련된):동적 메모리(예: calloc, malloc, new...)를 사용할 수 있습니다.1000명의 엔지니어와 함께 매우 큰 프로젝트를 수행하고 있다고 상상해 보십시오.새로운 다이내믹 메모리를 할당하고 나서 떼어내는 것을 잊은 경우(더 이상 사용하지 않게 되었을 때)는 어떻게 됩니까?임베디드 시스템이 장시간 실행되면 스택 오버플로가 발생하여 소프트웨어가 손상됩니다.품질을 확인하는 것은 쉽지 않습니다(동적 메모리를 금지하는 것이 가장 좋습니다).

비행기가 30일 이내에 작동하여 꺼지지 않으면 소프트웨어가 손상되면 어떻게 됩니까(비행기가 아직 공중에 있을 때)?

2/ 웹, PC 등의 기타 시스템(메모리 용량이 크다):

사용하는 메모리를 최적화하려면 변수를 "로컬"로 선언해야 합니다.이러한 시스템이 장시간 가동되어 스택오버플로가 발생했을 경우(다이나믹메모리를 떼어내는 것을 잊어버린 것이 원인).PC를 리셋하는 간단한 조작만 하면 된다:P 생활에는 영향이 없다

gcc version 4.7.0 매뉴얼에 기재된 내용을 인용하여 설명하겠습니다.

"컴파일러는 'c90'이나 'c++98'과 같은 몇 가지 기본 표준과 'gnu90'이나 'gnu++98'과 같은 표준 GNU 사투리를 받아들일 수 있습니다.기본 표준을 지정함으로써 컴파일러는 해당 표준을 따르는 모든 프로그램과 이를 위반하지 않는 GNU 확장을 사용하는 프로그램을 받아들입니다.예를 들어 '-std=c90'은 asm 및 type of 키워드 등 ISO C90과 호환되지 않는 GCC의 특정 기능을 끄지만 ISO C90에서 의미가 없는 다른 GNU 확장자(?: 식의 중간 용어를 생략하는 등)는 끄지 않습니다.

질문의 핵심은 옵션 "-std=c89"를 사용해도 gcc가 C89에 준거하지 않는 이유라고 생각합니다.당신의 gcc 버전은 모르겠지만 큰 차이는 없을 것 같습니다.gcc 개발자는 옵션 "-std=c89"는 단지 C89와 모순되는 확장자가 꺼진 것을 의미한다고 말했습니다.따라서 C89에서 의미가 없는 일부 확장 기능과는 관계가 없습니다.또한 변수 선언의 배치를 제한하지 않는 확장자는 C89와 모순되지 않는 확장자에 속합니다.

솔직히 모든 사람들은 "-std=c89" 옵션의 첫눈에 C89에 완전히 적합해야 한다고 생각합니다.하지만 그렇지 않다.처음에 모든 변수가 좋거나 나쁘다고 선언하는 문제는 단지 습관의 문제일 뿐이다.

언급URL : https://stackoverflow.com/questions/288441/variable-declaration-placement-in-c

반응형