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

Cfaq 2

achivenKakao 2009. 3. 26. 00:47
은근히 이게 항상 헤갈린다..

+

Q 4.3
‘*p++’은 ‘p’를 증가시키는 것인가요, 아니면, ‘p’가 가리키는 것을 증가시
키는 것인가요?
Answer *, ++, --와 같은, 피연산자가 하나인 연산자는 (unary operator라고 합니
다.) 항상 오른쪽에서 왼쪽으로 결합합니다. 따라서 *p++는 *(p++)와 같
습니다; 일단 p를 증가시킨 다음, p가 증가하기 전에 가리키던 곳의 값을
리턴합니다. p가 가리키는 것을 증가시키려면 (*p)++을 (또는, 부작용이
일어나도 상관없는 경우, ++*p를 써도 됩니다) 쓰면 됩니다.


Q 16.7
External structure를 푸는 코드를 받았습니다만, 실행하면 항상 “unaligned
access”라는 에러가 납니다. 이게 무슨 뜻인가요? 실제 코드는 다음과 같
습니다:
struct mystruct {
char c;
long int i32;
int i16;
};
char buf[7], *p;
fread(buf, 7, 1, fp);
p = buf;
s.c = *p++;
s.i32 = *(long int *)p;
p += 4;
s.i16 = *(int *)p;
Answer The problem is that you’re playing too fast and loose with your pointers.
어떤 시스템들은 데이터 값들이 정확히 align되어 있어야만 그 값을 access
할 수 있습니다. 예를 들어, 2-byte short int는 2의 배수 형태를 띄는
주소값에서만 읽을 수 있으며, 4-byte long int는 4의 배수 형태를 띄어야
만 access가 가능합니다. (질문 2.12 참고). (아무 곳이나 가리킬 수 있는)
char * 타입의 포인터를 강제로 int *나 long int * 포인터로 바꿔 쓰면,
프로세서에게 multitype 값을 제대로 align되지 않은 곳에 access하게 만들
게 됩니다.
주어진 역할을 좀 더 올바르게 수행할 수 있는 방법은 다음과 같습니다:
CHAPTER 16. STRANGE PROBLEMS 292
unsigned char *p = buf;
s.c = *p++;
s.i32 = (long *)p++ << 24;
s.i32 |= (long *)p++ << 16;
s.i32 |= (unsigned)(*p++ << 8);
s.i32 |= *p++;
s.i16 = *p++ << 8;
s.i16 |= *p++;

+

어렵다;;

+

Q 4.9
 함수가 범용 포인터(generic pointer)를 레퍼런스(reference)로 받게 하기
위해 다음과 같이 void ** 포인터를 써도 되나요?
void f(void **);
double *dp;
f((void **)&dp);
Answer 이식성이 없습니다. 위와 같은 코드는 동작할 수도 있고, 때때로 좋은 코
드로 여겨지기도 하지만, 이 코드는 “모든 포인터 타입이 실제로 같은 방
식으로 표현된다”를 전제로 하고 있으며, 이 전제는 대부분의 시스템에서
참이지만 그렇지 않은 경우도 있습니다. 질문 5.17을 참고하기 바랍니다.
C 언어에서 범용의 포인터를 가리키는 포인터 (pointer-to-pointer)는 존재
하지 않습니다. void *가 범용의 포인터로 쓰이는 이유는, 단지 다른 포인
터 타입의 값에 대입하거나, 대입될 때, 자동으로 변환(conversion)이 일어
나기 때문입니다; 이러한 변환은 void **에, void *가 아닌 다른 포인터
타입을 대입하거나, 대입될 때는 일어나지 않습니다. 여러분이 void **를
쓸 때 (예를 들어 void **에 * 연산을 써서 원래의 void *를 얻으려고 할
때), 컴파일러는 void *가, 다른 포인터 타입에서 변환되어서 온 것인지를
알 지 못합니다. 그래서 컴파일러는 말 그대로 void *로 인식합니다; 다른
형태의 implicit conversion을 전혀 수행할 수 없습니다.
즉, 여러분이 작업하고자 하는 void **는 반드시 어딘가에 실제로 있는,
void * 값을 가리키고 있는 포인터이어야 합니다. (void **)&dp와 같은
코드는, 강제로 캐스팅하므로 컴파일러가 경고를 출력하지 않지만, 이식성이
떨어지며, 때때로 원하는 결과를 얻지 못할 수 있습니다; 질문 13.9 참고.
만약 void **가 가리키고 있는 포인터가 void *가 아닐 경우, 그리고 그
포인터가 void *와는 다른 크기나 내부 표현을 (internal representation)
쓰고 있을 경우, 컴파일러는 그 값을 올바른 방법으로 얻지 못합니다.
질문에 주어진 코드를 동작하게 만들려면, void * 타입의 임시 변수를 만
드는 것이 바람직합니다:
CHAPTER 4. POINTER 79
double *dp;
void *vp = dp;
f(&vp);
dp = vp;
변수 vp에 할당하고, 얻어내는 방식을 쓰면, 컴파일러가 (필요한 경우) 적
당하게 conversion을 수행할 수 있습니다.
다시 말하지만, 이 모든 논쟁은, 포인터가 타입별로 크기나 표현 방식이 다
르다는 가정 아래에서 이뤄진 것이며, 최근의 시스템에서는 이런 경우가 드
뭅니다. void **에서 발생할 수 있는 문제를 좀 더 쉽게 알기 위해, 다음
과 같은 상황을 생각해 봅시다. 일단 int와 double의 크기와 내부 표현
방식이 다르다고 하고, 다음과 같은 함수를 보기 바랍니다.
void *incme(double *p)
{
*p += 1;
}
그리고 나서 다음과 같은 코드를 실행합니다:
int i = 1;
double d = i;
incme(&d);
i = d;
당연히, i는 1만큼 증가합니다. (이 것은 앞에서 vp라는 임시 변수를 써서
설명했던 void ** 코드와 같은 방식을 쓴 것입니다.) 이와 달리, 아래와 같
은 코드를 실행했다고 생각해 보기 바랍니다:
int i = 1;
incme((double *)&i) /* WRONG */
이 것은, 앞에서 동작하지 않는다고 말했던 (질문에 나온) 코드와 일치합니
다.


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