দ্বি-মাত্রিক অ্যারে বরাদ্দ দেওয়ার অদ্ভুত উপায়?


110

একটি প্রকল্পে, কেউ এই লাইনটি ধাক্কা দিয়েছে:

double (*e)[n+1] = malloc((n+1) * sizeof(*e));

যা অনুমান করা হয় (n + 1) * (n + 1) দ্বি-মাত্রিক অ্যারে তৈরি করে creates

সুস্পষ্টভাবে , আমি বলেছি, কারণ এখনও অবধি, আমি জিজ্ঞাসা করা কেউই আমাকে এটি বলতে পারে না যে এটি কী করে, ঠিক কোথায়, কোথা থেকে এটি উদ্ভূত হয়েছিল বা কেন এটি কাজ করা উচিত (যা অভিযোগ রয়েছে, এটি করে, তবে আমি এখনও এটি কিনছি না)।

সম্ভবত আমি স্পষ্ট কিছু মিস করছি, তবে কেউ যদি আমার কাছে উপরের লাইনে ব্যাখ্যা করতে পারে তবে আমি এটির প্রশংসা করব। কারণ ব্যক্তিগতভাবে, আমরা যদি আমরা আসলে বুঝতে পারি এমন কিছু ব্যবহার করি তবে আমি আরও ভাল বোধ করতাম।


15
এমনকি যদি এটি কাজ করে তবে পুশারটিকে ফায়ার করুন।
মার্টিন জেমস

22
@ মার্টিনজেমস কেন? অন্যথায় আপনি কীভাবে সংলগ্ন মেমরিতে 2D অ্যারে বরাদ্দ করবেন? একটি ম্যাংলেড 1 ডি অ্যারে? এটি 1990 এর প্রোগ্রামিং, এখন আমাদের ভিএলএ রয়েছে।
লন্ডিন

43
রেকর্ডের জন্য, যে হয় এক এবং পরিবর্তনশীল প্রকৃত 2D অ্যারে বণ্টন একমাত্র উপায়।
কোয়ান্টিন

15
@ কিন্নুগ নং, এটি পয়েন্টারগুলির একটি অ্যারে ঘোষণা করবে, 2D অ্যারে নয়। যদি আপনি 2 ডি অ্যারে চান তবে কোনও কারণ নেই যে আপনি কখনই পয়েন্টারের একটি অ্যারে বরাদ্দ করতে চান। এটি হ্যাপ বিভাজন এবং দুর্বল ক্যাশে মেমরির ব্যবহারের কারণে ধীর এবং এটি অনিরাপদ কারণ আপনি এটিকে অ্যারে হিসাবে ব্যবহার করতে পারবেন না (মেমকি ইত্যাদি) and এছাড়াও কোডটি ফুলে যায়। আপনার একাধিক ফ্রি () কল লাগবে এবং মেমরি ফাঁস হওয়া সহজ হবে। এ জাতীয় কোডটি যেমন ব্যাপকভাবে ছড়িয়ে পড়েছে, এটি সাধারণভাবে খারাপ।
লন্ডিন

15
এই সমস্যাটি ব্যাখ্যা / উত্তরটি আরও পরিষ্কার করা যেত যদি নমুনাটি একই মূল্যবান মাত্রা ব্যবহার না করে n+1তবে পরিবর্তেdouble (*e)[rows] = malloc(columns * sizeof *e);
chux - পুনরায় ইনস্টল করুন মনিকা

উত্তর:


87

পরিবর্তনশীল eহ'ল n + 1ধরণের উপাদানগুলির একটি অ্যারের নির্দেশক double

ডিरेফারেন্স অপারেটরটি ব্যবহার করে eআপনাকে বেস-প্রকার দেয় eযা " n + 1টাইপের উপাদানগুলির অ্যারে double"।

mallocকল কেবল বেস-টাইপ লাগে e(উপরে ব্যাখ্যা) এবং এটি দ্বারা তার আকার বৃদ্ধি পায় পায়, n + 1এবং যে আকার ক্ষণস্থায়ী mallocফাংশন। মূলত n + 1এর n + 1উপাদানগুলির অ্যারের অ্যারে বরাদ্দ করা double


3
@ মার্টিনজেমস sizeof(*e)এর সমতুল্য sizeof(double [n + 1])। এটি দিয়ে গুণ করুন n + 1এবং আপনি যথেষ্ট পান।
কিছু প্রোগ্রামার ডিউড

24
@ মার্টিনজেমস: এতে দোষ কী? এটি যে চক্ষু-ছত্রাক নয়, এটি গ্যারান্টি দেয় যে বরাদ্দকৃত সারিগুলি সামঞ্জস্যপূর্ণ এবং আপনি এটি অন্য 2 ডি অ্যারের মতো সূচক করতে পারেন। আমি এই আইডিয়মটি আমার নিজের কোডটিতে প্রচুর ব্যবহার করি।
জন বোদে

3
এটি সুস্পষ্ট বলে মনে হতে পারে তবে এটি কেবল বর্গাকার অ্যারেগুলির জন্য কাজ করে (একই মাত্রা)।
জেনস 17

18
@ জেনস: কেবলমাত্র এই অর্থে যে আপনি যদি n+1উভয় মাত্রা রাখেন তবে ফলাফলটি বর্গাকার হবে। যদি আপনি এটি করেন double (*e)[cols] = malloc(rows * sizeof(*e));তবে ফলাফলটিতে সুনির্দিষ্টভাবে উল্লেখ করা সারি এবং কলামের যে কোনও সংখ্যা থাকবে।
ব্যবহারকারী 2357112

9
@ user2357112 এখন যে আমি বরং বরং দেখতে চাই। এমনকি এর অর্থ যদি আপনাকে যুক্ত করতে হয় int rows = n+1এবং int cols = n+1। Godশ্বর আমাদের চালাক কোড থেকে বাঁচান।
candied_orange

56

আপনার গতিশীলভাবে 2D অ্যারে বরাদ্দ করা উচিত এটি সাধারণ উপায়।

  • eধরণের অ্যারেতে একটি অ্যারে পয়েন্টার double [n+1]
  • sizeof(*e)সুতরাং নির্দেশিত-প্রকারের ধরণটি দেয় যা একটি double [n+1]অ্যারের আকার ।
  • আপনি এই n+1জাতীয় অ্যারেগুলির জন্য ঘর বরাদ্দ করেন ।
  • আপনি অ্যারের পয়েন্টারটিকে eএই অ্যারেতে প্রথম অ্যারেতে বিন্দুতে সেট করেছেন ।
  • এই আপনি ব্যবহার করার অনুমতি দেয় eযেমন e[i][j]2D অ্যারের মধ্যে এক্সেস পৃথক আইটেম।

ব্যক্তিগতভাবে আমি মনে করি এই স্টাইলটি পড়া খুব সহজ:

double (*e)[n+1] = malloc( sizeof(double[n+1][n+1]) );

12
আপনার প্রস্তাবিত শৈলীর সাথে আমি দ্বিমত পোষণ না করে উত্তম উত্তর, স্টাইলটিকে প্রাধান্য দিন ptr = malloc(sizeof *ptr * count)
chux

ভাল উত্তর, এবং আমি আপনার পছন্দসই শৈলী পছন্দ। সামান্য উন্নতি হতে পারে আপনাকে এটি এইভাবে করা দরকার কারণ সারিগুলির মধ্যে প্যাডিং থাকতে পারে যা বিবেচনায় নেওয়া উচিত। (কমপক্ষে, আমি মনে করি এ কারণেই আপনার এটি করা দরকার)) (আমি ভুল কিনা তা আমাকে জানান Let)
ডেভিডবাক

2
@ ডেভিডবাক একই জিনিস অ্যারে সিনট্যাক্সটি কেবল স্ব-ডকুমেন্টিং কোড: এটি উত্স কোডের সাথে "2D অ্যারের জন্য ঘর বরাদ্দ করে" বলে।
লন্ডিন

1
@ ডেভিডবাক দ্রষ্টব্য: উপচে পড়লে মন্তব্যটির একটি সামান্য অসুবিধা malloc(row*col*sizeof(double))দেখা দেয় row*col*sizeof()তবে তা sizeof()*row*colহয় না। (যেমন সারি, করল int
হ'ল

7
@ ডেভিডবাক: sizeof *e * (n+1)বজায় রাখা সহজ; আপনি যদি কখনও বেস প্রকার (থেকে পরিবর্তন করার সিদ্ধান্ত নেন doubleকরতে long doubleউদাহরণস্বরূপ,), তারপর আপনি শুধুমাত্র ঘোষণা পরিবর্তন করতে হবে e; আপনার কলের sizeofঅভিব্যক্তিটি সংশোধন করার দরকার নেই malloc(যা সময় বাঁচায় এবং এটিকে এক জায়গায় পরিবর্তন থেকে রক্ষা করে তবে অন্য জায়গায় নয়)। sizeof *eসর্বদা আপনাকে সঠিক আকার দেবে।
জন বোদে

39

এই প্রতিমাটি স্বাভাবিকভাবেই 1 ডি অ্যারে বরাদ্দের বাইরে পড়ে। আসুন কিছু স্বেচ্ছাচারী ধরণের 1D অ্যারে বরাদ্দ দিয়ে শুরু করি T:

T *p = malloc( sizeof *p * N );

সরল, তাই না? অভিব্যক্তি *p টাইপ হয়েছে Tতাই হয়, sizeof *pহিসাবে একই ফলাফল দেয় sizeof (T), তাই আমরা একটি জন্য পর্যাপ্ত স্থান বণ্টন করছেন Nএর -element অ্যারে T। এটি যে কোনও ধরণেরT ক্ষেত্রে সত্য ।

এখন, এর Tমতো একটি অ্যারের সাথে প্রতিস্থাপন করা যাক R [10]। তারপরে আমাদের বরাদ্দ হয়ে যায়

R (*p)[10] = malloc( sizeof *p * N);

এখানকার শব্দার্থবিজ্ঞান 1 ডি বরাদ্দ পদ্ধতির সাথে হুবহু ; সমস্ত পরিবর্তিত হয় এর ধরণ p। পরিবর্তে T *, এটি এখন R (*)[10]। এক্সপ্রেশনতে *pটাইপ রয়েছে Tযা টাইপ R [10], তাই sizeof *pসমান sizeof (T)যা সমান sizeof (R [10])। সুতরাং আমরা একটি উপাদান অ্যারের Nদ্বারা পর্যাপ্ত স্থান বরাদ্দ করছি । 10R

আমরা চাইলে এটিকে আরও এগিয়ে নিতে পারি; ধরুন Rএটি নিজেই একটি অ্যারে টাইপ int [5]। যে জন্য বিকল্প Rএবং আমরা পেতে

int (*p)[10][5] = malloc( sizeof *p * N);

একই চুক্তি - sizeof *pহিসাবে একই sizeof (int [10][5]), এবং আমরা মেমরি বড় যথেষ্ট সংলগ্ন অঞ্চলে বণ্টন একটি রাখা গুটান Nদ্বারা 10দ্বারা 5অ্যারে int

সুতরাং যে বরাদ্দ দিক; এক্সেস পাশ সম্পর্কে কি?

মনে রাখবেন যে []সাবস্ক্রিপ্ট অপারেশন করা হয় সংজ্ঞায়িত পয়েন্টার এরিথমেটিক পরিপ্রেক্ষিতে: a[i]হিসাবে সংজ্ঞায়িত করা হয় *(a + i)1 । সুতরাং, সাবস্ক্রিপ্ট অপারেটর [] সুস্পষ্টভাবে একটি পয়েন্টার dereferences। যদি pপয়েন্টার Tহয় তবে আপনি আনারি *অপারেটরের সাথে সুস্পষ্টভাবে ডিফারেন্সিং করে পয়েন্ট-টু মানটি অ্যাক্সেস করতে পারবেন :

T x = *p;

বা[] সাবস্ক্রিপ্ট অপারেটর ব্যবহার করে :

T x = p[0]; // identical to *p

সুতরাং, যদি pকোনও অ্যারের প্রথম উপাদানটিকে নির্দেশ করে তবে আপনি পয়েন্টারে সাবস্ক্রিপ্ট ব্যবহার করে সেই অ্যারের কোনও উপাদান অ্যাক্সেস করতে পারবেন p:

T arr[N];
T *p = arr; // expression arr "decays" from type T [N] to T *
...
T x = p[i]; // access the i'th element of arr through pointer p

এখন, আবার আমাদের প্রতিস্থাপন অপারেশন করা যাক এবং Tঅ্যারের টাইপ সঙ্গে প্রতিস্থাপন R [10]:

R arr[N][10];
R (*p)[10] = arr; // expression arr "decays" from type R [N][10] to R (*)[10]
...
R x = (*p)[i];

এক সঙ্গে সঙ্গে স্পষ্ট পার্থক্য; pসাবস্ক্রিপ্ট অপারেটর প্রয়োগের আগে আমরা স্পষ্টতই dereferences করছি । আমরা এতে সাবস্ক্রিপ্ট করতে চাই না p, আমরা কী p পয়েন্টগুলিতে সাবস্ক্রিপ্ট করতে চাই (এই ক্ষেত্রে অ্যারে arr[0] )। যেহেতু ইউনারী *সাবস্ক্রিপ্ট কম প্রাধান্য রয়েছে []অপারেটর, আমরা স্পষ্টভাবে গ্রুপে প্রথম বন্ধনী ব্যবহার করতে হবে pসঙ্গে *। তবে উপরে থেকে মনে রাখবেন যে *pএটি একই p[0], তাই আমরা এটির সাথে বিকল্পটি পারি

R x = (p[0])[i];

বা শুধু

R x = p[0][i];

সুতরাং, যদি pএকটি 2 ডি অ্যারের দিকে নির্দেশ করে, আমরা সেইরকম মাধ্যমে অ্যারেতে সূচক করতে পারি p:

R x = p[i][j]; // access the i'th element of arr through pointer p;
               // each arr[i] is a 10-element array of R

এটি উপরের মত একই উপসংহারে নিয়ে যাওয়া এবং এর সাথে প্রতিস্থাপন Rকরা int [5]:

int arr[N][10][5];
int (*p)[10][5]; // expression arr "decays" from type int [N][5][10] to int (*)[10][5]
...
int x = p[i][j][k];

এটি ঠিক একইভাবে কাজ করে যদি pকোনও নিয়মিত অ্যারেতে নির্দেশ করে, বা এটি যদি বরাদ্দকৃত স্মৃতিতে নির্দেশ করে malloc

এই প্রতিবাদটির নিম্নলিখিত সুবিধা রয়েছে:

  1. এটি সহজ - টুকরোচর বরাদ্দ পদ্ধতির বিপরীতে কোডের কেবল একটি লাইন
    T **arr = malloc( sizeof *arr * N );
    if ( arr )
    {
      for ( size_t i = 0; i < N; i++ )
      {
        arr[i] = malloc( sizeof *arr[i] * M );
      }
    }
  2. বরাদ্দ করা অ্যারের সমস্ত সারি হ'ল * সংযুক্ত *, যা উপরের টুকরাফল বরাদ্দ পদ্ধতির ক্ষেত্রে নয়;
  3. একক কল করার সাথে অ্যারেটি হ্রাস করা ঠিক তত সহজ free। আবার, টুকরোয়াল বরাদ্দ পদ্ধতির সাথে সত্য নয়, যেখানে আপনাকে ডিঅলোকেট করার arr[i]আগে আপনাকে প্রতিটি ডিএলোকট করতে হবে arr

কখনও কখনও টুকরোয়াল বরাদ্দকরণ পদ্ধতি পছন্দনামূলক, যেমন আপনার গাদা খারাপভাবে খণ্ডিত হয়ে গেলে এবং আপনি আপনার স্মৃতিটিকে একটি স্বচ্ছ অংশ হিসাবে বরাদ্দ করতে পারবেন না বা আপনি একটি "জেগড" অ্যারে বরাদ্দ করতে চান যেখানে প্রতিটি সারির আলাদা দৈর্ঘ্য থাকতে পারে। তবে সাধারণভাবে, এটি যাওয়ার আরও ভাল উপায়।


1. মনে রাখবেন অ্যারেগুলি পয়েন্টার নয় - পরিবর্তে অ্যারের এক্সপ্রেশনগুলি প্রয়োজনীয় হিসাবে পয়েন্টার এক্সপ্রেশনগুলিতে রূপান্তরিত হয়।


4
+1 আপনি যেভাবে ধারণাটি উপস্থাপন করেছেন তা আমি পছন্দ করি: যে কোনও উপাদানগুলির জন্য সিরিজের কয়েকটি উপাদান বরাদ্দ করা সম্ভব, এমনকি যদি সে উপাদানগুলি নিজেই অ্যারে হয়।
লোগো_ লেখক

1
আপনার ব্যাখ্যাটি সত্যিই ভাল, তবে মনে রাখবেন যে সামঞ্জস্যপূর্ণ মেমরির বরাদ্দ করা কোনও উপকারী নয় যতক্ষণ না আপনার সত্যিকার প্রয়োজন হয়। অবিচ্ছিন্ন স্মৃতিটি আরও ব্যয়বহুল। সাধারণ 2 ডি অ্যারেগুলির জন্য আপনার জন্য মেমরি লেআউটে কোনও পার্থক্য নেই (বরাদ্দ এবং অবনতির জন্য রেখার সংখ্যা বাদে) সুতরাং অ-স্বচ্ছ মেমরি ব্যবহার করতে পছন্দ করুন।
ওলেগ লোকশিন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.