한 줄에 변수를 선언하고 다음 줄에 변수를 할당하는 이유는 무엇입니까?


101

나는 종종 C 및 C ++ 코드에서 다음과 같은 규칙을 봅니다.

some_type val;
val = something;

some_type *ptr = NULL;
ptr = &something_else;

대신에

some_type val = something;
some_type *ptr = &something_else;

처음에는 이것이 범위의 맨 위에 모든 지역 변수를 선언해야했던 시절부터 남은 습관이라고 가정했습니다. 그러나 나는 베테랑 개발자의 습관을 너무 빨리 무시하지 않는 법을 배웠습니다. 그렇다면 한 줄로 선언하고 나중에 할당해야 할 합당한 이유가 있습니까?


12
"베테랑 개발자의 습관을 너무 빨리 기각하지 않는 법을 배웠습니다." 그것은 배우는 현명한 교훈입니다.
와일드 카드

답변:


92

C89에서 모든 선언 범위 ( { ... }) 의 시작 부분에 있어야 했지만이 요구 사항은 빠르게 떨어졌습니다 (먼저 컴파일러 확장과 표준이 있음).

C ++

이 예제는 동일하지 않습니다. 기본 some_type val = something;생성자를 val = something;호출 한 다음 operator=함수 를 호출하는 동안 복사 생성자를 호출합니다 . 이 차이는 종종 중요합니다.

버릇

어떤 사람들은 변수를 먼저 선언하고 나중에 한 지점에서 선언하고 다른 지점에서 정의를 사용하여 코드를 다시 포맷하는 경우 나중에 변수를 선언하는 것을 선호합니다.

포인터에 관해서는, 어떤 사람들은 포인터로 무엇을 하든지 NULL또는 nullptr에 대한 모든 포인터를 초기화하는 습관을 가지고 있습니다 .


1
C ++에 대한 큰 차이점. 감사합니다. 평범한 C에서는 어떻습니까?
Jonathan Sterling

13
C 모드에서 컴파일 할 때 블록의 시작 부분을 제외하고 MSVC가 여전히 선언을 지원하지 않는다는 사실은 끝없는 자극의 원천입니다.
Michael Burr

5
@Michael Burr : MSVC가 C99를 전혀 지원하지 않기 때문입니다.
orlp

3
"some_type val = something; 복사 생성자 호출": 복사 생성자를 호출 할 있지만 표준에서는 컴파일러가 임시의 기본 구성, val의 복사 구성 및 임시의 직접 생성 및 val을 사용하여 val을 직접 생성 할 수 있습니다. 유일한 주장으로 some_type받아들이 something는 생성자 . 이것은 C ++에서 매우 흥미롭고 특이한 경우입니다 ...이 연산의 의미 적 의미에 대한 추정이 있음을 의미합니다.

2
@ Aerovistae : 내장 유형의 경우 동일하지만 사용자 정의 유형에 대해 항상 동일한 것은 아닙니다.
orlp

27

질문 C와 C ++에 동시에 태그를 달았지만이 언어에서는 답이 크게 다릅니다.

먼저, 질문 제목의 문구가 잘못되었습니다 (또는 더 정확하게는 질문 자체와 관련이 없음). 두 예제 모두에서 변수는 한 줄에 동시에 선언되고 정의 됩니다. 예제의 차이점은 첫 번째 변수에서 변수가 초기화되지 않거나 더미 값으로 초기화 다음 나중에 의미있는 값 이 할당 된다는 것입니다 . 두 번째 예에서 변수는 즉시 초기화 됩니다.

둘째, C ++ 언어에서 @nightcracker가 그의 답변에서 언급 했듯이이 두 구성은 의미 상 다릅니다. 첫 번째는 초기화에 의존하고 두 번째는 할당에 의존합니다. C ++에서 이러한 작업은 오버로드 가능하므로 잠재적으로 다른 결과를 초래할 수 있습니다 (비록 동일한 초기화 및 할당의 오버로드를 생성하는 것은 좋지 않습니다).

원래 표준 C 언어 (C89 / 90)에서 블록 중간에 변수를 선언하는 것은 불법이므로 블록의 시작 부분에 초기화되지 않은 (또는 더미 값으로 초기화 된) 변수가 선언 된 후 의미있는 변수가 할당 될 수 있습니다 나중에 의미있는 값을 사용할 수있게됩니다.

C99 언어에서는 블록 중간에 변수를 선언하는 것이 좋습니다 (C ++에서와 같이). 첫 번째 접근 방식은 선언 시점에서 이니셜 라이저를 알 수없는 특정 상황에서만 필요합니다. (이것은 C ++에도 적용됩니다).


2
@Jonathan Sterling : 예제를 읽었습니다. C 및 C ++ 언어의 표준 용어를 정리해야 할 수도 있습니다. 특히 선언정의 라는 용어에 대해서는 이러한 언어에서 특정 의미를 갖습니다. 다시 반복하겠습니다. 두 예제에서 변수는 한 줄로 선언되고 정의됩니다. C / C ++에서 행 은 변수를 some_type val;즉시 선언 하고 정의 합니다 val. 이것이 내 대답의 의미입니다.

1
무슨 말인지 알 겠어 내가 사용하는 방식이 무의미하다고 선언 하고 정의 하는 것이 옳 습니다. 나는 당신이 불쌍한 말과 엉뚱한 의견에 대해 사과드립니다.
Jonathan Sterling

1
따라서“선언”이 잘못된 단어라는 의견이 일치한다면, 나보다 표준에 대해 더 잘 아는 사람이 Wikibooks 페이지를 편집 할 것을 권합니다.
조나단 스털링

2
다른 문맥에서 선언은 올바른 단어이지만, 선언은 잘 정의 된 개념 이므로 결과적으로 C 및 C ++에서는 다른 문맥 에서처럼 느슨하게 사용할 수 없습니다.
orlp

2
@ybungalobill : 당신이 틀 렸습니다. C / C ++의 선언정의 는 상호 배타적 인 개념이 아닙니다. 실제로, 정의 는 단지 특정한 형태의 선언 입니다. 모든 정의는 동시에 선언입니다 (예외는 거의 없음). 정의 선언 (즉, 정의)과 비정의 선언이 있습니다. 또한, 일반적으로 therm 선언 은 두 정의가 중요한 문맥을 제외하고 항상 (정의 인 경우에도) 사용됩니다.

13

"로컬 선언"시대에 남은 오래된 습관이라고 생각합니다. 따라서 귀하의 질문에 대한 답변으로 : 아니요 좋은 이유가 있다고 생각하지 않습니다. 나는 그것을 직접하지 않습니다.


4

나는에서 그것에 대해 뭔가 말했다 내 대답Helium3하여 질문을 .

기본적으로 변경된 내용을 쉽게 볼 수있는 시각적 도구라고합니다.

if (a == 0) {
    struct whatever *myobject = 0;
    /* did `myobject` (the pointer) get assigned?
    ** or was it `*myobject` (the struct)? */
}

if (a == 0) {
    struct whatever *myobject;
    myobject = 0;
    /* `myobject` (the pointer) got assigned */
}

4

다른 답변은 꽤 좋습니다. C에는 이것에 관한 몇 가지 역사가 있습니다. C ++에는 생성자와 할당 연산자 사이에 차이가 있습니다.

아무도 추가 요점에 대해 언급하지 않았습니다. 선언을 변수 사용과 분리하여 유지하는 것이 때로는 더 읽기 쉽습니다.

시각적으로 말하자면, 코드를 읽을 때 변수의 유형 및 이름과 같은 더 평범한 인공물은 당신에게 튀어 나와 있지 않습니다. 그것은 당신이 일반적으로 가장 관심이 있고, 쳐다 보는 데 많은 시간을 할애 하는 진술 이므로 나머지를 한눈에 보는 경향이 있습니다.

동일한 유형의 공간에서 일부 유형, 이름 및 할당이 진행되는 경우 약간의 정보 과부하가 발생합니다. 또한, 내가 일반적으로 살펴 보는 공간에서 중요한 것이 진행되고 있음을 의미합니다.

말하기에는 다소 직관적이지 않을 수 있지만 소스가 더 수직 공간을 차지하게하면 더 좋을 수 있습니다. 나는 이것이 좁은 수직 공간에서 엄청난 양의 포인터 산술 및 할당을 수행하는 잼으로 채워진 줄을 작성해서는 안되는 이유와 유사하다고 생각합니다. 언어가 그러한 것들을 피할 수 있다고해서 그렇게해야한다는 의미는 아닙니다. 항상. :-)


2

C에서, 이것은 C ++에서와 달리 함수가 시작될 때 변수가 함수 본문의 어느 곳에서나 선언 될 수있을 때와 같이 함수 시작시 선언되어야했기 때문에 표준 관행이었습니다. 포인터가 가비지 없음을 가리 키도록하기 때문에 포인터가 0 또는 NULL로 설정되었습니다. 그렇지 않으면 내가 생각할 수있는 중요한 이점이 없으므로 누군가가 그렇게 할 수 있습니다.


2

변수 정의 지역화 및 의미있는 초기화에 대한 장점 :

  • 변수가 코드에 처음 나타날 때 습관적으로 의미있는 값이 할당되면 (같은 관점에서 또 다른 관점 : 의미있는 값이 유효 할 때까지 모양을 지연시키는 경우) 실수로 의미가 없거나 초기화되지 않은 값 으로 사용될 가능성은 없습니다 ( 조건문, 단락 평가, 예외 등으로 인해 실수로 일부 초기화가 우회되는 경우 쉽게 발생할 수 있음)

  • 효율적일 수있다

    • 초기 값 설정의 오버 헤드를 피합니다 (기본 구성 또는 NULL과 같은 일부 센티넬 값으로 초기화)
    • operator= 때로는 효율성이 떨어지고 임시 객체가 필요할 수 있습니다.
    • 때때로 (인라인 함수의 경우) 옵티마이 저가 일부 / 모든 비 효율성을 제거 할 수 있음

  • 차례로 변수 범위를 최소화하면 범위에서 동시에 평균 변수 수가 최소화 됩니다 .

    • 그것은하게 쉽게 정신적으로 추적하는 범위에서 변수, 그 변수에 영향을 줄 수있는 실행 흐름과 문 및 가치의 수입을
    • 최소한 복잡하고 불투명 한 객체 의 경우 프로그램의 리소스 사용량 (힙, 스레드, 공유 메모리, 설명자) 이 줄어 듭니다.
  • 정의에서 변수 이름을 반복하지 않고 초기 의미있는 할당에서 때때로 더 간결합니다.

  • 참조와 같은 특정 유형 및 객체를 원할 때 필요 const

변수 정의 그룹화를위한 인수 :

  • 때로는 여러 변수 유형을 고려하는 것이 편리하고 간결 합니다.

    the_same_type v1, v2, v3;

    (이유가 유형 이름이 너무 길거나 복잡하기 때문에 typedef때로는 더 좋을 수도 있습니다)

  • 때로는 일부 작업에 관련된 변수 세트 및 유형을 강조하기 위해 사용법과 독립적으로 변수를 그룹화하는 것이 바람직합니다.

    type v1;
    type v2; type v3;

    이것은 유형의 공통성을 강조하고 복사 붙여 넣기, //주석 달기 등 을 용이하게하는 줄 당 변수를 고수하면서 변경하기가 조금 더 쉽습니다 .

프로그래밍의 경우와 마찬가지로 대부분의 상황에서 한 가지 실습에는 분명한 경험적 이점이있을 수 있지만, 다른 한 가지 실습은 실제로 몇 가지 경우보다 훨씬 더 나을 수 있습니다.


새로운 변수가 같은 이름을 사용할 수 있지만 (즉, 이후의 문장이 같은 변수를 사용했는지 여부에 관계없이 동작이 동일한 경우) 코드가 선언하고 변수의 값을 다른 곳에 쓰지 않는 경우를 더 많은 언어가 구별하기를 바랍니다. 코드가 여러 곳에서 쓸 수있는 변수를 만드는 곳과 다른 점]. 두 유스 케이스 모두 동일한 방식으로 실행되지만 버그를 추적 할 때 변수가 언제 변경 될 수 있는지 아는 것이 매우 유용합니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.