दो-आयामी सरणी के लिए एक पॉइंटर बनाएं


119

मुझे एक स्थिर 2-आयामी सरणी के लिए एक संकेतक की आवश्यकता है। यह कैसे किया जाता है?

static uint8_t l_matrix[10][20];

void test(){
   uint8_t **matrix_ptr = l_matrix; //wrong idea 
}

मुझे सभी प्रकार की त्रुटियां मिलती हैं जैसे:

  • चेतावनी: असंगत पॉइंटर प्रकार से असाइनमेंट
  • अधोलिखित कीमत न ही सारणी है न ही सूचक
  • त्रुटि: लचीली सरणी सदस्य का अमान्य उपयोग

पढ़ें stackoverflow.com/questions/423823/... यह आपकी मदद कर सकता है
litb - Johannes Schaub

1
@ जोहान्सचैब-लिट जो अब मौजूद नहीं है। (मैं इसे फिर से कैसे देख सकता हूं ...? मुझे पता है कि कम प्रतिनिधि सदस्य इसे देख सकते हैं, लेकिन मैं भूल गया कि कैसे ...)
मतीन उल्हाक

@ मंटू: यहां इसकी एक प्रति है: gist.github.com/sharth/ede13c0502d5dd8d45bd
बिल लिंच

जवाबों:


139

यहाँ आप सरणी के पहले तत्व के लिए एक सूचक बनाना चाहते हैं

uint8_t (*matrix_ptr)[20] = l_matrix;

टाइपीडिफ के साथ, यह साफ दिखता है

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

तो आप फिर से जीवन का आनंद ले सकते हैं :)

matrix_ptr[0][1] = ...;

C में पॉइंटर / एरे की दुनिया से सावधान रहें , इसके बारे में बहुत भ्रम है।


संपादित करें

यहाँ कुछ अन्य उत्तरों की समीक्षा कर रहे हैं, क्योंकि टिप्पणी क्षेत्र वहाँ करने के लिए बहुत कम हैं। कई विकल्प प्रस्तावित किए गए थे, लेकिन यह नहीं दिखाया गया था कि वे कैसे व्यवहार करते हैं। यहाँ है कि वे कैसे करते हैं

uint8_t (*matrix_ptr)[][20] = l_matrix;

यदि आप त्रुटि को ठीक करते हैं और &निम्न स्निपेट जैसे ऑपरेटर का पता जोड़ते हैं

uint8_t (*matrix_ptr)[][20] = &l_matrix;

तब वह 20 uint8_t प्रकार के तत्वों के अपूर्ण सरणी प्रकार के लिए एक पॉइंटर बनाता है। क्योंकि सूचक सरणी के एक सरणी के लिए है, आपको इसके साथ एक्सेस करना होगा

(*matrix_ptr)[0][1] = ...;

और क्योंकि यह एक अपूर्ण सरणी के लिए एक संकेतक है, आप शॉर्टकट के रूप में नहीं कर सकते

matrix_ptr[0][0][1] = ...;

क्योंकि अनुक्रमणिका को तत्व प्रकार के आकार की आवश्यकता होती है (अनुक्रमणिका सूचक के पूर्णांक को जोड़ने का अर्थ है, इसलिए यह अपूर्ण प्रकारों के साथ काम नहीं करेगा)। ध्यान दें कि यह केवल काम करता है C, क्योंकि T[]और T[N]संगत प्रकार हैं। C ++ में संगत प्रकारों की अवधारणा नहीं है , और इसलिए यह उस कोड को अस्वीकार कर देगा, क्योंकि T[]और T[10]विभिन्न प्रकार हैं।


निम्न विकल्प बिल्कुल काम नहीं करता है, क्योंकि तत्व प्रकार का सरणी, जब आप इसे एक-आयामी सरणी के रूप में देखते हैं uint8_t , लेकिन नहींuint8_t[20]

uint8_t *matrix_ptr = l_matrix; // fail

निम्नलिखित एक अच्छा विकल्प है

uint8_t (*matrix_ptr)[10][20] = &l_matrix;

आप इसके साथ पहुँचें

(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now

इसका यह लाभ है कि यह बाहरी आयाम के आकार को संरक्षित करता है। तो आप इस पर sizeof लगा सकते हैं

sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20

एक अन्य उत्तर है जो इस तथ्य का उपयोग करता है कि किसी सरणी में आइटम को संचित रूप से संग्रहीत किया जाता है

uint8_t *matrix_ptr = l_matrix[0];

अब, यह औपचारिक रूप से आपको केवल दो आयामी सरणी के पहले तत्व के तत्वों तक पहुंचने की अनुमति देता है। वह है, निम्न स्थिति पकड़

matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid

matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior

आप देखेंगे कि यह संभवतः काम करता है 10*20-1, लेकिन यदि आप उर्फ ​​विश्लेषण और अन्य आक्रामक अनुकूलन पर फेंकते हैं, तो कुछ संकलक एक धारणा बना सकते हैं जो उस कोड को तोड़ सकता है। ऐसा कहने के बाद, मैंने कभी भी एक कंपाइलर का सामना नहीं किया है जो उस पर विफल रहता है (लेकिन फिर से, मैंने उस तकनीक का वास्तविक कोड में उपयोग नहीं किया है), और यहां तक ​​कि सी एफएक्यू में वह तकनीक निहित है (इसकी यूबीनेस के बारे में चेतावनी के साथ) ), और यदि आप सरणी प्रकार नहीं बदल सकते हैं, तो यह आपको बचाने के लिए एक अंतिम विकल्प है :)


+1 - इंट (*) पर अच्छी जानकारी [] [20] ब्रेकडाउन - सी ++ में ऐसा नहीं कर सकते
फैसल वली

@litb, मुझे खेद है लेकिन यह गलत है क्योंकि आपका समाधान सरणी के लिए भंडारण का कोई आवंटन प्रदान नहीं करता है।
रॉब वेल्स

2
@ रोब, मैं आपको बहुत नहीं समझता। इन सभी मामलों में स्टोरेज l_matix द्वारा ही प्रदान किया गया है। उनके लिए संकेत जहां-जहां और (स्टैक, स्टैटिक डेटा सेगमेंट, ...) के रूप में घोषित किए जाते हैं, वहां से भंडारण करते हैं।
जोहान्स स्काउब -

बस जिज्ञासु को हमें l_matrix के "और" पते की आवश्यकता क्यों है?
इलेक्ट्रो

1
@ सोहेब - नहीं, जो केवल एक पॉइंटर बनाता है। आपने इसे उलझा दिया होगा uint8_t *d[20], जो uint8_t को 3 पॉइंटर्स की एक सरणी बनाता है, लेकिन यह इस मामले में काम नहीं करेगा।
पालो

27

इसे पूरी तरह से समझने के लिए, आपको निम्नलिखित अवधारणाओं को समझना चाहिए :

ऐरे पॉइंटर्स नहीं हैं!

सबसे पहले (और यह पर्याप्त प्रचार किया गया है), सरणियां संकेत नहीं हैं । इसके बजाय, अधिकांश उपयोगों में, वे अपने पहले तत्व के पते को 'क्षय' करते हैं, जिसे एक पॉइंटर को सौंपा जा सकता है:

int a[] = {1, 2, 3};

int *p = a; // p now points to a[0]

मुझे लगता है कि यह इस तरह से काम करता है ताकि सरणी की सामग्री को उन सभी को कॉपी किए बिना एक्सेस किया जा सके। यह केवल सरणी प्रकारों का एक व्यवहार है और इसका मतलब यह नहीं है कि वे समान हैं।



बहुआयामी सरणियाँ

बहुआयामी सरणियाँ एक तरह से मेमोरी को 'पार्टीशन' करने का एक तरीका है जिसे कंपाइलर / मशीन समझ और संचालित कर सकते हैं।

उदाहरण के लिए, int a[4][3][5]= एक सरणी जिसमें पूर्णांक आकार की स्मृति के 4 * 3 * 5 (60) 'हिस्सा' होते हैं।

उपयोग करने पर लाभ int a[4][3][5]बनाम सादे int b[60]का है कि अब वे 'विभाजित' हो गए हैं (यदि आवश्यक हो तो अपने 'विखंडू के साथ काम करना आसान है), और कार्यक्रम अब बाध्य जाँच कर सकता है।

वास्तव में, int a[4][3][5]संग्रहीत किया जाता है वास्तव में की तरह int b[60]स्मृति में - केवल अंतर यह है कि कार्यक्रम अब इसे प्रबंधित करता है जैसे कि वे कुछ निश्चित आकार (विशेष रूप से, पांच के तीन समूहों के चार समूहों) की अलग-अलग इकाइयाँ हैं।

ध्यान रखें: दोनों int a[4][3][5]और int b[60]स्मृति में ही कर रहे हैं, और फर्क सिर्फ इतना है कि वे किस तरह आवेदन / संकलक द्वारा नियंत्रित कर रहे हैं

{
  {1, 2, 3, 4, 5}
  {6, 7, 8, 9, 10}
  {11, 12, 13, 14, 15}
}
{
  {16, 17, 18, 19, 20}
  {21, 22, 23, 24, 25}
  {26, 27, 28, 29, 30}
}
{
  {31, 32, 33, 34, 35}
  {36, 37, 38, 39, 40}
  {41, 42, 43, 44, 45}
}
{
  {46, 47, 48, 49, 50}
  {51, 52, 53, 54, 55}
  {56, 57, 58, 59, 60}
}

इससे, आप स्पष्ट रूप से देख सकते हैं कि प्रत्येक "विभाजन" केवल एक सरणी है जिसे प्रोग्राम ट्रैक करता है।



वाक्य - विन्यास

अब, सरणियाँ बिंदुओं से वाक्यात्मक रूप से भिन्न होती हैं । विशेष रूप से, इसका मतलब है कि संकलक / मशीन उन्हें अलग तरह से व्यवहार करेगी। यह बिना दिमाग के लग सकता है, लेकिन इस पर एक नज़र डालें:

int a[3][3];

printf("%p %p", a, a[0]);

उपरोक्त उदाहरण दो बार एक ही मेमोरी एड्रेस को प्रिंट करता है, जैसे:

0x7eb5a3b4 0x7eb5a3b4

हालाँकि, केवल एक पॉइंटर को सीधे ही सौंपा जा सकता है :

int *p1 = a[0]; // RIGHT !

int *p2 = a; // WRONG !

एक पॉइंटर को क्यों नहीं a सौंपा जा सकता है लेकिन a[0] हो सकता है?

यह, बस, बहुआयामी सरणियों का एक परिणाम है, और मैं समझाता हूँ क्यों:

' a' के स्तर पर , हम अभी भी देखते हैं कि आगे देखने के लिए हमारे पास एक और 'आयाम' है। के स्तर पर 'a[0]हालांकि, ' , हम पहले से ही शीर्ष आयाम में हैं, इसलिए जहां तक ​​कार्यक्रम का संबंध है, हम सिर्फ एक सामान्य सरणी देख रहे हैं।

आप पूछ रहे होंगे:

अगर इसके लिए एक पॉइंटर बनाने के संबंध में यह सरणी बहुआयामी है तो यह क्यों मायने रखता है?

इस तरह से सोचना सबसे अच्छा है:

एक बहुआयामी सरणी से एक 'क्षय' केवल एक पता नहीं है, बल्कि विभाजन डेटा के साथ एक पता है (AKA) के यह अभी भी समझता है कि इसका अंतर्निहित डेटा अन्य सरणियों से बना है), जिसमें पहले आयाम से परे सरणी द्वारा निर्धारित सीमाएं शामिल हैं।

जब तक हम इसे निर्दिष्ट नहीं करते, यह 'पार्टीशन' तर्क एक पॉइंटर के भीतर मौजूद नहीं हो सकता है:

int a[4][5][95][8];

int (*p)[5][95][8];

p = a; // p = *a[0] // p = a+0

अन्यथा, सरणी के छंटाई गुणों का अर्थ खो जाता है।

चारों ओर कोष्ठक के उपयोग पर भी ध्यान दें *p: int (*p)[5][95][8]- यह निर्दिष्ट करना है कि हम इन सीमाओं के साथ एक संकेतक बना रहे हैं, न कि इन सीमाओं के साथ संकेत का एक सरणी:int *p[5][95][8]



निष्कर्ष

पिछली समीक्षा:

  • यदि वे उपयोग किए गए संदर्भ में कोई अन्य उद्देश्य नहीं रखते हैं, तो पते का क्षय होता है
  • बहुआयामी सरणियाँ केवल सरणियों के सारणी हैं - इसलिए, 'क्षय' पते "मैं उप आयाम" का भार वहन करेगा
  • जब तक आप इसे नहीं देते, तब तक डायमेंशन डेटा एक पॉइंटर में मौजूद नहीं हो सकता

संक्षेप में: बहुआयामी सरणियों के पते का क्षय होता है जो उनकी सामग्री को समझने की क्षमता रखते हैं।


1
उत्तर का पहला भाग महान है, लेकिन दूसरा नहीं है। यह सही नहीं है: int *p1 = &(a[0]); // RIGHT !वास्तव में यह समान हैint *p1 = a;
2501

@ 2501 उस त्रुटि के लिए धन्यवाद, मैंने इसे ठीक कर लिया है। मैं निश्चित रूप से यह नहीं कह सकता कि इस "नियम" को परिभाषित करने वाले उदाहरण ने इसे क्यों परिभाषित किया। चिंताजनक बात यह है कि सिर्फ इसलिए कि दो संस्थाओं को संकेत के रूप में व्याख्या की जा सकती है और समान मूल्य प्राप्त कर सकते हैं, इसका मतलब यह नहीं है कि वे एक ही अर्थ रखते हैं।
सुपर कैट

7

में

int *ptr= l_matrix[0];

आप जैसे पहुँच सकते हैं

*p
*(p+1)
*(p+2)

सभी 2 आयामी सरणियों को भी 1-डी के रूप में संग्रहीत किया जाता है।


5

अच्छा दिन,

घोषणा

static uint8_t l_matrix[10][20];

20 यूनिट 8 स्तंभों की गणना करके प्रत्येक इकाई के साथ 20 यूनिट 8_ स्थानों के 10 पंक्तियों के लिए भंडारण की व्यवस्था की गई है, यानी 200 uint8_t आकार के स्थान।

तो नहीं करता है

uint8_t (*matrix_ptr)[20] = l_matrix;

आपको क्या चाहिए और सरणी की पहली पंक्ति के कॉलम शून्य तत्व को इंगित करें?

संपादित करें: इस बारे में थोड़ा और सोचना, क्या सरणी नाम नहीं है, परिभाषा से, एक सूचक? अर्थात्, एक सरणी का नाम पहले तत्व के स्थान का एक पर्याय है, अर्थात l_matrix [0] [0]?

Edit2: जैसा कि दूसरों ने उल्लेख किया है, टिप्पणी स्थान आगे की चर्चा के लिए बहुत छोटा है। वैसे भी:

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

विचाराधीन सरणी के लिए भंडारण का कोई आवंटन प्रदान नहीं करता है।

जैसा कि ऊपर उल्लेख किया गया है, और मानक द्वारा परिभाषित किया गया है, कथन:

static uint8_t l_matrix[10][20];

ने uint8_t प्रकार के 200 अनुक्रमिक स्थानों को अलग कर दिया है।

प्रपत्र के कथनों का उपयोग करते हुए l_matrix का उल्लेख:

(*l_matrix + (20 * rowno) + colno)

आपको पंक्ति पंक्ति में पाए जाने वाले कोलनोथ तत्व की सामग्री देगा।

सभी सूचक जोड़-तोड़ स्वचालित रूप से इंगित की गई वस्तु के आकार को ध्यान में रखते हैं। - के एंड आर सेक्शन 5.4, पी। १०३

यह भी मामला है अगर कोई पैडिंग या बाइट संरेखण शिफ्टिंग वस्तु के भंडारण में शामिल है। संकलक स्वचालित रूप से इन के लिए समायोजित करेगा। C ANSI मानक की परिभाषा द्वारा।

HTH

चियर्स,


1
uint8_t (* matrix_ptr) [] [20] << पहले कोष्ठक बाहर छोड़ दिया जाना चाहिए, सही है uint8_t (* matrix_ptr) [20]
Aconcagua

5

C99 में (क्लैंग और जीसीसी द्वारा समर्थित) संदर्भ के लिए कार्यों के लिए बहुआयामी सरणियों को पारित करने के लिए एक अस्पष्ट वाक्यविन्यास है:

int l_matrix[10][20];

void test(int matrix_ptr[static 10][20]) {
}

int main(void) {
    test(l_matrix);
}

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

अफसोस की बात है, यह ठीक नहीं है sizeof()और संकलक अभी तक उस जानकारी का उपयोग नहीं करते हैं, इसलिए यह एक जिज्ञासा बनी हुई है।


1
यह उत्तर भ्रामक है: यह तर्क को एक निश्चित आकार सरणी नहीं बनाता है, यह अभी भी एक संकेतक है। static 10कुछ प्रकार की गारंटी है कि कम से कम 10 तत्व मौजूद हैं, जिसका अर्थ है कि आकार निश्चित नहीं है।
13

1
@bluss प्रश्न एक पॉइंटर के बारे में था, इसलिए मैं यह नहीं देखता कि एक पॉइंटर ( संदर्भ द्वारा नोटिंग ) के साथ उत्तर देना कैसे भ्रामक है। सरणी को फ़ंक्शन के परिप्रेक्ष्य से निश्चित आकार दिया जाता है, क्योंकि इन सीमाओं से परे तत्वों तक पहुंच अपरिभाषित है।
कोर्नेल

मुझे नहीं लगता कि 10 से परे पहुंच अपरिभाषित है, मैं कुछ भी संकेत नहीं देख सकता।
दोषी ठहराया

यह उत्तर बताता है कि कीवर्ड के बिना static, सरणी को संदर्भ से पारित नहीं किया जाएगा, जो कि सच नहीं है। Arrays वैसे भी संदर्भ द्वारा पारित कर रहे हैं। मूल प्रश्न एक अलग उपयोग के मामले के बारे में पूछा - एक ही फ़ंक्शन / नाम स्थान के भीतर पूरक सूचक का उपयोग करके 2D सरणी के तत्वों तक पहुंचना।
पालो

4

आप हमेशा सरणी को रैखिक के रूप में घोषित करके और अपने आप से अनुक्रमणिका गणना करने के लिए (पंक्ति, कर्नल) कर कंपाइलर के साथ चक्कर लगाने से बच सकते हैं।

static uint8_t l_matrix[200];

void test(int row, int col, uint8_t val)

{

   uint8_t* matrix_ptr = l_matrix;
   matrix_ptr [col+y*row] = val; // to assign a value

}

यह वह है जो संकलक ने वैसे भी किया होगा।


1
यह वही है जो सी कंपाइलर वैसे भी करता है। सी में वास्तव में "सरणी" की कोई वास्तविक धारणा नहीं है - [] संकेतन सूचक अंकगणित के लिए सिर्फ वाक्य रचना चीनी है
केन कीनन

7
इस उपाय का नुकसान यह है कि इसे करने का सही तरीका कभी नहीं खोजा जा सकता है।
क्रेग मैकक्वीन

2

प्रारंभिक सूचक को इंगित करने वाला मूल सिंटैक्स जो कि बहु-विषयक सरणी को इंगित करता है

type (*pointer)[1st dimension size][2nd dimension size][..] = &array_name

इसे कॉल करने के लिए मूल सिंटैक्स है

(*pointer_name)[1st index][2nd index][...]

यहाँ एक उदाहरण है:

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

int main() {
   // The multidimentional array...
   char balance[5][100] = {
       "Subham",
       "Messi"
   };

   char (*p)[5][100] = &balance; // Pointer initialization...

   printf("%s\n",(*p)[0]); // Calling...
   printf("%s\n",(*p)[1]); // Calling...

  return 0;
}

आउटपुट है:

Subham
Messi

इसने काम कर दिया...


1

आप इसे इस तरह से कर सकते हैं:

uint8_t (*matrix_ptr)[10][20] = &l_matrix;

1
इस कब्जे नहीं है 10 * 20 बाइट्स की RAM? (एक माइक्रोकंट्रोलर पर im)
जिल

यह 4 बाइट पर कब्जा करेगा या आपके बॉक्स में एक पॉइंटर के आकार का जो भी बड़ा होगा। लेकिन याद रखें कि यदि आपके पास यह है, तो आपको मैट्रिक्स_प्ट्र [0] [x] [y] या (* मैट्रिक्स_प्ट्र) [x] [y] के साथ अनुक्रमण करना होगा। पी: यह प्रत्यक्ष और शब्द-दर-शब्द "दो आयामी सरणी के लिए सूचक" की व्याख्या है
litb - Johannes Schaub

धन्यवाद लिट, मैं यह कैसे उपयोग करने के लिए उल्लेख करना भूल गया। मेरे उत्तर को संपादित करने का कोई मतलब नहीं है क्योंकि आपने अपने उत्तर के साथ बहुत अच्छा काम किया है :)
निक डंडौलकिस

तो, क्या यह 10 * 20 बाइट्स की रैम पर कब्जा करता है या नहीं?
डेनियल

@ डायनजेल, चूंकि यह द्वि-आयामी सरणी के लिए एक संकेतक है, इसलिए यह आपके बॉक्स में केवल 4 बाइट्स या किसी भी आकार का पॉइंटर होगा, अर्थात 16-बिट्स, 32-बिट्स, 64-बिट्स, आदि
Nick Dandoulakak

1

आप पहले तत्व के लिए एक संकेतक चाहते हैं, इसलिए;

static uint8_t l_matrix[10][20];

void test(){
   uint8_t *matrix_ptr = l_matrix[0]; //wrong idea 
}

0

यदि आप नकारात्मक अनुक्रमित का उपयोग करना चाहते हैं तो आप एक ऑफसेट भी जोड़ सकते हैं:

uint8_t l_matrix[10][20];
uint8_t (*matrix_ptr)[20] = l_matrix+5;
matrix_ptr[-4][1]=7;

यदि आपका कंपाइलर एक त्रुटि या चेतावनी देता है, जिसका आप उपयोग कर सकते हैं:

uint8_t (*matrix_ptr)[20] = (uint8_t (*)[20]) l_matrix;

हैलो। इस प्रश्न के साथ टैग किया गया है c, इसलिए उत्तर उसी भाषा में होना चाहिए। कृपया टैग देखें।
2501
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.