IT story

C에서 변수 선언 배치

hot-time 2020. 7. 14. 07:58
반응형

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;
}

의 선언 안 csC89 / ANSI 모드에서 오류가 발생할?


GCC가 C89 또는 ANSI 표준의 일부가 아니더라도 GNU 확장으로 허용하기 때문에 성공적으로 컴파일됩니다. 해당 표준을 엄격히 준수하려면 -pedantic플래그 를 전달해야합니다 .


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

따라서 char c선언은 for 루프 범위 블록의 맨 위에 있으므로 유효합니다. 그러나 char *s선언은 오류가되어야합니다.


블록 맨 위에 변수 선언을 그룹화하는 것은 오래된 기본 C 컴파일러의 한계로 인해 레거시 일 가능성이 높습니다. 모든 현대 언어는 최신 시점에서 지역 변수 선언을 권장하고 때로는 시행하기도합니다. 실수로 임의의 값을 사용하는 위험을 제거하기 때문입니다. 선언과 초기화를 분리하면 가능한 경우 "const"(또는 "final")를 사용하지 못하게됩니다.

C ++은 불행히도 C와의 이전 버전과의 호환성을 위해 이전의 최고 선언 방법을 계속 수용합니다 (하나의 C 호환성은 다른 많은 것들 중에서 하나입니다 ...). 그러나 C ++은 그것을 멀리하려고합니다.

  • C ++ 참조의 디자인은 이러한 최상위 그룹을 허용하지도 않습니다.
  • C ++ 로컬 객체 의 선언과 초기화를 분리 하면 추가 생성자 비용을 지불하지 않아도됩니다. 인수없는 생성자가 없으면 다시 분리 할 수 ​​없습니다!

C99가 C를 같은 방향으로 움직이기 시작합니다.

로컬 변수가 선언 된 위치를 찾지 못할 경우 훨씬 큰 문제가 있음을 의미합니다. 둘러싸는 블록이 너무 길어서 분할되어야합니다.

https://www.securecoding.cert.org/confluence/display/cplusplus/DCL19-CPP.+Initialize+automatic+local+variables+on+declaration


구문 적 관점보다는 유지 관리 성 측면에서 최소한 세 가지 사고가 있습니다.

  1. 함수 시작 부분에 모든 변수를 선언하면 한 위치에있게되고 포괄적 인 목록을 한눈에 볼 수 있습니다.

  2. 모든 변수를 처음 사용한 장소와 최대한 가깝게 선언하면 변수 가 필요한지 수 있습니다.

  3. 가장 안쪽 범위 블록의 시작 부분에 모든 변수를 선언하므로 가능한 빨리 범위를 벗어나 컴파일러가 메모리를 최적화하고 의도하지 않은 위치에서 실수로 변수를 사용하는지 알려줍니다.

나는 일반적으로 첫 번째 옵션을 선호합니다. 다른 사람들은 종종 선언을위한 코드를 찾아야합니다. 모든 변수를 미리 정의하면 디버거에서 쉽게 초기화하고 볼 수 있습니다.

때때로 더 작은 스코프 블록 내에서 변수를 선언 할 것이지만 그 이유는 아주 적습니다. fork()자식 프로세스에서만 필요한 변수를 선언하기 위해 a 다음에 한 가지 예가있을 수 있습니다 . 나에게이 시각적 표시기는 그 목적을 알려주는 유용한 정보입니다.


다른 사람들이 언급했듯이, GC는 'pedantic'검사를 사용하지 않는 한 'C89'모드에 있더라도 이와 관련하여 (그리고 호출 된 인수에 따라 다른 컴파일러) 가능합니다. 솔직히 말하면, pedantic을 사용하지 않는 좋은 이유는 많지 않습니다. 고품질의 현대 코드는 항상 경고없이 컴파일해야합니다 (또는 컴파일러에게 의심스러운 특정 작업을 실수로 의심하는 경우는 거의 없습니다). 따라서 코드 설정을 사용하여 코드를 컴파일 할 수 없으면주의를 기울여야합니다.

C89 requires that variables be declared before any other statements within each scope, later standards permit declaration closer to use (which can be both more intuitive and more efficient), especially the simultaneous declaration and initialization of a loop control variable in 'for' loops.


As has been noted, there are two schools of thought on this.

1) Declare everything at the top of functions because the year is 1987.

2) Declare closest to first use and in the smallest scope possible.

My answer to this is DO BOTH! Let me explain:

For long functions, 1) makes refactoring very hard. If you work in a codebase where the developers are against the idea of subroutines, then you'll have 50 variable declarations at the start of the function and some of them might just be an "i" for a for-loop that's at the very bottom of the function.

I therefore developed declaration-at-the-top-PTSD from this and tried to do option 2) religiously.

I came back around to option one because of one thing: short functions. If your functions are short enough, then you will have few local variables and since the function is short, if you put them at the top of the function, they will still be close to the first use.

Also, the anti-pattern of "declare and set to NULL" when you want to declare at the top but you haven't made some calculations necessary for initialization is resolved because the things you need to initialize will likely be received as arguments.

So now my thinking is that you should declare at the top of functions and as close as possible to first use. So BOTH! And the way to do that is with well divided subroutines.

But if you're working on a long function, then put things closest to first use because that way it will be easier to extract methods.

My recipe is this. For all local variables, take the variable and move it's declaration to the bottom, compile, then move the declaration to just before the compilation error. That's the first use. Do this for all local variables.

int foo = 0;
<code that uses foo>

int bar = 1;
<code that uses bar>

<code that uses foo>

Now, define a scope block that starts before the declaration and move the end until the program compiles

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

int bar = 1;
<code that uses bar>

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

This doesn't compile because there is some more code that uses foo. We can notice that the compiler was able to go through the code that uses bar because it doesn't use foo. At this point, there are two choices. The mechanical one is to just move the "}" downwards until it compiles, and the other choice is to inspect the code and determine if the order can be changed to:

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

<code that uses foo>

int bar = 1;
<code that uses bar>

If the order can be switched, that's probably what you want because it shortens the lifespan of temporary values.

Another thing to note, does the value of foo need to be preserved between the blocks of code that use it, or could it just be a different foo in both. For example

int i;

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

<some stuff>

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

These situations need more than my procedure. The developer will have to analyse the code to determine what to do.

But the first step is finding the first use. You can do it visually but sometimes, it's just easier to delete the declaration, try to compile and just put it back above the first use. If that first use is inside an if statement, put it there and check if it compiles. The compiler will then identify other uses. Try to make a scope block that encompasses both uses.

After this mechanical part is done, then it becomes easier to analyse where the data is. If a variable is used in a big scope block, analyse the situation and see if you're just using the same variable for two different things (like an "i" that gets used for two for loops). If the uses are unrelated, create new variables for each of these unrelated uses.


I will quote some statements from the manual for gcc version 4.7.0 for a clear explanation.

"The compiler can accept several base standards, such as ‘c90’ or ‘c++98’, and GNU dialects of those standards, such as ‘gnu90’ or ‘gnu++98’. By specifying a base standard, the compiler will accept all programs following that standard and those using GNU extensions that do not contradict it. For example, ‘-std=c90’ turns off certain features of GCC that are incompatible with ISO C90, such as the asm and typeof keywords, but not other GNU extensions that do not have a meaning in ISO C90, such as omitting the middle term of a ?: expression."

I think the key point of your question is that why does not gcc conform to C89 even if the option "-std=c89" is used. I don't know the version of your gcc, but I think that there won't be big difference. The developer of gcc has told us that the option "-std=c89" just means the extensions which contradict C89 are turned off. So, it has nothing to do with some extensions that do not have a meaning in C89. And the extension that don't restrict the placement of variable declaration belongs to the extensions that do not contradict C89.

To be honest, everyone will think that it should conform C89 totally at the first sight of the option "-std=c89". But it doesn't. As for the problem that declare all variables at the beginning is better or worse is just A matter of habit.

참고URL : https://stackoverflow.com/questions/288441/variable-declaration-placement-in-c

반응형