offsetof 매크로에 대해
글쓴이: icanfly / 작성시간: 수, 2003/12/24 - 3:21오후
얼마전에 offsetof 매크로란게 있다는걸 알았습니다.
class또는 struct 내부에 있는 멤버와 그 class,struct와의 메모리 오프셋
을 구할때 쓴다는 식으로 막연하게 밖에 이해가 안되는데요. 소스를 한번 열
어 봤더니 VC++, MinGW에서 아래와 같이 구현되 있더군요.
VC++ 6.0
#define offsetof(s,m) (size_t)&(((s *)0)->m)
MinGW 3.x
#ifndef __cplusplus #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #else /* C++ */ /* The reference cast is necessary to thwart an operator& that might be applicable to MEMBER's type. See DR 273 for details. */ #define offsetof(TYPE, MEMBER) (reinterpret_cast <size_t> \ (&reinterpret_cast <char &>(static_cast <TYPE *> (0)->MEMBER)))
어째 상용 컴파일러가 더 허접하게 정의 된듯합니다만....
중요한건 도무지 저 코드들이 뭘 뜻하는지 모르겠다는 것입니다.
좀 자세하게 부연 설명을 해주시면 정말 감사하겠습니다.
그럼...
Forums:
코드 그대로 아닐까요?offsetof (int, 100)이 된다면
코드 그대로 아닐까요?
offsetof (int, 100)이 된다면
&((int *)0)->100
이 되겠죠?
메모리 0번지 부터 100바이트를 뛰어서의 번지를 나타내는 것이 아닌지요?
즉, 위 코드대로라면 100번지를 나타내는 것이라고 생각합니다.
(역시 C언어는 암호문이 맞는것 같네요. :oops: )
------------------------------
좋은 하루 되세요.
Re: offsetof 매크로에 대해
예를 들어서 다음의 구조체를 생각해 보면...
내가 s_a 인 구조체 a 의 주소를 알고 있을 때 (&a 겠죠) 그 안에 있는 멤버 c 의 주소가 a 의 주소로부터 얼마나 떨어져 있느냐(즉 offset) 를 알고 싶을 때가 있겠죠.
답이 2일 수도 있고 4일 수도 있겠죠. short b 바로 뒤에 이어서 c 가 저장된다면 전자가 되겠고 아키텍처가 4바이트 단위로 정렬을 하는 경우 후자가 되겠죠.
그러면 이 오프셋을 알고 싶을 때 매번 아키텍처가 뭔지를 판단하면서 계산을 해야 하느냐..의 답이 저 코드인거죠.
캐스팅 뒤에 0 이 있어서 혼란스럽지만 그 자리에 포인터 변수가 있다고 상상하면 간단해 집니다.
이 때,
이 값은 아키텍처에 따라서 &a , 즉 p_void 보다 2가 클 수도, 4가 클 수도 있겠죠? 이 때 저 p_void 의 값이 0 이었다면? 2 아니면 4가 반환되는 겁니다. 그게 위 매크로인 거죠.
사용법은
offsetof(struct s_a, c)
쯤 되겠네요. 반환값은 이 경우 아키텍처에 따라 2 아니면 4.
좋은 하루 되세요!
명쾌한 답변 정말 감사 드립니다. 그건 그렇고 답변을 보고 그냥
명쾌한 답변 정말 감사 드립니다.
그건 그렇고 답변을 보고 그냥 심심해서 다음과 같은 생각을 해봤는데요.
0번지에서 부터 포인터 연산을 하지 않고, 있는 그대로 다음과 같이 표현해도
원하는 결과를 얻을 수 있을거 같습니다.
위 표현이 적법하다면
가 된다는 말이고, 이를 전개해서 증명할 수 있지 않을까 하는 이상한
생각을 해보았습니다.
위 식에서 우변은
와 같으므로, &()를 밖으로 뽑아내면
가 되고.....-_-;
다음은..모르겠네요..
그냥 잡담이었습니다.
Re: offsetof 매크로에 대해
라고 하셨는데 정확히 말하면
는 아니고
가 됩니다. 따라서
&(((struct s_a *) 0)->c) 의 값이 4 였다면
&(((struct s_a *) 1)->c) 은 5(4+1) 가 출력되고
&(((struct s_a *) 2)->c) 은 6(4+2) 가 출력됩니다.
단 (((struct s_a *)0)->c) 이렇게 c 멤버값을 알아낼려고 하면 안되겠죠
Re: offsetof 매크로에 대해
^^; 맞는 말씀입니다. 그래서 그 위에
p_void = p_a;
라고 했지요. 그러니 멤버값을 알아내는 데는 문제가 없겠죠. :-)
((type *) 0)
이란 형태에 혼란스러워 하는 경우가 있어서 (사실은 바로 얼마전에도 이 코드 얘기를 다른 사람들과 했었거든요) 흔히 쓰는 형태의 예를 먼저 든 겁니다.
좋은 하루 되세요!
[quote="icanfly"]명쾌한 답변 정말 감사 드립니다.
포인터의 뺄셈은 정수의 뺄셈처럼은 안 되죠? :-) 게다가 위의 경우는 왼쪽은 s_a 구조체의 포인터고 오른쪽은 int 의 주소이니까 컴파일 자체가 안 됩니다. gcc 로 해 봐도 저 상태는 컴파일 에러가 나네요.
포인터의 뺄셈은 같은 형의 포인터끼리만 가능하고 그 결과도 정수의 차이가 아니라 (정수의 차이/해당 타입의 사이즈) 값이 나오니까... 양쪽을 struct s_a * 로 캐스트하면 0이 나오고 (2가 되든 4가 되든 어쨌거나 struct s_a 의 크기보다 작으니까) 양쪽을 int * 로 캐스트하면 나올 수 있는 값은 0 아니면 1인데, 그나마도 우측의 주소값이 더 크니까 -1 이 나오는군요.
아하, 방금 생각났는데, 양쪽을 (char *) 로 캐스트하면 되겠군요. ^^; 뺄셈의 순서도 바꿔서
(char *) &(((struct s_a *)p_void)->c) - (char *)p_void
요러면 4가 나오는군요. 근데 이러느니... ^^;;
좋은 하루 되세요!
include/linux/list.h에서 리눅스가 리스트를 처리하는 방식
include/linux/list.h에서 리눅스가 리스트를 처리하는 방식입니다.
좀 더 살펴보았더니 offsetof 는 POD 객체에 대해서만 유효하다고
좀 더 살펴보았더니 offsetof 는 POD 객체에 대해서만 유효하다고 나오는군요. class에 대해서 offsetof를 적용했을때, 이와 같은 워닝이 나서..그런데..POD 가 뭔지 몰라 인터넷을 뒤졌더니 이렇게 나와있네요.
실제로 명시적인 생성자가 있을때 컴파일러는 워닝을 내고. 명시적 생성자는
는 없고, 멤버 함수를 가지고 있을때는 워닝을 내지 않고 깨끗하게 컴파일 되었
습니다.
단, VC++ 6.0에 있는 CL컴파일러에서는 두경우다 아무런 워닝을 내지 않았습니다. 워닝레벨을 높여도 offsetof에 대한 워닝은 내지 않더군요.
역시 지식은 여러 사람들과 공유하면서 깊어지고 넓어지나봅니다.
그럼...
댓글 달기