조건부 조건의 할당이 나쁜 습관입니까?


35

C에서 두 개의 문자열을 연결하는 함수를 작성한다고 가정 해 봅시다. 작성 방법은 다음과 같습니다.

void concat(char s[], char t[]){
    int i = 0;
    int j = 0;

    while (s[i] != '\0'){
        i++;
    }

    while (t[j] != '\0'){
        s[i] = t[j];
        i++;
        j++;
    }

    s[i] = '\0';

}

그러나 책의 K & R 은 특히 ​​while 루프의 조건 부분에 최대한 포함하여 다르게 구현했습니다.

void concat(char s[], char t[]){
    int i, j;
    i = j = 0;
    while (s[i] != '\0') i++;

    while ((s[i++]=t[j++]) != '\0');

}

어떤 방법이 선호됩니까? K & R이하는 방식으로 코드를 작성하도록 권장하거나 권장하지 않습니까? 다른 사람들이 내 버전을 쉽게 읽을 수 있다고 생각합니다.


38
잊지 마십시오. K & R은 1978 년에 처음 출판되었습니다. 그 이후로 코딩 방식에 몇 가지 작은 변화가있었습니다.
corsiKa

28
가독성은 텔레 프린터와 라인 지향 편집기 시대에 매우 달랐습니다. 모든 것을 한 줄에 넣는 것이 읽기 쉬웠습니다 .
user2357112는

15
나는 그들이 다음과 같은 것 대신에 '\ 0'에 대한 인덱스와 비교를 가지고 있다는 것에 충격을 받았다 while (*s++ = *t++); . 그들의 원래 책은 매우 간결하고 관용적 인 코드를 가지고있었습니다.
user949300

4
가독성은 텔레타이프 시대에도 매우 개인적인 것입니다. 다른 사람들은 다른 스타일을 선호합니다. 많은 명령어가 코드 생성과 관련이있었습니다. 그 당시 일부 명령어 세트 (예 : Data General)는 여러 작업을 하나의 명령어로 만들 수 있습니다. 또한 80 년대 초반에는 괄호를 사용하면 더 많은 명령어가 생성된다는 신화가있었습니다. 코드 리뷰어에게 그것이 신화임을 증명하기 위해 어셈블러를 생성해야했습니다.
cup

10
두 코드 블록은 동일하지 않습니다. 첫 번째 코드 블록은 종료 '\0'를 복사하지 않습니다 t(먼저 while종료). 메모리 위치가 이미 0이 아닌 경우 결과 s문자열을 종료하지 않고 그대로 둡니다 '\0'. 두 번째 코드 블록은 루프 '\0'를 종료 하기 전에 종료 사본을 만듭니다 while.
Makyen

답변:


80

항상 영리함보다 선명함을 선호하십시오. 지난 몇 년 동안 최고의 프로그래머는 아무도 그 코드를 이해할 수 없었습니다. "나는 그의 코드를 이해할 수 없다. 그는 천재 여야한다" 고 그들은 말했다. 요즘 최고의 프로그래머는 누구나 이해할 수있는 코드입니다. 컴퓨터 시간은 이제 프로그래머 시간보다 저렴합니다.

어떤 바보라도 컴퓨터가 이해할 수있는 코드를 작성할 수 있습니다. 훌륭한 프로그래머는 인간이 이해할 수있는 코드를 작성합니다. 파울러

의심의 여지없이, 나는 옵션 A로 갈 것입니다. 그리고 그것은 나의 확실한 대답입니다.


8
매우 냉담한 이데올로기이지만 조건부 할당에 아무런 문제가 없다는 사실이 남아 있습니다. 루프의 앞쪽 또는 루프 앞이나 루프에서 코드를 일찍 종료하는 것이 훨씬 좋습니다.
Miles Rout

26
@MilesRout 있습니다. 함수 인수를 전달하거나 조건을 평가하는 등 예상치 못한 부작용이있는 코드에는 문제가 있습니다. 그것도 if (a=b)쉽게 착각 할 수 있다고 언급조차하지 않습니다 if (a==b).
Arthur Havlicek

12
@Luke : "내 IDE는 X를 정리할 수 있으므로 문제가되지 않습니다." 문제가되지 않는다면 왜 IDE가 "고정"하기 쉽습니까?
Kevin

6
@ArthurHavlicek 나는 당신의 일반적인 요점에 동의하지만 조건부에서 부작용이있는 코드는 실제로 드문 일이 아닙니다 while ((c = fgetc(file)) != EOF).
Daniel Jour

3
+1 "처음에 프로그램을 작성하는 것보다 디버깅이 두 배나 어렵다는 것을 고려할 때, 작성할 때 최대한 영리하다면 어떻게 디버깅 할 것인가?" BWKernighan
Christophe

32

Tulains Córdova의 답변과 같은 황금률은 명료 한 코드를 작성하는 것입니다. 그러나 나는 결론에 동의하지 않습니다. 이 황금률은 코드를 유지하는 일반적인 프로그래머가 이해할 수있는 코드를 작성하는 것을 의미합니다. 그리고 당신 은 코드를 유지하게 될 전형적인 프로그래머가 누구인지 가장 잘 판단합니다.

C로 시작하지 않은 프로그래머에게는 이미 알고있는 이유 때문에 첫 번째 버전이 이해하기 쉬울 것입니다.

C 스타일로 자란 사람들에게는 두 번째 버전이 이해하기가 더 쉬울 것입니다. 코드의 기능과 이해도도 비슷합니다. 세로 공간이 적 으면 화면에 더 많은 컨텍스트를 표시 할 수 있습니다.

자신의 선의에 의존해야합니다. 코드를 이해하기 가장 쉬운 대상은 무엇입니까? 이 코드는 회 사용으로 작성 되었습니까? 그런 다음 대상 독자는 해당 회사의 다른 프로그래머 일 것입니다. 이 프로젝트는 아무도 당신 자신 외에는 할 수없는 개인적인 취미 프로젝트입니까? 그렇다면 당신은 당신의 자신의 목표 청중입니다. 이 코드는 다른 사람들과 공유하고 싶습니까? 그런 사람들이 당신의 목표 청중입니다. 해당 대상과 일치하는 버전을 선택하십시오. 불행히도, 권장하는 선호되는 방법은 없습니다.


14

편집 :s[i] = '\0';이 첫 번째 버전에 추가되어 아래 변형 1에 설명 된대로 수정 했으므로 더 이상 현재 버전의 질문 코드에 적용되지 않습니다.

두 번째 버전은 올바른 장점이 있지만 첫 번째 버전은 그렇지 않습니다. 대상 문자열을 올바르게 종료하지 않습니다.

"조건부 할당"을 통해 " 널 문자 확인 하기 전에 모든 문자 복사"라는 개념을 매우 간결하게 표현할 수 있습니다. 요즘 많은 소프트웨어 엔지니어가이 스타일의 코드를 읽을 수는 없지만 . 첫 번째 버전 사용을 고집한다면

  1. 두 번째 루프가 끝난 후 널 종료를 추가하십시오 (더 많은 코드를 추가하지만 준비가 가치가 있다고 주장 할 수 있습니다) 또는
  2. 루프 본문을 "먼저 할당 한 다음 할당 된 문자를 확인 또는 저장 한 다음 색인을 증가 시키십시오"로 변경하십시오. 루프 중간의 상태를 확인하는 것은 루프가 끊어짐을 의미합니다 (대부분의 순수 주의자들이 겪는 선명도 감소). 할당 된 문자를 저장한다는 것은 임시 변수를 도입하는 것을 의미합니다 (명확성과 효율성 감소). 둘 다 내 의견으로는 이점을 소멸시킬 것입니다.

읽기 쉽고 간결한 것보다 정확합니다.
user949300

5

Tulains Córdova와 hvd의 답변은 명확성 / 가독성 측면을 잘 설명합니다. 조건에서 과제를 선호하는 다른 이유로 범위좁히 겠습니다 . 조건에서 선언 된 변수는 해당 문의 범위 내에서만 사용할 수 있습니다. 나중에 실수로 해당 변수를 사용할 수 없습니다. 에 대한 루프는 나이를 위해이 일을하고있다. 그리고 다가오는 C ++ 17 ifswitch에 대해 비슷한 구문을 도입하는 것이 중요 합니다 .

if (int foo = bar(); foo > 42) {
    do_stuff();
}

foo = 23;   // compiler error: foo is not in scope

3

아니요. 매우 표준적이고 일반적인 C 스타일입니다. 당신의 예제는 단지 for 루프이어야하기 때문에 좋지 않은 것이지만 일반적으로 아무런 문제가 없습니다.

if ((a = f()) != NULL)
    ...

예를 들어 (또는 while).


7
문제가 있습니다. C 조건부에서`! = NULL`과 그 친족은 더 낫습니다. 가치가 참 또는 거짓이라는 개념에 익숙하지 않은 개발자를 대신하기 위해서입니다 (또는 그 반대).
Jonathan Cast

1
@jcast 아니요, 포함하는 것이 더 명확합니다 != NULL.
마일 패주

1
아니요, 더 명확하게 말합니다 (x != NULL) != 0. 결국, 그것은 C가 실제로 점검하고있는 것입니다. 맞습니까?
Jonathan Cast

@jcast 아니요, 그렇지 않습니다. 어떤 것이 거짓이 아닌지 확인하는 것은 어떤 언어로 조건을 쓰는지가 아닙니다.
Miles Rout

"무언가와 다른 것이 아닌지 확인하는 것은 어떤 언어로든 조건을 쓰는 방법이 아닙니다." 정확하게.
Jonathan Cast

2

K & R 시대

  • 'C'는 휴대용 어셈블리 코드였습니다
  • 어셈블리 코드에서 생각한 프로그래머가 사용했습니다.
  • 컴파일러는 많은 최적화를하지 않았다
  • 대부분의 컴퓨터에는“복잡한 명령어 세트”가있었습니다. 예를 들어 while ((s[i++]=t[j++]) != '\0')대부분의 CPU에서 하나의 명령어에 매핑됩니다 (12 월 VAC 예상)

거기에 일

  • C 코드를 읽는 대부분의 사람들은 어셈블리 코드 프로그래머가 아닙니다
  • C 컴파일러는 많은 최적화 작업을 수행하므로 코드를 읽기가 더 간단 해지면 동일한 기계 코드로 번역 될 수 있습니다.

(항상 중괄호 사용에 대한 참고 사항 – 첫 번째 코드 세트는“필요하지 않은”일부로 인해 더 많은 공간을 차지합니다 {}. 제 경험상 이러한 코드는 종종 컴파일러에서 잘못 병합 된 코드를 방지하고 잘못된 ";"게재 위치의 오류를 허용합니다. 도구에 의해 감지됩니다.)

그러나 옛날에는 두 번째 버전의 코드를 읽었습니다. (내가 맞으면!)

concat(char* s, char *t){      
    while (*s++);
    --s;
    while (*s++=*t++);
}

2

이 작업을 전혀 할 수없는 것조차 매우 나쁜 생각입니다. 다음과 같이 구어체 적으로 "세계의 마지막 버그"라고합니다.

if (alert = CODE_RED)
{
   launch_nukes();
}

매우 심각한 실수를 저 지르지는 않지만 실수로 실수 하여 코드베이스에서 찾기 어려운 오류를 일으키는 것은 매우 쉽습니다. 대부분의 최신 컴파일러는 조건부 내에 할당에 대한 경고를 삽입합니다. 그들은 이유가 있기 때문에, 당신은 그것들에주의를 기울이고이 구조를 피하는 것이 좋습니다.


이 경고 전에 CODE_RED = alert컴파일러 오류가 발생하도록 작성 했습니다.
Ian

4
@Ian Yoda 조건부라고합니다. 읽기 어렵다. 불행히도 그것들의 필요성은입니다.
메이슨 휠러

아주 간단한 "소개하기"기간이 지나면 요다 조건은 일반적인 조건보다 읽기가 어렵지 않습니다. 때때로 그들은 더 읽기 쉽습니다 . 예를 들어, 일련의 ifs / elseifs가있는 경우 왼쪽에서 더 강조하기 위해 조건을 테스트하는 것이 IMO를 약간 개선하는 것입니다.
user949300

2
@ user949300 두 단어 : 스톡홀름 증후군 : P
메이슨 윌러

2

두 스타일 모두 잘 형성되고 정확하며 적절합니다. 어느 것이 더 적합한 지는 회사의 스타일 가이드 라인에 크게 좌우됩니다. 최신 IDE는 혼동의 원인이 될 수있는 영역을 명시 적으로 강조하는 라이브 구문 린트를 사용하여 두 가지 스타일을 쉽게 사용할 수 있습니다.

예를 들어, Netbeans 는 다음 표현식을 강조 표시합니다 .

if($a = someFunction())

"사고 할당"의 근거.

여기에 이미지 설명을 입력하십시오

Netbeans에 "그렇습니다. 실제로 그렇게하려고 했어요 ..."라고 명시 적으로 표현하기 위해 표현식을 괄호로 묶을 수 있습니다.

if(($a = someFunction()))

여기에 이미지 설명을 입력하십시오

하루가 끝나면 회사 스타일 지침과 개발 프로세스를 용이하게하는 최신 도구의 가용성으로 요약됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.