2008-09-14 22 views
8

में एक बहुत बड़ी 2 डी सरणी के आसपास कैसे काम करें I 800x800 आकार की 2 डी int सरणी बनाने की आवश्यकता है। लेकिन ऐसा करने से एक ढेर ओवरफ्लो (हा हा) बनाता है।सी ++

मैं सी ++ में नया हूं, तो क्या मुझे वैक्टर के वेक्टर की तरह कुछ करना चाहिए? और बस एक कक्षा में 2 डी सरणी encapsulate?

विशेष रूप से, यह सरणी एक ग्राफिक्स प्रोग्राम में मेरा zbuffer है। मुझे स्क्रीन पर प्रत्येक पिक्सेल के लिए एक जेड वैल्यू स्टोर करने की आवश्यकता है (इसलिए 800x800 का बड़ा आकार)।

धन्यवाद!

उत्तर

11

आपको लगभग 2.5 मेग्स की आवश्यकता है, इसलिए केवल ढेर का उपयोग ठीक होना चाहिए। आपको एक वेक्टर की आवश्यकता नहीं है जब तक कि आपको इसका आकार बदलने की आवश्यकता न हो। "2 डी" हीप सरणी का उपयोग करने के उदाहरण के लिए C++ FAQ Lite देखें।

int *array = new int[800*800]; 

(काम पूरा हो जाने delete[] के लिए यह मत भूलना।)

+0

आपको स्मृति को 'नया' करने की आवश्यकता नहीं है। संभवतः ज़बफर को कार्यों में रहने की आवश्यकता होगी। इसे वैश्विक घोषित करें (या यदि आपने मुझे लगता है कि कक्षा में कुछ वर्ग के लिए अव्यवस्थित है)। आप कुछ ऐसा कर सकते हैं जैसे 'int zBuffer [WIDTH * HEIGHT];' यह मानते हुए कि चौड़ाई और ऊंचाई बदल नहीं है। – Mark

+2

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

2

आप वैक्टर का एक वेक्टर कर सकता है, लेकिन है कि कुछ भूमि के ऊपर होगा। ज़ेड-बफर के लिए 800 * 800 = 640000 आकार की सरणी बनाने के लिए अधिक सामान्य विधि होगी। इस प्रकार

const int width = 800; 
const int height = 800; 
unsigned int* z_buffer = new unsigned int[width*height]; 

तब पिक्सल का उपयोग:

unsigned int z = z_buffer[y*width+x]; 
2

मैं 800 * 800 का एक भी आयाम सरणी बना सकता है। यह 800 अलग वैक्टर आवंटित करने के बजाय, इस तरह के एक आवंटन का उपयोग करने के लिए शायद अधिक कुशल है।

int *ary=new int[800*800]; 

फिर, शायद उस वर्ग में जो 2 डी सरणी की तरह कार्य करता है उसे समाहित करता है।

class _2DArray 
{ 
    public: 
    int *operator[](const size_t &idx) 
    { 
    return &ary[idx*800]; 
    } 
    const int *operator[](const size_t &idx) const 
    { 
    return &ary[idx*800]; 
    } 
}; 

अमूर्त यहाँ दिखाया गया है छेद, उदा का एक बहुत, यदि आप एक "पंक्ति" के अंत अतीत बाहर का उपयोग क्या होता है? "प्रभावशाली सी ++" पुस्तक में सी ++ में अच्छे बहु आयामी सरणी लिखने की एक अच्छी अच्छी चर्चा है।

+0

ऑपरेटर [] एक इंट पॉइंटर लौट रहा है? हो सकता है कि आप यही चाहते थे: int * ऑपरेटर [] (size_t y) {वापसी और आरी [वाई * चौड़ाई]; } तो फिर तुम लिख सकते हैं: my2DArray [y] [x] हालांकि बेहतर अभ्यास ऑपरेटर() का उपयोग करने होगा: पूर्णांक और ऑपरेटर [] (int x, int y) {वापसी ary [y * चौड़ाई + x]; } – Niall

+0

अच्छा बिंदु, हालांकि मैं वास्तव में सहमत नहीं हूं कि फ़ंक्शन कॉल ऑपरेटर को अधिभारित करना बेहतर अभ्यास है। यदि आप एक सरणी लपेट रहे हैं, तो आपको इसे एक सरणी की तरह दिखाना चाहिए। –

+0

सी ++ एफएक्यू में ऑपरेटर() का उपयोग करने के लिए कुछ औचित्य हैं। http://www.parashift.com/c++-faq-lite/operator-overloading.html#faq-13.11 – Niall

1

वहाँ ऐसा करने का रास्ता की तरह सी है:

const int xwidth = 800; 
const int ywidth = 800; 
int* array = (int*) new int[xwidth * ywidth]; 
// Check array is not NULL here and handle the allocation error if it is 
// Then do stuff with the array, such as zero initialize it 
for(int x = 0; x < xwidth; ++x) 
{ 
    for(int y = 0; y < ywidth; ++y) 
    { 
     array[y * xwidth + x] = 0; 
    } 
} 
// Just use array[y * xwidth + x] when you want to access your class. 

// When you're done with it, free the memory you allocated with 
delete[] array; 

आप एक आसान बात के साथ एक वर्ग के अंदर y * xwidth + x संपुटित सकता है और विधि सेट ([] ऑपरेटर ओवरलोडिंग के साथ संभवतः यदि आप और अधिक उन्नत में हो रही शुरू करना चाहते हैं सी ++)। मैं धीरे-धीरे यह प्राप्त करने की अनुशंसा करता हूं कि यदि आप अभी सी ++ से शुरू कर रहे हैं और एन-आयाम सरणी के लिए पुनः उपयोग करने योग्य पूरी तरह से क्लास टेम्पलेट्स बनाना शुरू नहीं करते हैं जो आपको शुरू होने पर ही भ्रमित कर देगा।

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

मुझे पता चला कि सी ++ लाइट अकसर किये गए सवाल इस तरह की जानकारी के लिए बहुत अच्छे थे।विशेष रूप से अपने प्रश्न का जवाब है:

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.16

+0

यदि आपने नई सरणी को "800 इंच की सरणी के सूचक" (int [800] * buff; // ??) में डाला है तो आप [x * w + y] kludge – BCS

+0

@BCS से बच सकते हैं: यह काम नहीं करता है , 800 पॉइंटर्स की कोई सरणी नहीं है। – Niall

+0

इस कोड में एक बग है, यह विफल हो जाएगा जब चौड़ाई और ऊंचाई बराबर नहीं है। सरणी [x * xwidth + y] सरणी होना चाहिए [y * xwidth + x]। – Niall

-1

ठीक है, क्या नियाल रयान शुरू कर दिया पर निर्माण, अगर प्रदर्शन एक मुद्दा है, तो आप इस एक कदम और आगे गणित के अनुकूलन और एक वर्ग में इस encapsulating द्वारा ले जा सकते हैं।

तो हम कुछ गणित के साथ शुरू करेंगे। याद रखें कि 800 के रूप में 2 की शक्तियों में लिखा जा सकता है:

:

int index = y << 9 + y << 8 + y << 5 + x; 

तो अगर हम एक अच्छा वर्ग पर हम पाते हैं में सब कुछ संपुटित:

800 = 512 + 256 + 32 = 2^5 + 2^8 + 2^9 

के रूप में तो हम अपने को संबोधित समारोह लिख सकते हैं

class ZBuffer 
{ 
public: 
    const int width = 800; 
    const int height = 800; 

    ZBuffer() 
    { 
     for(unsigned int i = 0, *pBuff = zbuff; i < width * height; i++, pBuff++) 
      *pBuff = 0; 
    } 

    inline unsigned int getZAt(unsigned int x, unsigned int y) 
    { 
     return *(zbuff + y << 9 + y << 8 + y << 5 + x); 
    } 

    inline unsigned int setZAt(unsigned int x, unsigned int y, unsigned int z) 
    { 
     *(zbuff + y << 9 + y << 8 + y << 5 + x) = z; 
    } 
private: 
    unsigned int zbuff[width * height]; 
}; 
+0

क्या आप "समयपूर्व अनुकूलन" कह सकते हैं? कंपाइलर तक उन तरह की फैंसी चाल क्यों न छोड़ें? –

+0

क्या 2 अतिरिक्त जोड़ और 3 बदलाव वास्तव में गुणा करने की तुलना में तेज़ हैं? क्या कंपाइलर नोटिस को एक कॉन्स द्वारा गुणा नहीं करेगा और यह वास्तव में तेज होगा अगर यह वास्तव में तेज था? साथ ही, यदि आप कॉन्स बदलते हैं, तो आपको शिफ्ट/एड कोड को फिर से लिखना याद रखना होगा। – rjmunro

+0

मुझे लगता है कि एक बफर पंक्ति पॉइंटर्स लुकअप टेबल बनाना वैसे भी तेज होगा। आप इसे बफर आकार के आधार पर कक्षा कन्स्ट्रक्टर में बना सकते हैं। – macbirdie

10

प्रत्येक पोस्ट अब तक प्रोग्रामर के लिए स्मृति प्रबंधन छोड़ देता है। यह से बचा जा सकता है और बचा जाना चाहिए। ReaperUnreal जो मैं करता हूं उसके करीब है, सिवाय इसके कि मैं एक सरणी के बजाय वेक्टर का उपयोग करता हूं और आयाम टेम्पलेट पैरामीटर भी बनाता हूं और एक्सेस फ़ंक्शंस को बदलता हूं - और ओह सिर्फ आईएनएनएसएचओ चीजों को थोड़ा सा साफ़ करें:

template <class T, size_t W, size_t H> 
class Array2D 
{ 
public: 
    const int width = W; 
    const int height = H; 
    typedef typename T type; 

    Array2D() 
     : buffer(width*height) 
    { 
    } 

    inline type& at(unsigned int x, unsigned int y) 
    { 
     return buffer[y*width + x]; 
    } 

    inline const type& at(unsigned int x, unsigned int y) const 
    { 
     return buffer[y*width + x]; 
    } 

private: 
    std::vector<T> buffer; 
}; 

अब आप ठीक ढेर पर इस 2-डी सरणी आवंटित कर सकते हैं:

void foo() 
{ 
    Array2D<int, 800, 800> zbuffer; 

    // Do something with zbuffer... 
} 

मुझे आशा है कि इस मदद करता है!

संपादित करें: Array2D::buffer से हटाए गए सरणी विनिर्देश। एंड्रियास को पकड़ने के लिए धन्यवाद!

+0

मैं कुछ वातावरण के लिए स्मृति प्रबंधन _ के बारे में पूरी तरह से असहमत हूं। आपके आवंटन पैटर्न के आधार पर, डिफ़ॉल्ट आवंटक स्मृति को बहुत खराब कर सकता है, जिससे प्रदर्शन करने के लिए कठिन प्रदर्शन होता है। मैं एक सरणी को कक्षा में बदलने की आवश्यकता को भी समझ नहीं पा रहा हूं। Obfuscation IMHO। – Mark

+0

कुछ वातावरण के लिए ... ठीक है। लेकिन एक कस्टम आवंटक का उपयोग करने के लिए Array2D :: बफर को बदलने के लिए पर्याप्त आसान है - ठीक है, अगर आपको पता है कि डिफ़ॉल्ट आवंटक भयानक है और बेहतर हैं, तो आपको यह भी पता चलेगा कि कस्टम आवंटन कैसे जोड़ना है। – Kevin

+0

जहां तक ​​एक वर्ग का उपयोग किया जाता है ... अच्छी तरह से, स्टैक का उपयोग करने से बचने के लिए और मैन्युअल मेमोरी प्रबंधन का उपयोग करने से बचने के लिए, आपको कक्षा का उपयोग करना होगा। यही सब है इसके लिए। यदि आप मैन्युअल मेमोरी प्रबंधन के लिए खुले हैं, तो 2-डी सरणी आवंटित करने के लिए 'नया' का उपयोग करें। – Kevin

1

एक बात आप कर सकते हैं कुलपति के साथ ध्वज यह है करने के लिए ढेर आकार बदल (यदि आप वास्तव में स्टैक पर सरणी चाहते हैं) है [/ एफ] (http://msdn.microsoft.com/en-us/library/tdkhxaks(VS.80).aspx)

लेकिन समाधान आप शायद चाहते हैं ढेर में के बजाय ढेर पर स्मृति डाल करने के लिए, कि आप vectors के vector का उपयोग करना चाहिए के लिए प्रत्येक तत्व 800 int रों की एक vector है और आप बचाता है।

निम्न पंक्ति एक 800 की vector तत्वों वाणी, मैन्युअल रूप से मेमोरी को प्रबंधित करने से।

std::vector<std::vector<int> > arr(800, std::vector<int>(800)); 

दो समापन कोण ब्रैकेट्स (> >) के बीच की जगह पर ध्यान दें, ताकि इसे शिफ्ट दाएं ऑपरेटर से अलग कर दिया जा सके (जिसे C++0x में अब आवश्यकता नहीं होगी)।

4

केविन के उदाहरण अच्छा है, तथापि:

std::vector<T> buffer[width * height]; 

std::vector<T> buffer; 

होना चाहिए यह आप निश्चित रूप से पर के बजाय ऑपरेटर-भार के जोड़ सकता है() थोड़ा विस्तार - कार्य:

const T &operator()(int x, int y) const 
{ 
    return buffer[y * width + x]; 
} 

और

T &operator()(int x, int y) 
{ 
    return buffer[y * width + x]; 
} 

उदाहरण:

int main() 
{ 
    Array2D<int, 800, 800> a; 
    a(10, 10) = 50; 
    std::cout << "A(10, 10)=" << a(10, 10) << std::endl; 
    return 0; 
} 
1

या आप की तरह कुछ की कोशिश कर सकते: यह भी करने के लिए

boost::shared_array<int> zbuffer(new int[width*height]); 

तुम अब भी सक्षम होना चाहिए:

++zbuffer[0]; 

नहीं स्मृति के प्रबंधन के बारे में अधिक चिंताएं, देखभाल करने के लिए कोई कस्टम वर्ग नहीं है, और चारों ओर फेंकना आसान है।

1

यदि आप केवल एक उदाहरण की आवश्यकता है, तो आप स्थिर स्टोरेज (फ़ाइल के दायरे में, या static क्वालीफायर को फ़ंक्शन स्कोप में जोड़ें) पर सरणी आवंटित कर सकते हैं।

int array[800][800]; 

void fn() 
{ 
    static int array[800][800]; 
} 

इस तरह यह ढेर पर नहीं जाएगा, और आपको गतिशील स्मृति से निपटने की आवश्यकता नहीं है।