Q 8.9 제 컴파일러에 버그가 있습니다. sizeof('a')의 값이 sizeof(char)인 1
로 나오지 않고, 2가 나옵니다.
Answer 놀랍게도, C 언어에서 문자 상수(character constant)의 타입은 int입니
다. 따라서 sizeof('a')는 sizeof(int)와 같습니다. (C++에서는 조금
다릅니다.) 덧붙여 질문 7.8도 참고하시기 바랍니다.
Note 참고로 C++에서 문자 상수의 타입은 char입니다. 즉, sizeof('a')는
sizeof(char)와 같습니다.
Q 10.1 다음과 같이 간단한, 함수와 비슷한 매크로를 만들려고 합니다.
#define square(x) x * x
그런데, 가끔씩 제대로 동작하지 않습니다. 왜 그럴까요?
Answer 매크로 확장(expansion)은 순수하게 텍스트로 이루어집니다. 즉, 쓰여진 그
대로 확장된다는 뜻이며, 때때로 (위와 같이) 개발자가 원하지 않은 결과를
가져올 수 있습니다. 함수같은 매크로를 정의할 때에는 다음과 같은 세가지
규칙을 지켜야 합니다:
(a) 매크로를 포함한 expression에서 연산자 우선 순위 때문에 원하지 않
은 결과를 얻을 수 있기 때문에, 항상 정의 전체를 괄호로 둘러 싸야
합니다. 예를 들어 질문에서 제공한 square() 매크로를 다음과 같이
썼다고 생각해 봅시다:
i / square(n)
그러면, 위 expression은 다음과 같이 확장됩니다:
i / n * n
따라서, (i / n) * n으로 평가됩니다. 물론 여러분이 의미한 것은 다
음과 같습니다:
i / (n * n)
이 경우는, 우선 순위(precedence)라기 보다는, 결합성(associativity)
에 관계된 문제입니다만, 결과는 같습니다.
(b) 매크로 정의 안에서, 모든 파라메터들은 괄호로 둘러 싸서 원치 않는
우선순위 문제를 제거합니다. 다시 한번, 질문에서 준 매크로를 다음과
같이 썼다고 가정합시다:
square(n + 1)
그러면, 위 expression은 다음과 같이 확장됩니다:
n + 1 * n + 1
즉, 다음과 같이, 우리가 원한 것과는 다른 결과가 나옵니다:
(n + 1) * (n + 1)
(c) 만약에, 매크로가 확장될 때 어떤 파라메터가 여러 번 쓰였다면, 그리
고 매크로에 전달한 actual argument가 side effect를 가지고 있는 것
이라면, 원하는 결과를 얻을 수 없을 수도 있습니다. 예를 들어 다음과
같이 매크로를 쓰면:
square(i++)
다음과 같이 확장되기 때문에, undefined behavior를 발생시킵니다.
(질문 3.2 참고)
i++ * i++
즉, 규칙 1과 2에 따라서, square() 매크로는 다음과 같이 정의해야
합니다.
#define square(x) ((x) * (x))
규칙 3을 지키는 것은 좀 어렵습니다. 때때로, &&나 ||, ?: 연산자의 shortcircuit
방식을 쓰면 (질문 3.6 참고), 파라메터가 딱 한번만 평가되도록 할
수 있습니다. 또는 매크로가 안전하지 않으니, 부를 때, side effect가 발생
하지 않도록 조심하라고 문서에 써 두는 것이 좋습니다. 또는, 안전하게 만
들 수 없는 것이면, 매크로로 만들지 않는 편이 좋을 때도 있습니다.
일반적으로, (스타일에 관계되어) 매크로 이름에는 대문자를 쓰거나, 또는
전부 대문자로 써서, 매크로라는 것을 암시해 줍니다. 만약, 완전하게 함수
와 같이 동작하는 것이라면, 소문자로만 이름을 만드는 것도 괜찮습니다. 그
러나 이 경우, 위 세가지 규칙을 준수하는지 확인하기 바랍니다. 질문에서
제공한 매크로는 위의 세가지 규칙을 지킬 수 없으므로, 다음과 같이 만드
는 것이 낫습니다:
#define Square(x) ((x) * (x)) /* UNSAFE */
로 나오지 않고, 2가 나옵니다.
Answer 놀랍게도, C 언어에서 문자 상수(character constant)의 타입은 int입니
다. 따라서 sizeof('a')는 sizeof(int)와 같습니다. (C++에서는 조금
다릅니다.) 덧붙여 질문 7.8도 참고하시기 바랍니다.
Note 참고로 C++에서 문자 상수의 타입은 char입니다. 즉, sizeof('a')는
sizeof(char)와 같습니다.
Q 10.1 다음과 같이 간단한, 함수와 비슷한 매크로를 만들려고 합니다.
#define square(x) x * x
그런데, 가끔씩 제대로 동작하지 않습니다. 왜 그럴까요?
Answer 매크로 확장(expansion)은 순수하게 텍스트로 이루어집니다. 즉, 쓰여진 그
대로 확장된다는 뜻이며, 때때로 (위와 같이) 개발자가 원하지 않은 결과를
가져올 수 있습니다. 함수같은 매크로를 정의할 때에는 다음과 같은 세가지
규칙을 지켜야 합니다:
(a) 매크로를 포함한 expression에서 연산자 우선 순위 때문에 원하지 않
은 결과를 얻을 수 있기 때문에, 항상 정의 전체를 괄호로 둘러 싸야
합니다. 예를 들어 질문에서 제공한 square() 매크로를 다음과 같이
썼다고 생각해 봅시다:
i / square(n)
그러면, 위 expression은 다음과 같이 확장됩니다:
i / n * n
따라서, (i / n) * n으로 평가됩니다. 물론 여러분이 의미한 것은 다
음과 같습니다:
i / (n * n)
이 경우는, 우선 순위(precedence)라기 보다는, 결합성(associativity)
에 관계된 문제입니다만, 결과는 같습니다.
(b) 매크로 정의 안에서, 모든 파라메터들은 괄호로 둘러 싸서 원치 않는
우선순위 문제를 제거합니다. 다시 한번, 질문에서 준 매크로를 다음과
같이 썼다고 가정합시다:
square(n + 1)
그러면, 위 expression은 다음과 같이 확장됩니다:
n + 1 * n + 1
즉, 다음과 같이, 우리가 원한 것과는 다른 결과가 나옵니다:
(n + 1) * (n + 1)
(c) 만약에, 매크로가 확장될 때 어떤 파라메터가 여러 번 쓰였다면, 그리
고 매크로에 전달한 actual argument가 side effect를 가지고 있는 것
이라면, 원하는 결과를 얻을 수 없을 수도 있습니다. 예를 들어 다음과
같이 매크로를 쓰면:
square(i++)
다음과 같이 확장되기 때문에, undefined behavior를 발생시킵니다.
(질문 3.2 참고)
i++ * i++
즉, 규칙 1과 2에 따라서, square() 매크로는 다음과 같이 정의해야
합니다.
#define square(x) ((x) * (x))
규칙 3을 지키는 것은 좀 어렵습니다. 때때로, &&나 ||, ?: 연산자의 shortcircuit
방식을 쓰면 (질문 3.6 참고), 파라메터가 딱 한번만 평가되도록 할
수 있습니다. 또는 매크로가 안전하지 않으니, 부를 때, side effect가 발생
하지 않도록 조심하라고 문서에 써 두는 것이 좋습니다. 또는, 안전하게 만
들 수 없는 것이면, 매크로로 만들지 않는 편이 좋을 때도 있습니다.
일반적으로, (스타일에 관계되어) 매크로 이름에는 대문자를 쓰거나, 또는
전부 대문자로 써서, 매크로라는 것을 암시해 줍니다. 만약, 완전하게 함수
와 같이 동작하는 것이라면, 소문자로만 이름을 만드는 것도 괜찮습니다. 그
러나 이 경우, 위 세가지 규칙을 준수하는지 확인하기 바랍니다. 질문에서
제공한 매크로는 위의 세가지 규칙을 지킬 수 없으므로, 다음과 같이 만드
는 것이 낫습니다:
#define Square(x) ((x) * (x)) /* UNSAFE */