'개발의 기수'에 해당되는 글 9건

  1. 2008.05.09 DEBUG macro 2 by Orchistro
  2. 2007.12.26 shift 연산 3 by Orchistro

DEBUG macro

개발의 기수 : 2008. 5. 9. 11:52
C 로 프로그래밍을 하는 사람이라면 거의 모두들 한번쯤 디버깅 메세지를 출력하는 문제에 대해 고민해 보았을 것이라 생각한다.
아마도 빌드 모드가 디버그일 경우에만 매크로가 확장되고, 릴리즈일 경우에는 사라지도록 다음과 같은 매크로를 정의해서 사용했을 것이다 :

#if (DEBUG_MODE)
#define DEBUG(x) printf x
#else
#define DEBUG(x)
#endif

그런데 위의 매크로를 쓰면서 불편한 것이 하나 있으니, 바로 다음과 같이 사용할 수 밖에 없다는 것이다 :

if (rc == ERROR)
{
DEBUG(("error!\n"));
}

그렇다. 치명적인(!!!) 약점이 있으니, 바로 괄호를 두개 써야 한다는 것이다.

C99 표준에서 이 문제를 해결할 수 있도록 매크로에 가변 인자를 지원하는 기능(?)이 포함되었으나 그 표준정의 자체가 한계를 가지고 있는 데다가 (함수의 가변 인자와는 달리 반드시 최소 하나의 인자를 받아야만 한다는 제약사항이 있다) 지원하는 컴파일러마저, VC++ 2005 이상에서만 지원한다. 물론 gcc 는 당연히 지원하는 데다가 플러스 알파로, C99 표준의 약점을 해결하기 위한 확장까지 제공하긴 하지만, 여기서 내가 원하는 것은 모든 컴파일러에서 통용되는 방법이므로 매크로 가변인자 기능을 사용하기에는 무리가 있다.

그러면 보기 싫은 두개의 괄호를 어떻게 해결할까...

아패와 같이 DEBUG 매크로를 정의해 보자.

#if (DEBUG_MODE)
#define DEBUG printf
#else
#define DEBUG (void)
#endif

혹은

#if (DEBUG_MODE)
#define DEBUG printf
#else
#define DEBUG while (0) printf
#endif

컴파일러의 최적화에 의존을 하게 되면 필요없는 printf 는 사라지게 된다. 게다가 요즘 컴파일러는 이정도의 최적화는 기본이다.

재미있는 것은, 아래의 것 보다, 함수 이름을 (void) 로 치환해 버리는 것인데,,,

그러면 위의 첫번째 방법을 사용했을 때 아래와 같은 코드는

DEBUG("ERROR!!!");

프리프로세서를 거치고 나면, 디버그 모드에서는

printf("ERROR!!!");

디버그 모드가 아니면

(void)("ERROR!!!");

로 확장되게 된다.

얼핏 보아서는 이게 컴파일이 될까 하는 생각이 들겠지만,
놀랍게도 컴파일이 된다! +_+ (콤마 연산자임)

정말 기발(!)하지 않은가?
나는 수년간 괄호 두개가보기 싫은데도 불구하고 달리 뾰족한 방법을 생각하지 못해서 괄호 두개를 계속 써 왔었는데, 저 방법을 보는 순간 갑자기 마구 억울해지기 시작했다.

Posted by Orchistro

shift 연산

개발의 기수 : 2007. 12. 26. 12:55
32비트 컴퓨터에서

#include

int main(void)
{
unsigned int a = 1;
a <<= 33;
printf("0x%08X\n", a);

return 0;
}



위 코드의 출력값은 얼마일까?

제일 처음 떠오르는 답은 0x00000000 일 것이다.

조금 눈치가 빠른 사람이라면 0x00000000 이 상식적인 답이겠지만
답이 0x00000000 이 아니기 때문에 이런 문제를 내었으리라 짐작하고서는
뭔가가 있다고 생각할 것이다.

정말 뭔가가 있다.
컴파일을 하고,

$ gcc b.c
b.c: In function 'main':
b.c:7: warning: left shift count >= width of type


gcc 가 친절하게 warning 도 내어 준다.


실행을 시키면,
$ ./a.out
0x00000002


마치 circular shift된 것 같은 결과가 나온다.
왜 그럴까?

컴파일러가 << 연산의 operand 에 modular 연산을 취할까?
단지 << 연산인데, 그냥 shl 같은 instruction으로 곧장 매핑시키지 않고
modular연산 따위를 컴파일러가 할까?
아무래도 그건 오버하는 것 같다.

cpu instruction manual 을 살펴 보았다.


Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2B: Instruction Set Reference, N-Z 에서는 shift 연산에 대해서 아래와 같이 이야기하고 있다 :


The destination operand can be a register or a memory location. The count operand can be an immediate value or the CL register. The count is masked to 5 bits (or 6 bits if in 64-bit mode and REX.W is used). The count range is limited to 0 to 31 (or 63 if 64-bit mode and REX.W is used). A special opcode encoding is provided for a count of 1.

역시나, 컴파일러가 오버하는 것이 아니었다.
인텔은 그렇다 해도 다른 CPU, ppc 나 sparc 은 어떨까?
sun 과 aix 에서 테스트 해 본 결과 마찬가지 결과가 나왔다.
sparc v9 instruction set reference manual을 보아도 마찬가지 설명(마스크 아웃한다는)이 나와 있다.

우연히 찾은 것이지만,
재미있는 것이라고 생각되어서 한번 적어 보았다.

Posted by Orchistro