2012-11-04 27 views
7

xem xét như sau:Tôi có thể đúc một mảng POD có phao nổi không?

#include <vector> 
using namespace std; 

struct Vec2 
{ 
    float m_x; 
    float m_y; 
}; 

vector<Vec2> myArray; 

int main() 
{ 
    myArray.resize(100); 

    for (int i = 0; i < 100; ++i) 
    { 
    myArray[i].m_x = (float)(i); 
    myArray[i].m_y = (float)(i); 
    } 

    float* raw; 
    raw = reinterpret_cast<float*>(&(myArray[0])); 
} 

raw đảm bảo có 200 nổi tiếp giáp với các giá trị có đúng không? Đó là, tiêu chuẩn có đảm bảo điều này không?

CHỈNH SỬA: Nếu điều kiện trên được đảm bảo và nếu Vec2 có một số chức năng (không phải ảo) và người xây dựng, thì việc đảm bảo vẫn còn đó?

LƯU Ý: Tôi nhận ra điều này rất nguy hiểm, trong trường hợp đặc biệt của tôi, tôi không có sự lựa chọn như tôi đang làm việc với một thư viện của bên thứ 3.

+1

Một câu hỏi tốt đẹp cho ngôn ngữ luật sư :) – Vlad

+0

Nếu công trình này, sẽ không 'nổi * thô = & (myArray [0] .m_x) 'làm tương tự mà không cần đúc? – Axel

Trả lời

5

Tôi nhận ra điều này rất nguy hiểm, trong trường hợp đặc biệt của tôi, tôi không có lựa chọn khi tôi đang làm việc với thư viện của bên thứ ba.

Bạn có thể thêm biên dịch kiểm tra thời gian kích thước cấu trúc:

live demo

struct Vec2 
{ 
    float a; 
    float b; 
}; 

int main() 
{ 
     int assert_s[ sizeof(Vec2) == 2*sizeof(float) ? 1 : -1 ]; 
} 

Nó sẽ tăng sự tự tin của bạn về cách tiếp cận của bạn (mà vẫn an toàn do reinterpret_cast, như đã đề cập).


liệu = reinterpret_cast (& (myArray [0]));

ISO C++ 98 9.2/17:

Một con trỏ tới một đối tượng POD struct, phù hợp chuyển đổi sử dụng một reinterpret_cast, trỏ tới thành viên ban đầu của nó (hoặc nếu thành viên đó là một trường bit, sau đó đến đơn vị mà nó cư trú) và ngược lại. [Lưu ý: Do đó có thể có đệm không tên trong một đối tượng cấu trúc bố trí chuẩn, nhưng không phải ở đầu của nó, khi cần thiết để đạt được sự liên kết phù hợp.lưu ý end]


Và cuối cùng, kiểm tra thời gian chạy của các địa chỉ tương ứng sẽ làm cho giải pháp này khá an toàn. Nó có thể được thực hiện trong quá trình kiểm tra đơn vị hoặc thậm chí ở mỗi lần bắt đầu chương trình (trên mảng thử nghiệm nhỏ).

Đưa nó tất cả cùng nhau:

live demo

#include <vector> 
#include <cassert> 
using namespace std; 
struct Vec2 
{ 
    float a; 
    float b; 
}; 

int main() 
{ 
    int assert_s[ sizeof(Vec2) == 2*sizeof(float) ? 1 : -1 ]; 
    typedef vector<Vec2> Vector; 
    Vector v(32); 
    float *first=static_cast<float*>(static_cast<void*>(&v[0])); 
    for(Vector::size_type i,size=v.size();i!=size;++i) 
    { 
     assert((first+i*2) == (&(v[i].a))); 
     assert((first+i*2+1) == (&(v[i].b))); 
    } 
    assert(false != false); 
} 
+1

C++ 11 có 'static_assert' phù hợp hơn cho mục đích này. –

+0

Tôi biết điều này. Và cũng có BOOST_STATIC_ASSERT. Nhưng mã trên sẽ làm việc trên các trình biên dịch C++ 98, không có bất kỳ thư viện bổ sung nào. –

+1

Khuyến khích việc sử dụng C++ 11 bằng cách hiển thị các tính năng tuyệt vời :) –

-1

Chỉ đảm bảo rằng bạn nhận được đối tượng gốc khi bạn reinterpret_cast đối tượng được truyền trở lại kiểu dữ liệu gốc.

Đặc biệt, raw không được đảm bảo có 200 giá trị liền kề với giá trị chính xác.

+1

Đây là điểm chính. –

+0

Điều này hoàn toàn đúng. – Oswald

+1

Không, tôi không nghĩ vậy. Như Axel đã nhận xét, ví dụ này có thể được đưa ra mà không có phôi nào cả với 'float * raw = & (myArray [0] .m_x)'. 'reinterpret_cast' không liên quan. –

3

Không, điều này không an toàn, vì trình biên dịch được tự do chèn đệm giữa hoặc sau hai float s trong cấu trúc và do đó cấu trúc float s có thể không liền kề nhau.

Nếu bạn vẫn muốn thử nó, bạn có thể thêm biên dịch kiểm tra thời gian để thêm chắc rằng nó sẽ làm việc:

static_assert(sizeof(Vec2) == sizeof(float) * 2, "Vec2 struct is too big!"); 
static_assert(offsetof(Vec2, b) == sizeof(float), "Vec2::b at the wrong offset!"); 
+0

IMHO, lần kiểm tra thứ hai (offsetof) không bắt buộc sau lần đầu tiên. Các thành viên cấu trúc POD đã tăng thứ tự trong bộ nhớ. –

Các vấn đề liên quan