문제
다음과 같이 테스트 구조 정의가 있습니다.
struct test{
int a, b, c;
bool d, e;
int f;
long g, h;
};
그리고 어딘가에 나는 이런 식으로 사용합니다.
test* t = new test; // create the testing struct
int* ptr = (int*) t;
ptr[2] = 15; // directly manipulate the third word
cout << t->c; // look if it really affected the third integer
이것은 내 창문에서 올바르게 작동합니다. 예상대로 15 인쇄하지만 안전합니까? 변수가 메모리의 현장에 있다고 확신 할 수 있습니까? 원하는 결합 된 스트러크의 경우 직접적으로 원시적으로 (예 : F는 내 컴파일러에 다섯 번째 단어이지만 6 번째 변수입니까)?
그렇지 않은 경우, 코드에 실제로 구조물> 멤버 구성이 없으면 구조물 멤버를 직접 조작하는 다른 방법이 있습니까?
해결책
두 가지 질문을하는 것 같습니다
3 길이의 int arrray로 치료 및 테스트하는 것이 안전합니까?
이것을 피하는 것이 가장 좋습니다. 이것은 C ++ 표준에서 정의 된 조치 일 수 있지만, 그렇지 않더라도 함께 일하는 모든 사람이 여기서하고있는 일을 이해할 가능성은 낮습니다. 스트러크를 패드 할 수있는 잠재력 때문에 표준을 읽으면 이것이 지원되지 않는다고 생각하지만 확실하지 않습니다.
이름이없는 회원에게 액세스하는 더 좋은 방법이 있습니까?
예. 사용해보십시오 오프셋 매크로/운영자. 이렇게하면 구조 내 특정 멤버의 메모리 오프셋을 제공하며 해당 멤버에게 지점을 올바르게 배치 할 수 있습니다.
size_t offset = offsetof(mystruct,c);
int* pointerToC = (int*)((char*)&someTest + offset);
또 다른 방법은 C의 주소를 직접 가져 오는 것입니다.
int* pointerToC = &(someTest->c);
다른 팁
아니오 확실하지 않습니다. 컴파일러는 구조 부재 사이에 패딩을 자유롭게 소개 할 수 있습니다.
추가합니다 Jaredpar의 대답, C ++의 또 다른 옵션 (일반 C가 아님)은 포인터 투 관련 객체를 만드는 것입니다.
struct test
{
int a, b, c;
bool d, e;
int f;
long g, h;
};
int main(void)
{
test t1, t2;
int test::*p; // declare p as pointing to an int member of test
p = &test::c; // p now points to 'c', but it's not associating with an object
t1->*p = 3; // sets t1.c to 3
t2->*p = 4; // sets t2.c to 4
p = &test::f;
t1->*p = 5; // sets t1.f to 5
t2->*p = 6; // sets t2.f to 6
}
당신은 아마도 offsetof
매크로. 이렇게하면 멤버의 바이트 오프셋이 제공됩니다. 그런 다음 해당 오프셋에서 멤버를 조작 할 수 있습니다. 그러나이 매크로는 구현에 따라 다릅니다. 포함 stddef.h
작동하기 위해.
아마도 안전하지 않으며 100% 읽을 수 없습니다. 따라서 실제 생산 코드에서 이러한 종류의 코드를 용납 할 수 없게 만듭니다.
세트 메소드와 부스트를 사용하십시오 ::이 변수를 변경할 수있는 functor를 만들기위한 바인드.
패딩/정렬 문제 외에 다른 답변이 제기 된 것 외에도 코드는 엄격한 별칭 규칙을 위반하므로 최적화 된 빌드에 대해서는 중단 될 수 있습니다 (MSVC 가이 작업을 수행하는 방법은 확실하지 않지만 GCC -O3
이러한 유형의 행동에서 깨질 것입니다). 본질적으로. 왜냐하면 test *t
그리고 int *ptr
유형이 다르고 컴파일러는 메모리의 다른 부분을 가리키고 있다고 가정 할 수 있으며 작동을 재정렬 할 수 있습니다.
이 사소한 수정을 고려하십시오.
test* t = new test;
int* ptr = (int*) t;
t->c = 13;
ptr[2] = 15;
cout << t->c;
끝의 출력도 마찬가지입니다 13
또는 15
, 컴파일러가 사용하는 작동 순서에 따라.
표준의 9.2.17 항에 따르면, 실제로 구조물을 제공하는 첫 번째 멤버와의 포인터에 포인터 간 스트럭을 시전하는 것은 합법적입니다. 현물 상환 지불:
reinterpret_cast를 사용하여 적절하게 변환 된 포드 스트럭 객체에 대한 포인터는 초기 멤버를 가리 키십시오 (또는 해당 멤버가 비트 필드 인 경우, 그것이 상주하는 단위로) 그 반대도 마찬가지입니다. [참고 : 따라서 포드 스트럭 객체 내에 이름이없는 패딩이있을 수 있지만 적절한 정렬을 달성하기 위해 필요한 경우 처음에는 그렇지 않을 수 있습니다. ]
그러나 표준은 스트러크의 레이아웃 (심지어 POD 스트러크)에 대해 보장하지 않습니다.private:
, protected:
또는 public:
) 그들 사이에. 그래서, 당신의 초기 부분을 취급합니다 struct test
3 개의 정수 배열은 기술적으로 정의되지 않은 동작입니다.