Menu

C의 주석: //와 /* */ 완전 정리

C에는 한 줄짜리 //와 여러 줄짜리 /* */ 두 가지 주석 스타일이 있고, 서로 다른 내력과 하나의 중첩 함정을 갖고 있습니다. 둘을 쓰는 방법과 함께, 무엇을 주석으로 남길 가치가 있고 무엇은 그렇지 않은지 살펴봅니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

주석은 컴파일러가 버리는 텍스트입니다. 오로지 나중에 코드를 읽을 사람들을 위해 존재하는데, 그중 한 명은 대개 여러분 자신이죠. C는 두 가지 형태를 제공하고, 각각이 언제 알맞은 도구인지 아는 데는 2분이면 충분합니다.

두 가지 형태

실행해 보세요. 출력은 한 줄입니다. 두 주석 모두 컴파일러가 프로그램을 파싱하기도 전에 삭제되었습니다. 실행 시점에 비용이 들지 않고 실행 파일에 아무것도 더하지 않습니다.

**//**는 물리적인 줄 끝까지 이어집니다. 그 줄에서 뒤에 아무것도 올 수 없으므로, 다음 코드는 보이는 것처럼 동작하지 않습니다.

int x = 5;  // x를 5로 설정  int y = 6;   /* y는 결코 선언되지 않습니다 */

**/* ... */**는 위치가 어디든 첫 번째 */에서 끝납니다. 줄 중간에서 시작하고 끝날 수도 있어 가끔 유용합니다.

int total = price /* 세전 */ + shipping;

왜 두 가지 스타일이 있을까

/* */는 1972년의 원조 C 문법입니다. //는 C++에서 왔고 C99에 와서야 C에 정식으로 추가됐습니다. 이 내력은 오래된 코드를 읽을 때 눈에 띄는 한 가지를 설명해 줍니다. C89로 이식되도록 작성된 라이브러리들은 한 줄짜리 주석에도 /* */를 씁니다. 그들이 여전히 지원하던 낡은 툴체인에서는 //가 컴파일되지 않았기 때문입니다.

오늘날 여러분이 쓸 법한 컴파일러는 모두 둘 다 받아들입니다. 평범한 설명에는 //를, 주석이 정말로 여러 줄에 걸칠 때는 /* */를 쓰세요. 아주 오래된 임베디드 컴파일러를 대상으로 한다면 //에 기대기 전에 확인해 보세요.

주석은 중첩되지 않습니다

이것이 유일한 진짜 함정입니다.

/* 일단 이 부분을 비활성화
   int a = compute();
   /* 그 고전적인 헬퍼 - 계속 지켜볼 것 */
   int b = a * 2;
*/

블록 주석은 첫 번째 */, 즉 3번째 줄의 것에서 끝납니다. 그러면 4번째와 5번째 줄은 다시 살아 있는 코드가 되고, 6번째 줄의 */는 문법 오류입니다. 컴파일러의 메시지는 마지막 줄을 가리키며 원인에 대해서는 전혀 도움이 되지 않습니다.

해결책은 중첩을 제대로 처리해 주는 전처리기를 쓰는 것입니다.

#if 0
    int a = compute();
    /* 그 고전적인 헬퍼 - 계속 지켜볼 것 */
    int b = a * 2;
#endif

#if 0은 결코 참이 아니므로 전처리기가 컴파일러가 보기 전에 #endif까지의 모든 것을 삭제합니다. 안쪽의 주석, 따옴표, 다른 #if 블록에도 끄떡없고, 나중에 정리할 때 검색하기도 쉽습니다.

디버깅 중에 코드 주석 처리하기

한 줄을 잠시 제거하는 것은 주석의 가장 흔한 일상적 용도입니다. 프로그램이 이상하게 동작할 때 문장을 하나씩 비활성화해 보면 어느 것이 문제인지 알 수 있습니다.

printf의 주석을 풀고 다시 실행해서 반복문이 답을 쌓아 가는 과정을 지켜보세요. 출력으로 추적하는 방식이 우아하지는 않지만, C에서는 빠르고 언제나 통합니다. 디버거는 더 많은 것을 알려 주고, printf는 지금 당장 무언가를 알려 줍니다.

이것이 엉망이 되지 않게 해 주는 습관이 둘 있습니다. 주석 처리한 코드는 커밋하기 전에 지우세요. 버전 관리가 예전 버전을 기억하니 여러분이 기억할 필요는 없습니다. 그리고 비활성화한 줄을 일부러 남겨 둘 때는 그 옆에 이유를 적어 두세요.

문서화 주석

함수 위의 블록 주석은 그 함수가 무엇을 하는지, 매개변수가 무엇을 뜻하는지, 그리고 놀라운 점이 있다면 무엇인지 설명하는 자리입니다.

Doxygen 같은 도구는 이런 구조화된 주석을 읽어 레퍼런스 문서를 생성합니다. Doxygen 자체의 스타일은 @param@return 태그와 함께 /** ... */를 씁니다.

/**
 * 섭씨를 화씨로 변환합니다.
 * @param c 섭씨 온도
 * @return 같은 온도를 화씨로 환산한 값
 */
double celsius_to_fahrenheit(double c);

자기 코드라면 어느 쪽이든 괜찮습니다. 중요한 것은 주석이 사람들이 실제로 읽는 선언 옆에, 보통은 헤더 파일에 있어야지 구현 안에 파묻혀 있으면 안 된다는 점입니다.

무엇을 주석으로 남길 가치가 있을까

실제 코드베이스와 부딪혀도 살아남는 규칙은 이것입니다. 무엇을 하는지가 아니라 왜 하는지를 적으세요.

i++;  // i를 증가시킴          <- 코드가 이미 말한 것 외에 아무 정보도 없음
/* BOM을 건너뜁니다: 예전 시스템에서 내보낸 파일은
   데이터에 속하지 않는 3바이트로 시작합니다. */
offset += 3;

두 번째 주석에는 코드 어디에도 없는 정보가 담겨 있습니다. 첫 번째는 잡음이며, 코드가 바뀌어도 주석은 갱신되지 않으므로 언젠가는 자기가 설명하는 줄과 모순되고 말 것입니다.

특히 C에서 주석을 달 가치가 진짜로 있는 것들은 이렇습니다.

  • 이 메모리를 누가 소유하는가. 함수가 호출자가 free해야 하는 포인터를 반환한다면 그렇게 적으세요. C에는 그것을 타입으로 표현할 방법이 없습니다.
  • 단위와 범위. int timeout;은 모호합니다. 초인가요, 밀리초인가요?
  • 자명하지 않은 정확성. 왜 반복문이 n - 1에서 멈추는지, 왜 이 캐스트가 안전한지, 왜 버퍼가 256바이트인지.
  • 의도적인 기이함. 버그처럼 보이지만 버그가 아닌 코드는, 표시해 두지 않으면 나중에 읽는 사람들의 "수정"을 불러옵니다.

이 주석은 제값을 합니다. 그 아래 줄은 불필요해 보이지만 실은 그렇지 않으니까요.

문자열 안의 주석은 주석이 아닙니다

마지막 한 가지입니다. 주석 표시는 문자열 리터럴이나 문자 상수 안에서는 아무런 특별한 의미가 없습니다.

두 줄 모두 그대로 출력됩니다. 컴파일러는 주석을 찾기 전에 문자열을 토큰화하므로, 따옴표 안의 //는 그냥 문자 두 개입니다. (첫 줄의 %%printf로 퍼센트 기호 자체를 출력하는 방법입니다. % 하나만 쓰면 형식 지정자가 시작되니까요.)

자주 묻는 질문

C에서 주석은 어떻게 쓰나요?

두 가지 방법이 있습니다. // 이것은 주석입니다는 줄 끝까지 이어집니다. /* 이것은 주석입니다 */는 줄 수에 상관없이 이어지며 닫는 */에서 끝납니다. 둘 다 컴파일 전에 제거되므로 프로그램에 아무런 영향을 주지 않습니다.

C도 // 주석을 지원하나요?

네, C99부터 지원합니다. C++에서 빌려온 것이고 오늘날에는 어디서나 쓸 수 있습니다. 정말 오래된 C89 컴파일러만 이를 거부하는데, 그래서 아주 오래된 코드는 한 줄짜리에도 전부 /* */를 씁니다.

C에서 주석을 중첩할 수 있나요?

없습니다. /* 바깥 /* 안쪽 */ 아직 바깥 */첫 번째 */에서 끝나므로 아직 바깥 */이 깨진 코드로 남습니다. 이미 /* */ 주석을 품고 있는 블록을 비활성화하려면 대신 #if 0 ... #endif를 쓰세요. 이쪽은 중첩이 제대로 됩니다.

C에서 코드 블록을 통째로 주석 처리하려면 어떻게 하나요?

안에 블록 주석이 없다면 /* */로 감싸거나, 각 줄 앞에 //를 붙이세요. 넓은 영역에 확실한 선택지는 앞에 #if 0, 뒤에 #endif를 두는 것입니다. 전처리기가 그 사이의 모든 것을 제거하며, 안쪽의 주석이나 따옴표에도 끄떡없습니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기