2010-05-02 10 views
10

2 मैट्रिक्स structs है बराबर डेटा का मतलब है, लेकिन इन की तरह अलग अलग रूप है:सी संरचना को कैसे एक और संरचना प्रकार कास्ट करने के लिए करें यदि उनकी मेमोरी आकार बराबर है?

// Matrix type 1. 
typedef float Scalar; 
typedef struct { Scalar e[4]; } Vector; 
typedef struct { Vector e[4]; } Matrix; 

// Matrix type 2 (you may know this if you're iPhone developer) 
// Defines CGFloat as float for simple description. 
typedef float CGFloat; 
struct CATransform3D 
    { 
    CGFloat m11, m12, m13, m14; 
    CGFloat m21, m22, m23, m24; 
    CGFloat m31, m32, m33, m34; 
    CGFloat m41, m42, m43, m44; 
}; 
typedef struct CATransform3D CATransform3D; 

उनकी स्मृति आकार के बराबर हैं। तो मेरा मानना ​​है कि वहाँ एक रास्ता इस तरह किसी भी सूचक आपरेशन के बिना इन प्रकार बदल सकते हैं या कॉपी करने के लिए है:

// Implemented in external lib. 
CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz); 
Matrix m = (Matrix)CATransform3DMakeScale (1, 2, 3); 

यह संभव है? वर्तमान में संकलक एक "त्रुटि: गैर-स्केलर प्रकार के अनुरोध में रूपांतरण" संदेश प्रिंट करता है।

उत्तर

16

शायद आपके दो structs को एक संघ में जोड़ना सबसे अच्छा समाधान होगा। यदि आप एक ही डेटा को दो अलग-अलग तरीकों से समझना चाहते हैं तो यह स्पष्ट विकल्प है। विकल्प पॉइंटर कास्ट का उपयोग करना है, लेकिन यह बदसूरत है, एलियासिंग नियमों को तोड़ता है, और उन त्रुटियों को छुपा सकता है जो अन्यथा संकलक द्वारा रिपोर्ट किए जा सकते हैं।

typedef float Scalar; 
typedef struct { Scalar e[4]; } Vector; 
typedef struct { Vector e[4]; } Matrix; 

struct CATransform3D 
{ 
    CGFloat m11, m12, m13, m14; 
    CGFloat m21, m22, m23, m24; 
    CGFloat m31, m32, m33, m34; 
    CGFloat m41, m42, m43, m44; 
}; 
typedef struct CATransform3D CATransform3D; 

typedef union 
{ 
    CATransform3D t; 
    Matrix m; 
} UMatrix; 

CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz); 
UMatrix um; 
um.t = CATransform3DMakeScale (1, 2, 3); 
// 
// now you can just use um.m when you need to refer to the Matrix type... 
// 
+1

शानदार। लेकिन मेरी राय में, यह एक प्रकार का प्रॉक्सी वैरिएबल की आवश्यकता है। क्या यह कुछ प्रतिलिपि नहीं बनाता है? – Eonil

+0

नहीं - बस एक टाइपिफ़ के रूप में एक संघ को परिभाषित करें - उदाहरण के ऊपर देखें। –

+1

संघ सबसे अच्छा विचार है। अधिकांश प्रकार के सुरक्षित, सरल रूपांतरण, कोई ओवरहेड नहीं। – Puppy

1

ठीक है, तुम सिर्फ इस तरह CATransform3DMakeScale घोषित कर सकता है:

Matrix CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz); 

सी नहीं सुनिश्चित करें कि आपके मंदी मूल पुस्तकालय से मेल खाता बनाने के लिए टाइप-जाँच करता है। यदि मेमोरी लेआउट समान है, तो यह काम करेगा। हालांकि, यह खराब कोडिंग अभ्यास है और आपको अपने कार्यों को न्यायसंगत बनाने में एक लंबी टिप्पणी शामिल करनी चाहिए। ;-)

अन्यथा, इसके चारों ओर कोई रास्ता नहीं है: आपको पॉइंटर्स का उपयोग करना होगा या डेटा कॉपी करना होगा। यह काम करेगा:

CATransform3D m3d = CATransform3DMakeScale (1, 2, 3); 
Matrix m; 
memcpy(&m, &m3d, sizeof m); 

जैसा कि आपने पाया है, आप संरचना को सीधे नहीं डाल सकते हैं। तुम भी ऐसा नहीं कर सकते:

Matrix m = *(Matrix*) &CATransform3DMakeScale (1, 2, 3); 

क्योंकि सी ही आप एक एल-मूल्य पर & ऑपरेटर का उपयोग करने के लिए अनुमति देता है।

+1

यह पॉइंटर कास्ट के माध्यम से 'पनिंग टाइप' है - यह एलियासिंग नियमों को तोड़ता है और किसी भी सभ्य कंपाइलर के साथ चेतावनी उत्पन्न करना चाहिए। देखें: http://en.wikipedia.org/wiki/Type_punning। ध्यान दें कि यूनियन चाल भी सख्ती से यूबी है लेकिन आमतौर पर पॉइंटर कास्ट विधि से सुरक्षित माना जाता है। –

+1

@ पॉल आर: अच्छा बिंदु। मैंने अपना जवाब अपडेट कर लिया है। –

संबंधित मुद्दे