2008-09-15 3 views
7

में पूर्णांक के तीन आयामी सरणी, मैं पॉइंटर अंकगणितीय/गतिशील स्मृति आवंटन का उपयोग करके, या वैकल्पिक रूप से STL तकनीकों का उपयोग करते हुए, सी ++ में पूर्णांक के तीन आयामी सरणी को कार्यान्वित करने के सुरक्षित तरीकों को जानना चाहता हूं।सी ++

अनिवार्य रूप से मैं अपने पूर्णांक सरणी आयाम की तरह लग रहे हैं:

[ x ][ y ][ z ] 

x और y रेंज 20-6000 z में जाना जाता है में हैं और बराबर होती है 4.

+0

पुरानी हड्डियों को खोदना, मुझे पता है ... लेकिन लोग क्यों सी ++ का उपयोग करते हैं जब बहु-आयामी सरणी जैसी "बुनियादी" चीजें अन्य उच्च स्तरीय भाषाओं के साथ आती हैं? क्या ऐसा कुछ है जिसके लिए आपको सी ++ का उपयोग करना है ताकि आपके हाथ आपकी पीठ के पीछे बंधे हों? प्रदर्शन? – MikeMurko

+0

हाय माइकमुरको, इस समय हाथ से बंधे कारण सबसे सही होंगे। मैं जावा या सी # में ऐसा कुछ करने में भी उतना ही खुश हूं। – AndyUK

उत्तर

11

बूस्ट पर एक नज़र डालें multi-dimensional array पुस्तकालय।

#include "boost/multi_array.hpp" 

int main() { 
    // Create a 3D array that is 20 x 30 x 4 
    int x = 20; 
    int y = 30; 
    int z = 4; 

    typedef boost::multi_array<int, 3> array_type; 
    typedef array_type::index index; 
    array_type my_array(boost::extents[x][y][z]); 

    // Assign values to the elements 
    int values = 0; 
    for (index i = 0; i != x; ++i) { 
    for (index j = 0; j != y; ++j) { 
     for (index k = 0; k != z; ++k) { 
     my_array[i][j][k] = values++; 
     } 
    } 
    } 
} 
5

वर्ग कोष्ठक में से प्रत्येक जोड़ी एक dereferencing आपरेशन (जब एक सूचक के लिए आवेदन किया है): यहाँ एक उदाहरण (बूस्ट प्रलेखन से रूपांतरित) है। उदाहरण के लिए, कोड की लाइनों के निम्नलिखित जोड़े बराबर हैं:

x = myArray[4]; 
x = *(myArray+4); 

 

x = myArray[2][7]; 
x = *((*(myArray+2))+7); 

आपके द्वारा सुझाए गए सिंटैक्स का उपयोग करने के लिए आप बस अपसंदर्भन कर रहे हैं मूल्य पहले भिन्नता से लौट आए।

int*** myArray = (some allocation method, keep reading); 
// 
// All in one line: 
int value = myArray[x][y][z]; 
// 
// Separated to multiple steps: 
int** deref1 = myArray[x]; 
int* deref2 = deref1[y]; 
int value = deref2[z]; 

इस सरणी आवंटन के बारे में जाने के लिए, आप बस पहचान करने के लिए कि आप वास्तव में पूर्णांकों का एक तीन आयामी सरणी की जरूरत नहीं है की जरूरत है। आपके पास पूर्णांक के सरणी के सरणी हैं।

// Start by allocating an array for array of arrays 
int*** myArray = new int**[X_MAXIMUM]; 

// Allocate an array for each element of the first array 
for(int x = 0; x < X_MAXIMUM; ++x) 
{ 
    myArray[x] = new int*[Y_MAXIMUM]; 

    // Allocate an array of integers for each element of this array 
    for(int y = 0; y < Y_MAXIMUM; ++y) 
    { 
     myArray[x][y] = new int[Z_MAXIMUM]; 

     // Specify an initial value (if desired) 
     for(int z = 0; z < Z_MAXIMUM; ++z) 
     { 
      myArray[x][y][z] = -1; 
     } 
    } 
} 

इस सरणी Deallocating आवंटित करने के लिए इसी तरह की प्रक्रिया इस प्रकार है:

for(int x = 0; x < X_MAXIMUM; ++x) 
{ 
    for(int y = 0; y < Y_MAXIMUM; ++y) 
    { 
     delete[] myArray[x][y]; 
    } 

    delete[] myArray[x]; 
} 

delete[] myArray; 
+0

हाँ, आप यह कर सकते हैं, लेकिन आप इसे एक मेमोरी खंड और केवल एक आवंटन में भी कर सकते हैं। यह आमतौर पर एक मेमोरी खंड का उपयोग करने के लिए बहुत तेज होता है कि कई छोटे, स्मृति संकेतों की कीमत होती है। जब आप पूर्ण आंतरिक सरणी का उपयोग नहीं करते हैं और आप उन्हें आवंटित करने से पूरी तरह से बच सकते हैं तो आप जिस विधि को दिखाते हैं वह अधिकतर स्पैर मैट्रिक्स के लिए उपयोगी होता है। – kriss

+0

@kriss मैं पूरी तरह से सहमत हूं, और मैं व्यक्तिगत रूप से एकल सरणी का उपयोग करता हूं (बड़ी सामग्री के लिए पृष्ठ द्वारा आवंटित)। जिस कारण से मैंने इसे यहां स्थापित किया है, सिंटैक्स अनुरोध के लिए है (जैसा कि मैंने देखा है)। पहुंच के लिए प्रदर्शन अंतर मामूली है, हालांकि एक बड़ी पर्याप्त सरणी और ज्ञात कैश व्यवहार के लिए मैं निश्चित रूप से अपर्याप्त उपयोग के मामलों (दोनों के लिए) बना सकता हूं, और प्रारंभिकरण के लिए प्रदर्शन अप्रासंगिक है (यदि यह महत्वपूर्ण है, तो आप इसे गलत में प्रारंभ कर रहे हैं जगह - इसे पहले करो)। – Zooba

+0

आप इसे आसानी से एक मेमोरी खंड में पोस्टर के लिए पूछे गए वाक्यविन्यास के साथ भी कर सकते हैं। मैंने एक उत्तर दिखाया (एक तरफ, बहुत सारे हैं) सी ++ का उपयोग करके बस इतना करना है, और परिवर्तनीय लंबाई सरणी अब सी 99 की एक विशेषता है (बहुत बुरा यह सी ++ के लिए अपना रास्ता नहीं मिला)।वैसे भी मैं आपका जवाब +1 कर दूंगा क्योंकि यह ठीक काम करता है। – kriss

1

यह ध्यान देने योग्य है कि, सभी इरादों और मकसदों, आप केवल एक 2D सरणी के साथ काम कर रहे हैं, क्योंकि तिहाई (के लिए और कम से कम महत्वपूर्ण) आयाम ज्ञात है।

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

हालांकि, अगर आप पहले से अपने डेटासेट के बारे में कुछ जानते हैं, जैसे कि कुल मिलाकर कितने आइटम संग्रहीत किए जाएंगे, या यदि सरणी को कम से कम आबादी दी जाती है, तो आप किसी प्रकार के हैश/बाल्टी फ़ंक्शन का उपयोग करना बेहतर हो सकते हैं , और XYZ इंडेक्स को अपनी कुंजी के रूप में उपयोग करें। इस मामले में, प्रति आयाम 8192 (13 बिट्स) प्रविष्टियों को मानते हुए, आप 40-बिट (5-बाइट) कुंजी के साथ प्राप्त कर सकते हैं। या, मान लीजिए कि हमेशा 4 एक्स जेड प्रविष्टियां होती हैं, आप बस 26-बिट XY कुंजी का उपयोग करेंगे। यह गति, स्मृति उपयोग, और गतिशील आवंटन के बीच अधिक कुशल व्यापार-बंदों में से एक है।

2
वैक्टर के साथ

:

std::vector< std::vector< std::vector<int> > > array3d; 

प्रत्येक तत्व array3d [x] [y] सुलभ बुद्धि [z] अगर तत्व पहले से ही जोड़ दिया गया है। (उदाहरण के लिए push_back के माध्यम से)

1

नए/हटाए जाने पर आपकी याददाश्त को प्रबंधित करने के लिए एसटीएल का उपयोग करने के कई फायदे हैं। आपके डेटा का प्रतिनिधित्व करने का विकल्प इस बात पर निर्भर करता है कि आप इसका उपयोग कैसे करें।एक सुझाव एक वर्ग होगा जो कार्यान्वयन के निर्णय को छुपाता है और एक आयामी एसटीएल वेक्टर को तीन आयामी प्राप्त/सेट विधियों प्रदान करता है।

यदि आपको सच में विश्वास है कि आपको कस्टम 3 डी वेक्टर प्रकार बनाने की आवश्यकता है, तो पहले बूस्ट की जांच करें।

// a class that does something in 3 dimensions 

class MySimpleClass 
{ 
public: 

    MySimpleClass(const size_t inWidth, const size_t inHeight, const size_t inDepth) : 
    mWidth(inWidth), mHeight(inHeight), mDepth(inDepth) 
    { 
     mArray.resize(mWidth * mHeight * mDepth); 
    } 


    // inline for speed 
    int Get(const size_t inX, const size_t inY, const size_t inZ) { 
    return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX]; 
    } 

    void Set(const size_t inX, const size_t inY, const size_t inZ, const int inVal) { 
    return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX]; 
    } 

    // doing something uniform with the data is easier if it's not a vector of vectors 
    void DoSomething() 
    { 
    std::transform(mArray.begin(), mArray.end(), mArray.begin(), MyUnaryFunc); 
    } 

private: 

    // dimensions of data 
    size_t mWidth; 
    size_t mHeight; 
    size_t mDepth; 

    // data buffer 
    std::vector<int> mArray; 
}; 
-2

पीटर के सुझाव निश्चित रूप से अच्छा है, लेकिन एक बात आप को ध्यान में रखना है बड़ा सरणियों निर्माण के मामले में यह काफी धीमी गति से हो सकता है। हर बार वेक्टर क्षमता में परिवर्तन होता है, सभी डेटा की प्रतिलिपि बनाई जानी चाहिए (वेक्टरों के 'एन' वैक्टर)।

+0

हालांकि आप हाथ से पहले आसानी से आवंटित कर सकते हैं – Raindog

5

नीचे प्रत्येक सरणी के लिए स्मृति के एक खंड में सी या सी ++ का उपयोग करके 3 डी सरणी बनाने का एक सीधा तरीका है। बूस्ट (यहां तक ​​कि अगर यह अच्छा है) का उपयोग करने की आवश्यकता नहीं है, या एकाधिक संकेतों के साथ लाइनों के बीच आवंटन को विभाजित करने की आवश्यकता नहीं है (यह काफी खराब है क्योंकि यह डेटा तक पहुंचने पर बड़ी प्रदर्शन जुर्माना देता है और यह स्मृति स्मृति)।

समझने की एकमात्र चीज यह है कि बहुआयामी सरणी जैसी कोई चीज नहीं है, केवल सरणी के सरणी (सरणी के)। सबसे आंतरिक सूचकांक स्मृति में सबसे दूर है।

#include <stdio.h> 
#include <stdlib.h> 

int main(){ 

    { 
     // C Style Static 3D Arrays 
     int a[10][20][30]; 
     a[9][19][29] = 10; 
     printf("a[9][19][29]=%d\n", a[9][19][29]); 
    } 

    { 
     // C Style dynamic 3D Arrays 
     int (*a)[20][30]; 
     a = (int (*)[20][30])malloc(10*20*30*sizeof(int)); 
     a[9][19][29] = 10; 
     printf("a[9][19][29]=%d\n", a[9][19][29]); 
     free(a); 
    } 

    { 
     // C++ Style dynamic 3D Arrays 
     int (*a)[20][30]; 
     a = new int[10][20][30]; 
     a[9][19][29] = 10; 
     printf("a[9][19][29]=%d\n", a[9][19][29]); 
     delete [] a; 
    } 

} 

अपने वास्तविक समस्या के लिए, के रूप में वहाँ संभवतः दो अज्ञात आयाम है, वहाँ मेरे प्रस्ताव के साथ एक समस्या पर यह केवल एक अज्ञात आयाम अनुमति देते हैं। इसे प्रबंधित करने के कई तरीके हैं।

अच्छी खबर यह है कि चर का उपयोग अब सी के साथ काम करता है, इसे परिवर्तनीय लंबाई सरणी कहा जाता है। विवरण के लिए आप here देखें।

int x = 100; 
    int y = 200; 
    int z = 30; 

    { 
     // C Style Static 3D Arrays 
     int a[x][y][z]; 
     a[99][199][29] = 10; 
     printf("a[99][199][29]=%d\n", a[99][199][29]); 
    } 

    { 
     // C Style dynamic 3D Arrays 
     int (*a)[y][z]; 
     a = (int (*)[y][z])malloc(x*y*z*sizeof(int)); 
     a[99][199][29] = 10; 
     printf("a[99][199][29]=%d\n", a[99][199][29]); 
     free(a); 
    } 

तो सी सबसे आसान तरीका का उपयोग कर ++ ऑपरेटर सारणी वाक्य-विन्यास के साथ रहना अधिक भार का उपयोग करने के शायद है:

{ 
     class ThreeDArray { 
      class InnerTwoDArray { 
       int * data; 
       size_t y; 
       size_t z; 
       public: 
       InnerTwoDArray(int * data, size_t y, size_t z) 
        : data(data), y(y), z(z) {} 

       public: 
       int * operator [](size_t y){ return data + y*z; } 
      }; 

      int * data; 
      size_t x; 
      size_t y; 
      size_t z; 
      public: 
      ThreeDArray(size_t x, size_t y, size_t z) : x(x), y(y), z(z) { 
       data = (int*)malloc(x*y*z*sizeof data); 
      } 

      ~ThreeDArray(){ free(data); } 

      InnerTwoDArray operator [](size_t x){ 
       return InnerTwoDArray(data + x*y*z, y, z); 
      } 
     }; 

     ThreeDArray a(x, y, z); 
     a[99][199][29] = 10; 
     printf("a[99][199][29]=%d\n", a[99][199][29]); 
    } 

ऊपर कोड InnerTwoDArray तक पहुँचने के लिए कुछ अविवेक लागत है (लेकिन एक अच्छा संकलक शायद अनुकूलन कर सकते हैं यह दूर) लेकिन ढेर पर आवंटित सरणी के लिए केवल एक स्मृति खंड का उपयोग करता है। जो आमतौर पर सबसे कुशल विकल्प है।

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