컴퓨터공부/C & C++ & STL

Cfaq3

achivenKakao 2009. 3. 27. 01:04

Q 4.12
함수를 호출할 때, 포인터를 써서 호출하는 방식을 봤습니다. 왜 이런 일을
하는 거죠?
Answer 원래, 함수를 가리키는 포인터는 * 연산자를 쓸 경우 (그리고 우선 순위를
위해 괄호를 같이 쓸 경우) 진짜(real) 함수 호출로 변경됩니다:
int r, func(), (*fp)() = func;
r = (*fp)();
위 코드의 마지막 줄의 의미는 다음과 같습니다: fp는 함수를 가리키는 포
인터이고, *fp는 이 함수를 뜻합니다. 그리고 뒤따르는 ()는 함수 호출에
서 쓰이는 인자를 받는 인자 list입니다. 이때 연산자 우선 순위를 고려해서
*fp를 괄호로 둘러쌉니다. 이러면 완벽한 함수 호출이 됩니다.
다음과 같은 이론도 있습니다: 함수는 항상 포인터를 써서 불려지고, 수식
에서 “진짜” 함수 이름은, 초기화에서 이루어지는 것처럼, 항상 함축적으로
포인터로 변환됩니다 (decay): 질문 1.34 참고. 이 이유는 실제로 pcc에서
부터 널리 퍼졌고, ANSI 표준에 채택되었습니다. 따라서 fp가 위와 같이,
함수를 가리키는 포인터일 때, r = fp();는 올바른 표현입니다. (이렇게
쓰는 것은 전혀 혼동스러워 할 필요가 없습니다. 왜냐하면, 함수 이름 뒤에
인자 리스트가 따라올 때, 함수 호출 이외에 다르게 쓰이는 경우가 없기 때
문입니다.) 직접 *을 써서 함수 호출하는 것은, 오래된 컴파일러로 이식이
필요한 경우에 많이 쓰입니다.
덧붙여 질문 1.34도 참고하시기 바랍니다.

Q 4.13
완전하게 ‘generic’한 포인터 타입이 있나요? 제가 쓰는 컴파일러는 함수
포인터를 void *로 변환하려고 하니 경고가 발생합니다.
CHAPTER 4. POINTER 82
Answer 완벽하게, 아무때나 쓸 수 있는 ‘total generic’한 포인터 타입은 존재하지
않습니다. void *는 아무 오브젝트(또는 데이터) 포인터를 저장할 수 있
도록 보장된 포인터입니다; 따라서 함수 포인터를 void *로 변환하는 것은
이식성이 떨어집니다. (어떤 시스템들에서, 함수에 대한 주소값은 매우 큰
값입니다—모든 데이터 포인터가 가질 수 있는 값보다 훨씬 큰)
그렇지만, 모든 함수 포인터들은, 쓰이기 전에 원래의 타입으로 다시 변환된
다는 가정 아래에서, 서로 다른 함수 포인터를 가리킬 수 있도록 보장되어
있습니다. 따라서 아무런 함수 포인터 타입을 하나 정해서 (일반적으로 int
(*)()나 void (*)(). 즉 int나 void를 리턴하는 함수 포인터), ’generic’
한 함수 포인터로 쓸 수 있습니다.
만약, 데이터와 함수를 구별하지 않는 포인터가 필요하다면, 그리고 이식성
이 중요하다면, union을 써서, 한 멤버는 void *로, 다른 하나는 (앞에서
말한) 함수 포인터로 만들어 씁니다.
덧붙여 질문 1.22, 5.8도 참고하시기 바랍니다.

+

union을 이용한 general pointer

union xPtr {
        u8* u8;
        u16* u16;
        u32* u32;
}


Could I now use my xPtr to do better pointer trickery? Can I do stuff like:

xPtr p = &MyData;

TheoByteSet( p.u8, Len );
TheoShortSet( p.u16, Len );
TheoLongSet( p.u32, Len );

+

Q 5.3
포인터가 널 포인터인지 비교하기 위해 “if (p)”라고 쓰는 것이 안전한가
요? 만약 널 포인터의 실제 값이 0이 아닐 경우에는 어떻게 되는 건가요?
Answer 항상 안전합니다. C 언어에서 불리언(boolean) 값이 필요할 때 (예를 들어,
if, while, for 그리고 do와 같은 문장이나, &&, ||, ! 그리고 ?:와 같은
연산자에서), 거짓은 0을 의미하며, 참(true)은 0이 아닌 값을 의미하게 됩
니다. 따라서 다음과 같이 쓰게 되면:
if (expr)
실제로 ‘expr’이 무엇이든지, 컴파일러는 위의 코드를 다음의 코드와 같은
것으로 봅니다.
if ((expr) != 0)
따라서 ‘expr’을 주어진 ‘p’로 바꾸면, ‘if (p)’가 ‘if (p != 0)’이 됩니
다. 그리고 이 수식은 비교를 하는 문맥(comparison context)이기 때문에,
컴파일러는 0이 널 포인터 상수라는 것을 알고, 실제 널 포인터 값으로 변
경해줍니다. 크게 특별한 기술을 사용한 것도 아니고, 컴파일러는 두 경우
CHAPTER 5. NULL POINTERS 88
모두 같은 코드를 만들어 냅니다. 여기에서 실제 널 포인터의 값이 0인지
아닌지는 전혀 문제되지 않습니다.
다음과 같이 불리언 부정(not) 연산자인 !를 쓰는 것은:
!expr
다음과 같이 쓰는 것과 완전히 같습니다:
(expr) ? 0 : 1
또는 다음과 같이 쓸 수 있습니다:
((expr) == 0)
따라서, 다음과 같이 쓰는 것은:
if (!p)
다음과 같이 쓰는 것과 같습니다:
if (p == 0)
줄여서 (abbreviation) if (p)로 쓰는 것은 전혀 문제될 것이 없습니다. 그
러나 어떤 사람들은 이런 식으로 코딩하는 것이 나쁜 습관이라고 말합니다
(물론 어떤 사람들은 좋은 습관이라고 말합니다. 질문 17.10을 참고하기 바
랍니다).


출처 : http://achiven.tistory.com/entry/c-faq-한글판
         http://www.cinsk.org/cfaqs/index-ko.html