2017-03-17 12 views
1

C에서 구조체의 첫 번째 요소는 구조체 자체와 동일한 주소를가집니다. 첫 번째 요소가 POD이면 C++의 비 POD 구조체에서도 마찬가지입니다.C++ 비 포드 시작 주소

struct bar 
{ 
    struct bar *p1, *p2; 
    unsigned char h; 
} 

struct foo 
{ 
    struct bar node; 
    int a; 

    private: 
     int x; 
}; 

int main(void) 
{ 
struct foo A; 
struct bar *ptr; 

ptr = &A.node; 

struct foo *n = ((struct foo*)((char*)(ptr)-(unsigned long)(&((struct foo*)0)->node))); 

return 0; 
} 

내가 "아마도을 offsetof 매크로를 잘못 사용 ... NULL 개체의 비 정적 데이터 멤버 'foo는 :: 노드'에 대한 잘못된 접근"는 얻을 :이 코드 주어진 예를 들어

, 경고.

제 질문은 -이 노드가 foo (같은 주소)의 시작 부분에 있다고 가정 할 수 있습니까?

그래, 난 그냥 재 해석 캐스트를 사용할 수 있으며, 내가 경고 얻을 경우 : C++ 구조체 공공 POD 데이터로 시작하면 객체의 첫 번째 요소 점유율 것 주소, 그래서

struct foo *o = reinterpret_cast<struct foo*>(ptr); 

을 표준 당?

감사

--edit-- In a class with no virtual methods or superclass, is it safe to assume (address of first member variable) == this?는 가능한 답변으로 지적되었다. "개입 액세스 지정자없이 선언 된 (비 노조) 클래스의 비 정적 데이터 멤버는 나중에 객체가 클래스 객체 내에서 더 높은 주소를 갖도록 할당됩니다."즉, 첫 번째 요소 내 경우에는 객체 자체와 동일한 주소입니다.

표준은 그것에 대해 아무 것도 말하지 않기 때문에 나는 그럴 수 없다고 생각합니다. 구조체의 끝에있는 객체를 포인터로 변경하고 필요에 따라 객체를 할당해야 할 수도 있습니다.

+0

C++에서'struct'는 struct 유형의 변수를 선언 할 필요가 없습니다. –

+2

[cppreference] (http://en.cppreference.com/w/cpp/language/data_members)에서 : * "액세스 제어가 다른 멤버는 지정되지 않은 순서로 할당됩니다 (컴파일러가 함께 그룹화 할 수 있음)"* - 'bar'가 첫 번째 멤버로 할당되었다는 것을 보장 할 수 없다는 것을 의미합니다. – UnholySheep

+1

'struct'는 가상 테이블 포인터를 가질 수 있고, 처음에 일부 플랫폼에서 알고있는 것과 일치합니다. 그래서 아니야. – Slava

답변

-4

가상 테이블이 struct의 일부가 될 수 있으므로 Slava에 따라 짧은 대답은 no입니다. 이제 vtable은 표준의 일부가 아닙니다. 그것들은 가상의 것을 구현하고 따라서 다형성을 달성하는 메커니즘 일뿐입니다. 모든 실용적인 이유로 대답은 여전히 ​​아니오입니다.

다음 코드를 참조하십시오

#include <iostream> 

using namespace std; 

struct bar 
{ 
    struct bar *p1, *p2; 
    unsigned char h; 
}; 

struct foo 
{ 
    struct bar node; 
    int a; 

    private: 
     int x; 
}; 

int main(void) 
{ 
    struct foo A; 
    struct bar *ptr; 

    A.node.p1 = new bar(); 
    A.node.p2 = new bar(); 
    ptr = &A.node; 

    struct foo *n = ((struct foo*)((char*)(ptr)-(unsigned long)(&((struct foo*)0)->node))); 
    cout << n->a << endl; 

    return 0; 
} 

코드는 g++ -Wall -Werror -pedantic -std=c++14 test.cpp로 컴파일됩니다. 문제는 내가 p1p2에 메모리를 할당하지 않았다는 것입니다.

+0

그리고 이것은 무엇을 보여줘야합니까? 그리고이 질문에 대한 답변은 무엇입니까 (구체적으로 표준에 대해 묻는 질문)? – UnholySheep

+0

@UnholySheep 음흉한 깃발은이 코드가 표준을 준수하며 문제가 없음을 보장합니다. 그냥 OP가 제대로 작성하지 않았습니다. – user902384

+0

주석 주셔서 감사 합니다만 p1과 p2의 할당은 관련이 없습니다 (포인터로 존재 함). – Marc

1

기준은 [class.mem]에/19 표준 레이아웃 클래스 객체가 아닌 정적 데이터 멤버가 있다면

는, 자신의 어드레스가 첫 번째 비 정적의 어드레스와 동일하다라고 데이터 회원. 그렇지 않으면 그 주소는 첫 번째 기본 클래스 하위 객체의 주소와 동일합니다 (있는 경우). [주의 : 따라서 표준 정렬 구조체 인 객체 내에는 이름없는 패딩이있을 수 있지만 적절한 정렬을 달성하는 데 필요한만큼 처음에는 패딩되지 않을 수 있습니다. -end 노트]

그래서 클래스가 당신이하는 표준 레이아웃 클래스 아닌 경우 표준이 첫 번째 구성원의 주소를 보장 클래스의 주소입니다. 귀하의 경우 그것은 그것의 첫 번째 기본 클래스의 주소입니다. 기본 객체가 없으므로 첫 번째 멤버가 객체의 주소를 공유하므로 객체의 시작 부분에 패딩이 없어 질 수 있습니다.이것은 foo의 주소가 bar 회원의 주소이고 bar이 표준 레이아웃 클래스이므로 p1의 주소이기도하다는 것을 의미합니다.

첫 번째 멤버 다음에 오는 멤버를 얻으려고하면 정의되지 않은 동작이 발생합니다. 클래스 유형 (여기에는 struct 포함)은 정렬을 위해 클래스의 멤버간에 패딩 할 수 있습니다. 즉, 정확히 다른 멤버가 첫 번째 멤버와 관련이있는 위치를 알 수 없습니다.

+0

"bar"가 다음과 같은 경우 : struct foo { struct bar node; int a; objectx X; 객체 Y : ; }; "node"의 reinterpret_cast에서 "foo"의 주소를 얻을 수 있지만 이후에는 아무 것도 나오지 않습니까? – Marc

+0

내가 잘 모르겠다 :'reinterpret_cast' 객체의 포인터/주소가 첫 번째 비 정적 멤버에 대해 엄격한 앨리어싱 규칙을 위반해야합니다. 그렇지 않아야합니까? 또는이 항목이 * "AliasedType 및 DynamicType의 범주에 속하는 지 여부는 둘 다 가능합니다 (각 수준에서 cv 자격이있는 다중 수준 일 가능성이 있음). 동일한 유형 T"* ('reinterpret_cast' (http : // en.cppreference.com/w/cpp/language/reinterpret_cast) 참조 페이지) – UnholySheep

+0

@Marc 예. 'foo'가'struct foo {struct bar node; int a; objectx X; 객관적인 Y; }; 그러면 표준 레이아웃 유형이고'& foo_object' 및'& foo.object.node'는 같은 주소가됩니다. 'node'와'a' 사이에 패딩이있을 수 있기 때문에'a'의 주소가 무엇인지 알 것입니다. – NathanOliver