CONTAINING_RECORD 매크로

5221 단어 contain
CONTAINING_RECORD 와 같은 매크로 는 다음 과 같은 정 의 를 보 았 습 니 다. \ # define CONTAININGRECORD(address, type, field) ((type *)( (PCHAR)(address) - (ULONG_PTR)(&((type*)0)->field)))class A{      char c;      int a;      short b;}int a = 100;int *pInt = &a;예 를 들 어, 나 는 CONTAINING 을 호출 했다.RECORD(pInt,A,a);완전히 펼 친 후 다음 과 같 습 니 다: (A *) (char *) pInt - (unsigned long) (& (A *) 0) - > a) 이 매크로 를 왜 사용 합 니까?       이 매크로 가 하 는 작업 은 사실 pInt 와 A 구조 에 해당 하 는 유형 값 을 위치 배합 하 는 것 입 니 다.위 에서 int a 가 두 번 째 데이터 에 정 의 된 것 을 볼 수 있 습 니 다. 만약 에 우리 가 a 의 주소 가 있 고 a 를 A * 로 바 꾸 면 a 의 주 소 는 A * 의 첫 번 째 주소 가 되 는 것 이 분명 합 니 다. 그러나 문 제 는 A 의 첫 번 째 요 소 는 char 형 입 니 다. 그러면 pInt 는 구조 중의 a 요소 의 위 치 를 맞 출 수 없습니다.그래서 편 이 량 조작 을 해 야 합 니 다. 다음은 다음 과 같 습 니 다.        우선, 빨간색 부분 은 쉽게 이해 할 수 있 습 니 다. 우 리 는 int * a 가 있다 면 알 고 있 습 니 다.의 지침, 우리 a - 1 은 사실 a - sizeof (int) 에 해당 합 니 다. 지침 을 오른쪽으로 4 개의 위 치 를 옮 기 고 하나의 지침 을 하나의 char * 형 으로 바 꾸 는 것 과 같 습 니 다. 그러면 네 가지 연산 을 할 때 우리 의 정상 적 인 조작 에 따라 (char *) (a - 1) 지침 을 한 위치 로 만 이동 합 니 다.        그 다음 에 파란색 부분 을 보 세 요. 먼저 0 포인터 의 수치 추출 작업 이 틀 리 지 않 습 니 다. 다만 이 값 이 어떤 값 으로 돌아 가 는 지 확실 하지 않 습 니 다. 물론 우리 가 이 값 을 수정 하면 위험 합 니 다.여기 서 사용 하 는 0 위치 포인 터 는 매우 특별 합 니 다. 0 의 위치 에 비해 0 포인 터 는 - > a 의 조작 입 니 다. 돌아 오 는 수 치 는 주소 값 을 얻 은 후에 unsigned long 값 으로 바 뀌 었 습 니 다. 사실은 a 가 구조 체 A 에 비해 몇 개의 위 치 를 옮 겼 습 니까?0 은 시작 주소 입 니 다. 그러면 하나의 - > 조작 에 대해 간단하게 이해 해 야 합 니 다. 사실은 0 (구조 체 시작 주소) + sizeof (a 앞의 데이터) 에 해당 합 니 다. 물론 바이트 정렬 문 제 를 고려 해 야 합 니 다. 그러나 컴 파일 러 는 이것 을 모두 완성 해 줄 것 입 니 다.       마지막 으로, 우 리 는 pInt 의 구조 체 의 첫 번 째 주 소 를 알 게 되 었 습 니 다. a 의 오프셋 주 소 를 알 게 되 었 습 니 다. 그러면 우 리 는 pInt 의 주소 값 - 오프셋 을 pInt 의 주소 값 을 뒤로 돌 린 것 과 같 습 니 다. 그리고 우리 가 다시 A * 를 돌리 면 A * 의 시작 주 소 는 이미 pInt 의 앞 오프셋 주소, 즉 a 맨 앞의 요소 의 주소 값 입 니 다.A 에 게 는 char c 의 주소 입 니 다. 그러면 우 리 는 정확 한 시작 주 소 를 얻 은 다음 에 (A *) 로 전환 하면 우리 의 pInt 는 A * 의 a 주소 와 대응 할 수 있 습 니 다.
=========================
전재 하 다
매크로 CONTAININGRECORD 의 용 도 는 사실 상당히 크 고 편리 하 다. 그 주요 역할 은 구조 체 중의 한 구성원 의 지침 에 따라 이 구조 체 의 지침 을 추산 하 는 것 이다!다음은 간단 한 예 에서 시작 합 니 다. 우 리 는 하나의 구조 체 를 정의 하고 유형 화 합 니 다.
typedef struct{

    int a;

    int b;

    int c;

}ss;

이것 은 매우 간단 한 구조 체 입 니 다. 특별한 것 이 없습니다. 이 구조 체 를 조금 분석 해 보 세 요. 구조 체 의 크기 (바이트): 4 + 4 + 4 = 12 바이트 구성원 a 의 오프셋: 0 구성원 b 의 오프셋: 4 구성원 c 의 오프셋: 8 우 리 는 ss 로 변 수 를 정의 합 니 다. ss = {1, 2, 3};그러면 이때 a, b, c 의 값 은 각각 a = 1, b = 2, c = 3 이다. 사실 컴 파일 러 는 코드 를 생 성 할 때 이렇게 구성원 변수 에 값 을 부여 한다. s 의 주 소 는 0 x 12000000 이 라 고 가정 하면 * (int *) (char *) & s + 0 = 1 이다.    *(int*)((char*)&s + 4) = 2;    *(int*)((char*)&s + 8) = 3;즉, & s 의 주 소 를 바탕 으로 변수의 오프셋 을 추가 하여 구성원 의 지침 을 확인 하고 값 을 부여 하 는 것 입 니 다. 따라서 & s - > a 는 0x12000000 + 0 = 0x12000000 & s - > b 는 0x12000000 + 4 = 0x120000004 & s - > c 는 0x12000000 + 8 = 0x120000008 을 얻 을 것 입 니 다.   +  구성원 변수의 오프셋 = 구성원 변수의 주소 이동 항목: 구성원 변수의 주소 -  구성원 변수의 오프셋 = 구조 체 의 주 소 는 이것 이 바로 우리 가 원 하 는 주소 입 니 다. 바로 뺄셈 을 한 것 이 아 닙 니까? 우선, 구성원 변수의 주 소 는 우리 가 알 고 있 습 니 다. 그 다음 에 우 리 는 구성원 변수의 오프셋 (구성원 b 의 오프셋 으로 가정) 을 받 아야 합 니 다. 어떻게 해 야 합 니까?우 리 는 이렇게 할 수 있다: & s - > b - (unsigned long) & s, 그러면 멤버 b 의 오프셋 을 얻 을 수 있다. 그러나, & s 는 우리 가 필요 로 하 는 것 이다. 분명히 잠시 미지수 이다. 그렇다면... 우리 다시 한 번 뺄셈 을 하 자 (정확 하지 않 은 C 언어 표현 식 이지 만 결 과 는 문제 가 없다. 여 기 는 단지 명확 하 게 보일 뿐이다): & (s - s) - > b -(unsinged long)&(s-s), 그 중에서 유형 이 일치 하도록 하기 위해 서 는 s - s = (ss *) 0 (unsigned long) & (s - s) = (unsigned long) (ss *) 0 = 0 을 직접 생략 하면 됩 니 다. 그러면 화 간 은 & (ss *) 0 - > b - (unsigned long) 0 의 가장 간단 한 결 과 를 얻 을 수 있 습 니 다. & (ss *) 0)- > b, 이것 이 바로 b 의 오프셋 입 니 다 하하, 간단 하 죠? 0 포인터 의 묘 미 는 모두 두 번 의 감법 을 했 을 뿐 입 니 다. 그 중에서 우 리 는 ss 구조 체 의 원형, ss 구조 체 중의 한 구성원 변수 b (사실은 어느 것 이 든 똑 같 습 니 다. 앞에서 지침 을 제공 하 는 그 변수 와 일치 해 야 합 니 다)요약 하면 저 희 는 구조 체 중의 한 구성원 변수의 주소, 이 구조 체 의 원형, 이 구조 체 중의 한 구성원 변수 (앞에서 같은 변수 라면) 의 최종 CONTAINING RECORD 의 정 의 를 제공 해 야 합 니 다.
#define CONTAINING_RECORD(addr,type,field) ((type*)((unsigned char*)addr - (unsigned long)&((type*)0)->field))
  addr:
  type:
  field: ( )

자, 모든 결론 이 나 왔 습 니 다. 이것 은 만능 공식 입 니 다. 멤버 변수 가 어떤 결과 든 정확 합 니 다. 이것 은 첫 번 째 변 수 를 아 는 주소 에 비해 첫 번 째 멤버 의 주소 (pa = & s - > a) 를 알 고 있다 면 가장 간단 한 상황 입 니 다. 유형 전환 을 직접 강제 하면 됩 니 다. (ss *) pa 면 됩 니 다. 이때 & (s)(type *) 0) - > field 이 부분 은 마침 0 이기 때문에 결 과 는 바로 (type *) addr 입 니 다. 가장 간단 한 상황 입 니 다. 또한 우리 가 가장 쉽게 생각 할 수 있 는 상황 입 니 다. 예 를 들 어 링크 요 소 를 구조 체 의 초기 에 두 는 것 입 니 다. 여기까지 이 CONTAINING RECORD 매크로 는 이미 말 을 마 쳤 습 니 다. 지금 은 LIST ENTRY 등 양 방향 링크 를 사용 할 때 이 링크 를 구조 체 의 어느 곳 에 두 든 링크 를 옮 겨 다 닐 때 CONTAINING RECORD 매크로 를 통 해 전체 구조 체 의 주 소 를 정확하게 얻 을 수 있 습 니 다. 링크 의 특정한 요 소 를 제거 할 때 free 전체 구조 체 의 주 소 를 기억 해 야 합 니 다.그래 야 합 니 다. WDK 가 제공 하 는 조작 함 수 는 이 링크 요 소 를 전체 링크 에서 벗 어 나 addr 를 unsigned char * 로 바 꾸 는 이 유 는 포인터 로 계산 할 때 계산 단위 가 1 이기 때 문 입 니 다. 즉, (unsigned char *) addr + 1 = addr + 1, 바 꾸 지 않 으 면 잘못된 것 입 니 다 & (type *) 0 - > field 를 (unsigned long) 으로 바 꾸 는 것 입 니 다.4 개의 바이트 가 넓 은 동시에 표현 식 이 두 개의 포인터 의 산술 조작 으로 구 성 된 것 이 아니 라 C 언어 표준 이 그러한 연산 을 정의 하지 않 았 기 때 문 입 니 다.

좋은 웹페이지 즐겨찾기