(C) 타입 Types / Type Casting / 가변 인자
<stdint.h>
를 사용하도록 하자. int
보다 uint8_t
같은 고정 크기 타입을 사용하는 것이 좋다. char
보다 TCHAR
를 사용하는 것이 좋다. 윈도우인 경우 컴파일 옵션에 따라 리눅스 등에서도 호환 가능하기 때문.
Types
1
uintptr\_t
포인터를 대상으로 덧셈, XOR 등 수치 연산이 필요한 경우 포인터를 숫자값으로 취급하기 위해 사용하는 타입. 어떤 포인터라도 담을 수 있는 크기의 데이터 타입이라는 의미가 된다. 따라서 포인터를 대상으로 수치 연산이 필요한 경우 사용한다. (uintptr\_t)this + 4
이렇게 더하기 연산을 해도 1씩 증가하기 때문에, ` uint8_t*같은 것으로 변환하는 것 보다 더 낫다.
uint8_t*로 변환하면 + 같은건 되는데
% / *` 연산자에서 에러난다.
1
(void \*)
단순히 어떤 것을 가리키는 포인터 일 경우 사용한다. uintptr_t
와 달리 원래의 type으로 다시 cast했을 때 제대로 동작함을 보장해준다.
1
size\_t
배열 선언 시 할당할 수 있는 원소 수의 최댓값을 의미한다. 따라서 배열 원소 수의 최댓값에 사용한다.
Type Casting
타입 캐스팅을 적절히 해주지 않으면 계산에서 오류가 발생한다. 특히 포인터가 그렇다. printf()
할 때도 마찬가지.
source :
1
2
3
chunk0\_ptr[1] = (uint64\_t) &chunk0\_ptr - (3\*sizeof(uint64\_t));
printf("chunk0\_ptr[1] : %p / &chunk0\_ptr - 3 : %p\n",
chunk0\_ptr[1], &chunk0\_ptr - (3\*sizeof(uint64\_t)));
result :
1
2
chunk0\_ptr[1] : 0x602050 / &chunk0\_ptr - (3\*sizeof(uint64\_t)) : 0x601fa8
이런 식으로 대입할 때와 같은 값을 출력하도록 했는데 다른 값이 출력되는 이유는, &chunk0_ptr
은 주소이므로 -1
당 c -8
이 되기 때문. ( 64bit OS ) 따라서 - (3\*sizeof(uint64\_t)) ==> - 8\*(3\*sizeof(uint64_t))
이 되어버린다.
이를 방지하려면 대입할 때와 동일하게 형변환을 해주거나, 형변환을 하지 않으려면 -3
만 빼주어야 한다.
1
2
3
chunk0\_ptr[1] : 0x602050 / (uint64\_t) &chunk0\_ptr - 3\*(sizeof(uint64\_t)) : 0x602050
chunk0\_ptr[1] : 0x602050 / &chunk0\_ptr - 3 : 0x602050
~0u
~(uint)0
과 같다. 따라서 0xffffffff
가 된다.
가변 인자
1
2
#include <stdarg.h>
int \_\_printf (const char \*format, ...)
- 가변 인자 함수를 만들기 위해서는
stdarg.h
가 필요하다. - 반드시 1개 이상의 고정 파라미터가 있어야 한다.
...
은 파라미터 가장 뒤에 위치해야 한다. 대충 이런 식으로 동작한다.
1
2
3
4
5
int va\_func(int args, ...) {
va\_list ap;
va\_start(ap, args); // ap가 가변 인자 리스트를 가리키도록 대입.
int arg = va\_arg(ap, int);
va\_end(ap);
const
파라미터로 받는 변수가 실제로는 상수가 아니지만, 함수 내부에서 변경하지 못하도록 설정하고 싶은 경우 const
를 활용할 수 있다.
1
2
3
int f(const int a){
a = 3; // compile time error
}
const type *ptr / type * const ptr
1
2
3
4
5
int a = 3;
int b = 5;
const int \*ptr = &a;
ptr = &b; // right
\*ptr = 10; // error
const int
를 가리키는 포인터. 따라서 \*ptr
을 수정할 수 없다. 이 때 가리키고 있는 대상이 const
가 아니어도, ptr
로 접근하는 경우는 수정 불가능하다.
1
2
3
4
5
int a = 3;
int b = 5;
int \* const ptr = &a;
ptr = &b; // error
\*ptr = 10; // right
int \*
를 가리키는 const
포인터. 따라서 c ptr
을 수정할 수 없다.