আমি কীভাবে সি ++ এ অ্যারে ব্যবহার করব?


480

সি ++ সি থেকে উত্তরাধিকার সূত্রে প্রাপ্ত অ্যারে যেখানে তারা কার্যত সর্বত্র ব্যবহৃত হয়। সি ++ এমন বিমূর্ততা সরবরাহ করে যা ব্যবহার করা সহজ এবং ত্রুটি-প্রবণতা কম ( std::vector<T>সি ++ 98 এবং তারপর std::array<T, n>থেকে) সি ++ 11 ), তাই অ্যারে প্রয়োজনীয়তার বেশ প্রায়ই যেমন সি করে তবে উঠা নয়, উত্তরাধিকার যখন আপনি পড়তে কোড সি বা লিখিত একটি লাইব্রেরির সাথে ইন্টারেক্ট করার জন্য, অ্যারে কীভাবে কাজ করে তা আপনার দৃ a়ভাবে উপলব্ধি থাকা উচিত।

এই FAQ পাঁচটি ভাগে বিভক্ত:

  1. প্রকার স্তর এবং অ্যাক্সেসিংয়ের উপাদানগুলিতে অ্যারে
  2. অ্যারে তৈরি এবং সূচনা
  3. অ্যাসাইনমেন্ট এবং প্যারামিটার পাসিং
  4. বহুমাত্রিক অ্যারে এবং পয়েন্টারগুলির অ্যারে
  5. অ্যারে ব্যবহার করার সময় সাধারণ সমস্যাগুলি

যদি আপনি এই FAQ- এ কিছু গুরুত্বপূর্ণ অনুপস্থিত মনে করেন তবে একটি উত্তর লিখুন এবং একটি অতিরিক্ত অংশ হিসাবে এখানে লিঙ্ক করুন।

নিম্নলিখিত পাঠ্যে, "অ্যারে" এর অর্থ "সি অ্যারে", শ্রেণির টেম্পলেট নয় std::array। সি ঘোষক সিনট্যাক্সের প্রাথমিক জ্ঞান ধরে নেওয়া হয়। নোট করুন যে ম্যানুয়াল ব্যবহার newএবং deleteনীচে প্রদর্শিত হিসাবে ব্যতিক্রমগুলির ক্ষেত্রে অত্যন্ত বিপজ্জনক, তবে এটি অন্য FAQ এর বিষয় ।

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


তারা আরও ভাল হবে যদি পয়েন্টাররা সর্বদা তাদের লক্ষ্যের মাঝখানে কোথাও না গিয়ে পরিবর্তের দিকে দিকে ইঙ্গিত করে তবে ...
প্রতিলিপি

আপনার এসটিএল ভেক্টর ব্যবহার করা উচিত কারণ এটি আপনাকে আরও বেশি নমনীয়তা সরবরাহ করে।
মোইজ সাজিদ

2
std::arrayS, std::vectors এবং gsl::spans এর সম্মিলিত প্রাপ্যতার সাথে - আমি কীভাবে সি ++ এ অ্যারে ব্যবহার করতে পারি সে সম্পর্কে একটি FAQ প্রত্যাশা করব "এখনই, আপনি সেগুলি ব্যবহার না করে ন্যায়সঙ্গত, ভাল, বিবেচনা শুরু করতে পারেন ।"
einpoklum

উত্তর:


302

টাইপ স্তরের অ্যারেগুলি

অ্যারের প্রকারটি উপাদান উপাদানটিT[n] কোথায় রয়েছে Tতা হিসাবে চিহ্নিত করা হয় এবং এটি ধনাত্মক আকার , অ্যারের উপাদানগুলির সংখ্যা। অ্যারে টাইপ উপাদান ধরণের এবং আকারের একটি পণ্য ধরণ। যদি এই দুটি উপাদানের মধ্যে একটি বা উভয়ই আলাদা হয় তবে আপনি একটি স্বতন্ত্র প্রকার পাবেন:n

#include <type_traits>

static_assert(!std::is_same<int[8], float[8]>::value, "distinct element type");
static_assert(!std::is_same<int[8],   int[9]>::value, "distinct size");

নোট করুন যে আকারটি প্রকারের একটি অংশ, অর্থাত্, বিভিন্ন আকারের অ্যারে প্রকারগুলি বেমানান প্রকারগুলি যার একে অপরের সাথে একেবারে কিছুই করার নেই। sizeof(T[n])সমতুল্যn * sizeof(T)

অ্যারে-থেকে-পয়েন্টার ক্ষয়

শুধুমাত্র মধ্যে "সংযোগ" T[n]এবং T[m]যে উভয় প্রকারের পরোক্ষভাবে করা যেতে পারে রূপান্তরিত করতে T*, এবং এই রূপান্তর ফলাফলের অ্যারের প্রথম উপাদান একটি পয়েন্টার। এটি হ'ল যে কোনও জায়গায় যে কোনও T*প্রয়োজন হয়, আপনি একটি সরবরাহ করতে পারেন T[n], এবং সংকলক নিঃশব্দে সেই পয়েন্টারটি সরবরাহ করবে:

                  +---+---+---+---+---+---+---+---+
the_actual_array: |   |   |   |   |   |   |   |   |   int[8]
                  +---+---+---+---+---+---+---+---+
                    ^
                    |
                    |
                    |
                    |  pointer_to_the_first_element   int*

এই রূপান্তরটি "অ্যারে-টু-পয়েন্টার ক্ষয়" হিসাবে পরিচিত এবং এটি বিভ্রান্তির একটি প্রধান উত্স। অ্যারেটির আকারটি এই প্রক্রিয়াটিতে হারিয়ে গেছে, যেহেতু এটি আর ( T*) টাইপের অংশ নয় । প্রো: প্রকার স্তরের অ্যারের আকারটি ভুলে যাওয়া কোনও পয়েন্টারকে কোনও আকারের অ্যারের প্রথম উপাদানটির দিকে নির্দেশ করতে পারে । কন: কোন অ্যারের প্রথম (বা অন্য কোনও) উপাদানকে একটি পয়েন্টার দেওয়া হয়েছে, অ্যারেটি কত বড় তা চিহ্নিত করার উপায় নেই বা ঠিক ঠিক যেখানে বিন্দুটি অ্যারের সীমানার সাথে তুলনা করে। পয়েন্টারগুলি অত্যন্ত বোকা

অ্যারেগুলি পয়েন্টার নয়

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

static_assert(!std::is_same<int[8], int*>::value, "an array is not a pointer");

অপারেটর তার সাথে প্রয়োগ করা হয় এমন একটি গুরুত্বপূর্ণ প্রসঙ্গে যার মধ্যে অ্যারের প্রথম উপাদানটির পয়েন্টারে ক্ষয় হয় না& is সেক্ষেত্রে &অপারেটর পুরো অ্যারেতে একটি পয়েন্টার দেয়, তার প্রথম উপাদানটির জন্য কেবল পয়েন্টার নয়। যদিও সেক্ষেত্রে মানগুলি (ঠিকানাগুলি) একই, তবে একটি অ্যারের প্রথম উপাদানটির একটি পয়েন্টার এবং সম্পূর্ণ অ্যারেতে একটি পয়েন্টার সম্পূর্ণ স্বতন্ত্র প্রকার:

static_assert(!std::is_same<int*, int(*)[8]>::value, "distinct element type");

নিম্নলিখিত ASCII শিল্প এই পার্থক্য ব্যাখ্যা করে:

      +-----------------------------------+
      | +---+---+---+---+---+---+---+---+ |
+---> | |   |   |   |   |   |   |   |   | | int[8]
|     | +---+---+---+---+---+---+---+---+ |
|     +---^-------------------------------+
|         |
|         |
|         |
|         |  pointer_to_the_first_element   int*
|
|  pointer_to_the_entire_array              int(*)[8]

প্রথম উপাদানটির পয়েন্টারটি কীভাবে কেবল একটি একক পূর্ণসংখ্যার দিকে নির্দেশ করে (একটি ছোট বাক্স হিসাবে চিত্রিত হয়), যখন পুরো অ্যারেটির পয়েন্টারটি 8 টি পূর্ণসংখ্যার অ্যারে নির্দেশ করে (বৃহত বাক্স হিসাবে চিত্রিত))

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

আপনি যদি সি ডিক্লেটার সিনট্যাক্সের সাথে অপরিচিত হন তবে টাইপের মধ্যে প্রথম বন্ধনী int(*)[8]আবশ্যক:

  • int(*)[8] 8 টি পূর্ণসংখ্যার অ্যারের পয়েন্টার।
  • int*[8]8 টি পয়েন্টারের একটি অ্যারে, টাইপের প্রতিটি উপাদান int*

উপাদান অ্যাক্সেস করা হচ্ছে

অ্যারের পৃথক উপাদানগুলিতে অ্যাক্সেস করতে সি ++ দুটি সিনট্যাকটিক তারতম্য সরবরাহ করে। এগুলির কোনওটিই অপরের চেয়ে উচ্চতর নয় এবং আপনার উভয়ের সাথেই নিজেকে পরিচিত করা উচিত।

পয়েন্টার গাণিতিক

pএকটি অ্যারের প্রথম উপাদানকে একটি পয়েন্টার দেওয়া , অভিব্যক্তি অ্যারের p+iআই-তম উপাদানকে একটি পয়েন্টার দেয়। এরপরে সেই পয়েন্টারটিকে ডিফারেন্স করে কোনও ব্যক্তি পৃথক উপাদানগুলিতে অ্যাক্সেস করতে পারে:

std::cout << *(x+3) << ", " << *(x+7) << std::endl;

যদি xবোঝায় একটি অ্যারে , তবে অ্যারে-থেকে-পয়েন্টার ক্ষয় শুরু হবে, কারণ একটি অ্যারে এবং একটি পূর্ণসংখ্যা যোগ করা অর্থহীন (অ্যারেগুলিতে কোনও প্লাস অপারেশন নেই), তবে পয়েন্টার এবং একটি পূর্ণসংখ্যা যোগ করা অর্থপূর্ণ:

   +---+---+---+---+---+---+---+---+
x: |   |   |   |   |   |   |   |   |   int[8]
   +---+---+---+---+---+---+---+---+
     ^           ^               ^
     |           |               |
     |           |               |
     |           |               |
x+0  |      x+3  |          x+7  |     int*

(দ্রষ্টব্য যে সুস্পষ্টভাবে উত্পন্ন পয়েন্টারের কোনও নাম নেই, তাই আমি লিখেছি x+0 এটি সনাক্ত করার জন্য ))

অন্যদিকে, যদি অ্যারের প্রথম (বা অন্য কোনও) উপাদানটিতে xএকটি পয়েন্টারকে চিহ্নিত করে , তবে অ্যারে-থেকে-পয়েন্টার ক্ষয়টি প্রয়োজনীয় নয়, কারণ যে বিন্দুটিতেi যুক্ত হতে চলেছে তা ইতিমধ্যে বিদ্যমান:

   +---+---+---+---+---+---+---+---+
   |   |   |   |   |   |   |   |   |   int[8]
   +---+---+---+---+---+---+---+---+
     ^           ^               ^
     |           |               |
     |           |               |
   +-|-+         |               |
x: | | |    x+3  |          x+7  |     int*
   +---+

নোট করুন যে চিত্রিত ক্ষেত্রে, xপয়েন্টার ভেরিয়েবল (পাশের ছোট বাক্সের দ্বারা উপলব্ধিযোগ্য x), তবে এটি ঠিক একইভাবে পয়েন্টারটি ফেরত ফাংশনের ফলেও হতে পারে (বা অন্য কোনও ধরণের অভিব্যক্তি)T* ) ।

সূচক অপারেটর

যেহেতু সিনট্যাক্সটি *(x+i)কিছুটা আনাড়ি, তাই সি ++ বিকল্প সিনট্যাক্স সরবরাহ করে x[i]:

std::cout << x[3] << ", " << x[7] << std::endl;

সংযোজনটি কমটিভেটিভ হওয়ার কারণে, নিম্নলিখিত কোডটি ঠিক একই কাজ করে:

std::cout << 3[x] << ", " << 7[x] << std::endl;

সূচক অপারেটরের সংজ্ঞাটি নিম্নলিখিত আকর্ষণীয় সমতুল্যতার দিকে নিয়ে যায়:

&x[i]  ==  &*(x+i)  ==  x+i

তবে &x[0]সাধারণত এর সমতুল্য হয় নাx । পূর্ববর্তীটি একটি পয়েন্টার, পরে একটি অ্যারে। কেবলমাত্র যখন প্রসঙ্গটি অ্যারে-টু-পয়েন্টার ক্ষয়কে ট্রিগার করে xএবং &x[0]বিনিময়যোগ্যভাবে ব্যবহার করা যায়। উদাহরণ স্বরূপ:

T* p = &array[0];  // rewritten as &*(array+0), decay happens due to the addition
T* q = array;      // decay happens due to the assignment

প্রথম লাইনে, সংকলক একটি পয়েন্টার থেকে পয়েন্টারের কাছে একটি কার্য সনাক্ত করে, যা তুচ্ছভাবে সফল হয়। দ্বিতীয় লাইনে এটি একটি অ্যারে থেকে পয়েন্টারের কাছে একটি কার্য সনাক্ত করে । যেহেতু এটি অর্থহীন (তবে পয়েন্টার) থেকে পয়েন্টার অ্যাসাইনমেন্টটি বোঝায়), অ্যারে-থেকে-পয়েন্টার ক্ষয়টি যথারীতি কিক করে।

রেঞ্জ

ধরনের একটি অ্যারে T[n]আছে nউপাদান, থেকে সূচীবদ্ধ 0করার n-1; কোন উপাদান নেই n। এবং তবুও, অর্ধ-খোলা রেঞ্জগুলিকে সমর্থন করার জন্য (যেখানে শুরুটি অন্তর্ভুক্ত রয়েছে এবং শেষটি একচেটিয়া ), সি ++ পয়েন্টারকে (অস্তিত্বহীন) এন-থিম উপাদানটির গণনা করার অনুমতি দেয় তবে সেই পয়েন্টারটিকে অবজ্ঞা করা অবৈধ:

   +---+---+---+---+---+---+---+---+....
x: |   |   |   |   |   |   |   |   |   .   int[8]
   +---+---+---+---+---+---+---+---+....
     ^                               ^
     |                               |
     |                               |
     |                               |
x+0  |                          x+8  |     int*

উদাহরণস্বরূপ, আপনি যদি একটি অ্যারে বাছাই করতে চান তবে নীচের দুটিই সমানভাবে কাজ করবে:

std::sort(x + 0, x + n);
std::sort(&x[0], &x[0] + n);

নোট করুন যে এটি &x[n]দ্বিতীয় হিসাবে যুক্তি হিসাবে সরবরাহ করা অবৈধ, যেহেতু এটি সমতুল্য &*(x+n), এবং উপ-এক্সপ্রেশন *(x+n)প্রযুক্তিগতভাবে অনির্ধারিত আচরণকে অনুরোধ করে সি ++ (তবে সি 99 তে নয়) এ অপরিজ্ঞাত জন্য প্রার্থনা করে।

এছাড়াও নোট করুন যে আপনি কেবল xপ্রথম যুক্তি হিসাবে সরবরাহ করতে পারেন । এটি আমার স্বাদের জন্য কিছুটা বিরক্তিকর এবং এটি সংকলকটির জন্য টেমপ্লেট আর্গুমেন্ট ছাড়াকে কিছুটা শক্ত করে তোলে, কারণ সেই ক্ষেত্রে প্রথম যুক্তি একটি অ্যারে তবে দ্বিতীয় যুক্তিটি পয়েন্টার। (আবার, অ্যারে-টু-পয়েন্টার ক্ষয় লাথি মেরে)


যে ক্ষেত্রগুলিতে বিন্যাস বিন্দুতে ক্ষয় হয় না সেগুলি এখানে রেফারেন্সের জন্য চিত্রিত করা হয়েছে
কিংবদন্তি

@ ফ্রেডওভারফ্লো অ্যাক্সেস বা রেঞ্জের অংশে এটি উল্লেখযোগ্য হবে যে সি-অ্যারে লুপগুলির জন্য সি ++ 11 রেঞ্জ-ভিত্তিক কাজ করে।
gnzlbg

135

প্রোগ্রামাররা প্রায়শই বহুমাত্রিক অ্যারেগুলিকে পয়েন্টারগুলির অ্যারে দিয়ে বিভ্রান্ত করে।

বহুমাত্রিক অ্যারে

বেশিরভাগ প্রোগ্রামার নামধারী বহুমাত্রিক অ্যারেগুলির সাথে পরিচিত, তবে অনেকে বহুমুখী অ্যারে বেনামেও তৈরি হতে পারে এই বিষয়টি সম্পর্কে অসচেতন। বহুমাত্রিক অ্যারে প্রায়শই "অ্যারের অ্যারে" বা " সত্য " হিসাবে পরিচিত বহুমাত্রিক অ্যারে" হিসাবে পরিচিত।

নামকরণ বহুমাত্রিক অ্যারে

নামযুক্ত বহুমাত্রিক অ্যারেগুলি ব্যবহার করার সময়, সমস্ত মাত্রা সংকলনের সময় জানা উচিত:

int H = read_int();
int W = read_int();

int connect_four[6][7];   // okay

int connect_four[H][7];   // ISO C++ forbids variable length array
int connect_four[6][W];   // ISO C++ forbids variable length array
int connect_four[H][W];   // ISO C++ forbids variable length array

একটি নামযুক্ত বহুমাত্রিক অ্যারে স্মৃতিতে দেখতে কেমন লাগে:

              +---+---+---+---+---+---+---+
connect_four: |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+

নোট করুন যে উপরের মতো 2D গ্রিডগুলি নিছক সহায়ক সাহায্যদর্শন। সি ++ এর দৃষ্টিকোণ থেকে, মেমরিটি বাইটের একটি "সমতল" ক্রম। একটি বহুমাত্রিক অ্যারের উপাদানগুলি সারি-প্রধান ক্রমে সংরক্ষণ করা হয়। অর্থাৎ connect_four[0][6]এবং connect_four[1][0]মেমরি প্রতিবেশী। আসলে, connect_four[0][7]এবং connect_four[1][0]একই উপাদান বোঝা! এর অর্থ হল যে আপনি বহুমাত্রিক অ্যারে নিতে পারেন এবং তাদেরকে বৃহত, এক-মাত্রিক অ্যারে হিসাবে গণ্য করতে পারেন:

int* p = &connect_four[0][0];
int* q = p + 42;
some_int_sequence_algorithm(p, q);

বেনামে বহুমাত্রিক অ্যারে

বেনামে বহুমাত্রিক অ্যারে সহ প্রথমটি ব্যতীত সমস্ত মাত্রা অবশ্যই সংকলনের সময় জানা উচিত:

int (*p)[7] = new int[6][7];   // okay
int (*p)[7] = new int[H][7];   // okay

int (*p)[W] = new int[6][W];   // ISO C++ forbids variable length array
int (*p)[W] = new int[H][W];   // ISO C++ forbids variable length array

অজ্ঞাত বহুমাত্রিক অ্যারে স্মৃতিতে দেখতে কেমন লাগে:

              +---+---+---+---+---+---+---+
        +---> |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |     |   |   |   |   |   |   |   |
        |     +---+---+---+---+---+---+---+
        |
      +-|-+
   p: | | |
      +---+

নোট করুন যে অ্যারে নিজেই মেমরিতে একক ব্লক হিসাবে বরাদ্দ করা হয়েছে।

পয়েন্টার অ্যারে

আপনি অন্য স্তরের ইন্ডিরিয়ারেশন প্রবর্তন করে স্থির প্রস্থের সীমাবদ্ধতা অতিক্রম করতে পারেন।

পয়েন্টারের নামযুক্ত অ্যারে

এখানে পাঁচটি পয়েন্টারের নাম দেওয়া অ্যারে রয়েছে যা বিভিন্ন দৈর্ঘ্যের বেনামে অ্যারে দিয়ে শুরু করা হয়:

int* triangle[5];
for (int i = 0; i < 5; ++i)
{
    triangle[i] = new int[5 - i];
}

// ...

for (int i = 0; i < 5; ++i)
{
    delete[] triangle[i];
}

এটি এখানে স্মৃতিতে কেমন দেখাচ্ছে:

          +---+---+---+---+---+
          |   |   |   |   |   |
          +---+---+---+---+---+
            ^
            | +---+---+---+---+
            | |   |   |   |   |
            | +---+---+---+---+
            |   ^
            |   | +---+---+---+
            |   | |   |   |   |
            |   | +---+---+---+
            |   |   ^
            |   |   | +---+---+
            |   |   | |   |   |
            |   |   | +---+---+
            |   |   |   ^
            |   |   |   | +---+
            |   |   |   | |   |
            |   |   |   | +---+
            |   |   |   |   ^
            |   |   |   |   |
            |   |   |   |   |
          +-|-+-|-+-|-+-|-+-|-+
triangle: | | | | | | | | | | |
          +---+---+---+---+---+

যেহেতু এখন প্রতিটি লাইন পৃথকভাবে বরাদ্দ করা হয়েছে, 2D অ্যারে 1D অ্যারে হিসাবে দেখা আর কাজ করে না।

পয়েন্টারগুলির বেনামে অ্যারে

এখানে 5 (বা অন্য কোনও সংখ্যার) বেনামে অ্যারে রয়েছে যা বিভিন্ন দৈর্ঘ্যের বেনামে অ্যারে দিয়ে সূচনা করা হয়:

int n = calculate_five();   // or any other number
int** p = new int*[n];
for (int i = 0; i < n; ++i)
{
    p[i] = new int[n - i];
}

// ...

for (int i = 0; i < n; ++i)
{
    delete[] p[i];
}
delete[] p;   // note the extra delete[] !

এটি এখানে স্মৃতিতে কেমন দেখাচ্ছে:

          +---+---+---+---+---+
          |   |   |   |   |   |
          +---+---+---+---+---+
            ^
            | +---+---+---+---+
            | |   |   |   |   |
            | +---+---+---+---+
            |   ^
            |   | +---+---+---+
            |   | |   |   |   |
            |   | +---+---+---+
            |   |   ^
            |   |   | +---+---+
            |   |   | |   |   |
            |   |   | +---+---+
            |   |   |   ^
            |   |   |   | +---+
            |   |   |   | |   |
            |   |   |   | +---+
            |   |   |   |   ^
            |   |   |   |   |
            |   |   |   |   |
          +-|-+-|-+-|-+-|-+-|-+
          | | | | | | | | | | |
          +---+---+---+---+---+
            ^
            |
            |
          +-|-+
       p: | | |
          +---+

রূপান্তর

অ্যারে-থেকে-পয়েন্টার ক্ষয় স্বাভাবিকভাবে অ্যারে এবং পয়েন্টারগুলির অ্যারেগুলিতে বিস্তৃত হয়:

int array_of_arrays[6][7];
int (*pointer_to_array)[7] = array_of_arrays;

int* array_of_pointers[6];
int** pointer_to_pointer = array_of_pointers;

যাইহোক, কোন অন্তর্নিহিত রূপান্তর হয় T[h][w]থেকে T**। যদি এ ধরনের একটি অন্তর্নিহিত রূপান্তর উপস্থিত ছিল, ফলে একজন অ্যারের প্রথম উপাদান একটি পয়েন্টার হবে hপয়েন্টার T(মূল 2D অ্যারের মধ্যে একটি লাইন প্রথম উপাদান প্রতিটি ইশারা), কিন্তু যে পয়েন্টার অ্যারের মধ্যে যে কোন জায়গায় কোন অস্তিত্ব নেই স্মৃতি এখনও। আপনি যদি এই জাতীয় রূপান্তর চান, আপনার অবশ্যই প্রয়োজনীয় পয়েন্টার অ্যারে নিজেই তৈরি এবং পূরণ করতে হবে:

int connect_four[6][7];

int** p = new int*[6];
for (int i = 0; i < 6; ++i)
{
    p[i] = connect_four[i];
}

// ...

delete[] p;

নোট করুন যে এটি মূল বহুমাত্রিক অ্যারেটির একটি ভিউ তৈরি করে। পরিবর্তে যদি আপনার একটি অনুলিপি প্রয়োজন হয় তবে আপনাকে অতিরিক্ত অ্যারে তৈরি করতে হবে এবং ডেটা নিজেই অনুলিপি করতে হবে:

int connect_four[6][7];

int** p = new int*[6];
for (int i = 0; i < 6; ++i)
{
    p[i] = new int[7];
    std::copy(connect_four[i], connect_four[i + 1], p[i]);
}

// ...

for (int i = 0; i < 6; ++i)
{
    delete[] p[i];
}
delete[] p;

পরামর্শ হিসেবে: আপনি নির্দেশ, উচিত int connect_four[H][7];, int connect_four[6][W]; int connect_four[H][W];সেইসাথে int (*p)[W] = new int[6][W];এবং int (*p)[W] = new int[H][W];বৈধ বিবৃতি, যখন হয় Hএবং Wকম্পাইল-সময়ে পরিচিত হয়।
রবার্টস

88

নিয়োগ

কোনও নির্দিষ্ট কারণে, অ্যারে একে অপরকে বরাদ্দ করা যায় না। std::copyপরিবর্তে ব্যবহার করুন:

#include <algorithm>

// ...

int a[8] = {2, 3, 5, 7, 11, 13, 17, 19};
int b[8];
std::copy(a + 0, a + 8, b);

সত্য অ্যারে অ্যাসাইনমেন্টটি যা সরবরাহ করতে পারে তার চেয়ে এটি আরও নমনীয় কারণ বৃহত্তর অ্যারের টুকরাগুলি আরও ছোট অ্যারেতে অনুলিপি করা সম্ভব। std::copyসর্বাধিক কর্মক্ষমতা দেওয়ার জন্য সাধারণত আদিম ধরণের জন্য বিশেষীকরণ করা হয়। এটা অসম্ভবstd::memcpy আরও ভালভাবে সম্পাদন করার । সন্দেহ হলে, পরিমাপ।

যদিও আপনি সরাসরি অ্যারে নির্ধারণ করতে পারবেন না, আপনি স্ট্রাক্ট এবং ক্লাসগুলি বরাদ্দ করতে পারেন যাতে অ্যারে সদস্য রয়েছে। এর কারণ অ্যারে সদস্যরা সদস্যপদে অনুলিপি করা হয় অ্যাসাইনমেন্ট অপারেটর দ্বারা করা হয় যা সংকলক দ্বারা ডিফল্ট হিসাবে সরবরাহ করা হয়। যদি আপনি নিজের স্ট্রাক্ট বা শ্রেণির ধরণের জন্য অ্যাসাইনমেন্ট অপারেটরটিকে ম্যানুয়ালি সংজ্ঞায়িত করেন তবে আপনাকে অ্যারে সদস্যদের জন্য ম্যানুয়াল অনুলিপি করতে হবে।

প্যারামিটার পাস হচ্ছে

অ্যারেগুলি মান দিয়ে পাস করা যায় না। আপনি হয় সেগুলি পয়েন্টার বা রেফারেন্স দিয়ে পাস করতে পারেন।

পয়েন্টার দিয়ে পাস করুন

যেহেতু অ্যারেগুলি মান দ্বারা পাস করা যায় না, সাধারণত তাদের প্রথম উপাদানটির একটি পয়েন্টার পরিবর্তে মান দ্বারা প্রেরণ করা হয়। একে প্রায়শই "পাস বাই পয়েন্টার" বলা হয়। যেহেতু অ্যারেটির আকারটি সেই পয়েন্টারের মাধ্যমে পুনরুদ্ধারযোগ্য নয়, আপনাকে অ্যারের (ক্লাসিক সি সমাধান) আকারটি নির্দেশ করে একটি দ্বিতীয় প্যারামিটার পাস করতে হবে বা অ্যারের শেষ উপাদানটির পরে দ্বিতীয় পয়েন্টারটি নির্দেশ করবে (সি ++ পুনরুক্তি সমাধান) :

#include <numeric>
#include <cstddef>

int sum(const int* p, std::size_t n)
{
    return std::accumulate(p, p + n, 0);
}

int sum(const int* p, const int* q)
{
    return std::accumulate(p, q, 0);
}

সিন্ট্যাক্টিক বিকল্প হিসাবে, আপনি প্যারামিটারগুলি হিসাবে ঘোষণা করতে পারেন T p[]এবং এটি T* p কেবলমাত্র পরামিতি তালিকার প্রসঙ্গে যেমন একই জিনিসটি বোঝায় :

int sum(const int p[], std::size_t n)
{
    return std::accumulate(p, p + n, 0);
}

আপনি rewriting যেমন কম্পাইলার মনে করতে পারেন T p[]থেকে T *p প্যারামিটার তালিকা শুধুমাত্র প্রেক্ষাপটে । এই বিশেষ নিয়মটি অ্যারে এবং পয়েন্টারগুলি সম্পর্কে পুরো বিভ্রান্তির জন্য আংশিকভাবে দায়ী। অন্য প্রতিটি প্রসঙ্গে, কোনও অ্যারে বা পয়েন্টার হিসাবে কোনও কিছু ঘোষণা করা বিশাল makes পার্থক্য করে।

দুর্ভাগ্যক্রমে, আপনি কোনও অ্যারে প্যারামিটারে একটি আকারও সরবরাহ করতে পারেন যা সংকলক চুপচাপ উপেক্ষা করে। এটি হ'ল, নিম্নলিখিত তিনটি স্বাক্ষর হুবহু সমতুল্য, সংকলক ত্রুটির দ্বারা নির্দেশিত:

int sum(const int* p, std::size_t n)

// error: redefinition of 'int sum(const int*, size_t)'
int sum(const int p[], std::size_t n)

// error: redefinition of 'int sum(const int*, size_t)'
int sum(const int p[8], std::size_t n)   // the 8 has no meaning here

রেফারেন্স দ্বারা পাস

অ্যারে রেফারেন্স দ্বারাও পাস করা যেতে পারে:

int sum(const int (&a)[8])
{
    return std::accumulate(a + 0, a + 8, 0);
}

এই ক্ষেত্রে অ্যারের আকার উল্লেখযোগ্য is যেহেতু ঠিক এমন 8 টি উপাদানের অ্যারে গ্রহণ করে এমন কোনও ফাংশন লিখলে খুব কম ব্যবহার হয়, প্রোগ্রামাররা সাধারণত টেমপ্লেট হিসাবে এই জাতীয় ফাংশন লেখেন:

template <std::size_t n>
int sum(const int (&a)[n])
{
    return std::accumulate(a + 0, a + n, 0);
}

মনে রাখবেন যে আপনি কেবলমাত্র একটি পূর্ণসংখ্যার আসল বিন্যাস সহ এই জাতীয় ফাংশন টেম্পলেট কল করতে পারেন, কোনও সংখ্যার পয়েন্টারের সাথে নয়। অ্যারের আকারটি স্বয়ংক্রিয়ভাবে অনুমিত হয় এবং প্রতিটি আকারের জন্য nটেমপ্লেট থেকে একটি আলাদা ফাংশন ইনস্ট্যান্ট হয়। আপনি বেশ কার্যকর ফাংশন টেম্পলেটগুলিও লিখতে পারেন যা উভয় উপাদান উপাদান থেকে এবং আকার থেকে বিমূর্ত হয়।


2
একটি নোট যুক্ত করার পক্ষে এটি উপযুক্ত হতে পারে যে এমনকি void foo(int a[3]) aএটির মতো দেখে মনে হচ্ছে যে কোনওটি মান দ্বারা অ্যারেটি পার করছে, এর aভিতরে fooপরিবর্তন করে আসল অ্যারেটি সংশোধন করবে। এটি পরিষ্কার হওয়া উচিত কারণ অ্যারেগুলি অনুলিপি করা যায় না, তবে এটি আরও শক্তিশালী করা উপযুক্ত।
gnzlbg

সি ++ 20 এর রয়েছেranges::copy(a, b)
এলএফ

int sum( int size_, int a[size_]);- থেকে (আমি মনে করি) সি 99 থেকে
শেফ গ্ল্যাডিয়েটর

73

৫. অ্যারে ব্যবহার করার সময় সাধারণ সমস্যাগুলি।

5.1 পিটফল: টাইপ-অনিরাপদ লিঙ্কে ভরসা করা।

ঠিক আছে, আপনাকে বলা হয়েছে, বা নিজেকে খুঁজে পেয়েছেন, যে গ্লোবালগুলি (অনুবাদ ক্ষেত্রের বাইরে থাকা নেমস্পেস স্কোপ ভেরিয়েবলগুলি) এভিল ™ কিন্তু আপনি কি জানেন কী সত্যই মন্দ ™ এগুলি কি? দুটি ফাইল [মেইন.সি.পি.পি.] এবং [নাম্বারস.সি.পি.] সমন্বয়ে নীচের প্রোগ্রামটি বিবেচনা করুন:

// [main.cpp]
#include <iostream>

extern int* numbers;

int main()
{
    using namespace std;
    for( int i = 0;  i < 42;  ++i )
    {
        cout << (i > 0? ", " : "") << numbers[i];
    }
    cout << endl;
}

// [numbers.cpp]
int numbers[42] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

উইন্ডোজ In-এ এই সংকলনগুলি এবং MinGW g ++ 4.4.1 এবং ভিজ্যুয়াল সি ++ 10.0 উভয়ের সাথেই সূক্ষ্ম লিঙ্ক করে।

যেহেতু প্রকারগুলি মেলে না, সুতরাং আপনি যখন এটি চালাবেন তখন প্রোগ্রামটি ক্র্যাশ হয়ে যায়।

উইন্ডোজ 7 ক্র্যাশ সংলাপ

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

অনুশীলন ব্যাখ্যা: main.cppঅ্যারেতে পয়েন্টার হিসাবে বিবেচনা করা হয়, অ্যারের হিসাবে একই ঠিকানায় স্থাপন করা হয়। 32-বিট এক্সিকিউটেবলের জন্য এর অর্থ intহ'ল অ্যারেতে প্রথম মানটি পয়েন্টার হিসাবে বিবেচিত হয়। অর্থাত, এ পরিবর্তনশীল রয়েছে, বা প্রদর্শিত হয় ধারণ, । এর ফলে প্রোগ্রামটি ঠিকানার জায়গার একেবারে নীচে মেমোরি অ্যাক্সেস করতে পারে যা প্রচলিতভাবে সংরক্ষিত এবং ফাঁদে ফেলে। ফলাফল: আপনি একটি ক্রাশ পান।main.cppnumbers(int*)1

সংকলকগণ এই ত্রুটিটি নির্ণয় না করার জন্য সম্পূর্ণরূপে তাদের অধিকারের মধ্যে রয়েছে, কারণ সি ++ 11 §3.5 / 10 ঘোষণার জন্য সামঞ্জস্যপূর্ণ ধরণের প্রয়োজনীয়তা সম্পর্কে বলে,

[N3290 §3.5 / 10]
প্রকারের পরিচয় সম্পর্কিত এই বিধি লঙ্ঘনের জন্য ডায়গনিস্টিকের প্রয়োজন হয় না।

একই অনুচ্ছেদে অনুমোদিত যে প্রকরণটির অনুমতি দেওয়া হয়েছে:

… একটি অ্যারে অবজেক্টের জন্য ঘোষণাগুলি অ্যারের প্রকারগুলি নির্দিষ্ট করতে পারে যা প্রধান অ্যারে বাউন্ডের উপস্থিতি বা অনুপস্থিতির দ্বারা পৃথক হয় (8.3.4)

এই অনুমোদিত প্রকরণটির মধ্যে একটি অনুবাদ ইউনিটে একটি অ্যারে হিসাবে নাম ঘোষণা করা এবং অন্য অনুবাদ ইউনিটে পয়েন্টার হিসাবে অন্তর্ভুক্ত নয়।

5.2 সমস্যা: অকাল অপটিমাইজেশন করা ( memsetএবং বন্ধুরা)।

এখনও লেখা হয়নি

5.3 পিটফল: উপাদান সংখ্যা পেতে সি আইডিয়াম ব্যবহার করে।

গভীর সি অভিজ্ঞতার সাথে লেখাই স্বাভাবিক ...

#define N_ITEMS( array )   (sizeof( array )/sizeof( array[0] ))

যেহেতু arrayযেখানে প্রয়োজন সেখানে প্রথম উপাদানটির দিকে ইঙ্গিত করার সিদ্ধান্ত নিয়েছে, তাই প্রকাশটিও sizeof(a)/sizeof(a[0])লিখতে পারে sizeof(a)/sizeof(*a)। এটি একই অর্থ এবং এটি যেভাবে লেখা হোক না কেন এটি অ্যারের সংখ্যা উপাদানগুলি সন্ধানের জন্য সি আইডিয়াম

মূল ক্ষতি: সি আইডিয়ম টাইপসেফ নয়। উদাহরণস্বরূপ, কোড…

#include <stdio.h>

#define N_ITEMS( array ) (sizeof( array )/sizeof( *array ))

void display( int const a[7] )
{
    int const   n = N_ITEMS( a );          // Oops.
    printf( "%d elements.\n", n );
}

int main()
{
    int const   moohaha[]   = {1, 2, 3, 4, 5, 6, 7};

    printf( "%d elements, calling display...\n", N_ITEMS( moohaha ) );
    display( moohaha );
}

এতে একটি পয়েন্টার পাস করে N_ITEMSএবং সম্ভবত সম্ভবত একটি ভুল ফলাফল তৈরি করে। এটি তৈরি করে উইন্ডোজ 7-তে 32-বিট এক্সিকিউটেবল হিসাবে সংকলিত ...

7 উপাদান, প্রদর্শন কলিং ...
1 উপাদান।

  1. কম্পাইলার নতুন করে লেখা হয় int const a[7]শুধু int const a[]
  2. কম্পাইলার নতুন করে লেখা হয় int const a[]থেকেint const* a
  3. N_ITEMS সুতরাং একটি পয়েন্টার সহ আহ্বান করা হয়।
  4. একটি 32-বিট এক্সিকিউটেবলের জন্য sizeof(array) (পয়েন্টারের আকার) এর জন্য 4 হয়।
  5. sizeof(*array)সমান sizeof(int), যা একটি 32-বিট এক্সিকিউটেবলের জন্য 4ও হয়।

রান সময়ে এই ত্রুটিটি সনাক্ত করতে আপনি করতে পারেন…

#include <assert.h>
#include <typeinfo>

#define N_ITEMS( array )       (                               \
    assert((                                                    \
        "N_ITEMS requires an actual array as argument",        \
        typeid( array ) != typeid( &*array )                    \
        )),                                                     \
    sizeof( array )/sizeof( *array )                            \
    )

7 টি উপাদান, প্রদর্শন কলিং ...
দৃser় ব্যর্থতা: ("এন_আইটিইএমএসের যুক্তি হিসাবে একটি আসল অ্যারের প্রয়োজন", টাইপড (ক)! = টাইপড (& * ক)), ফাইল রানটাইম_ডিটেক্ট ion.cpp, লাইন 16

এই অ্যাপ্লিকেশনটি রানটাইমটিকে অস্বাভাবিক উপায়ে বন্ধ করার জন্য অনুরোধ করেছে।
আরও তথ্যের জন্য দয়া করে অ্যাপ্লিকেশনটির সহায়তা দলের সাথে যোগাযোগ করুন।

রানটাইম ত্রুটি সনাক্তকরণ কোনও সনাক্তকরণের চেয়ে ভাল, তবে এটি একটি সামান্য প্রসেসরের সময় এবং সম্ভবত আরও অনেক প্রোগ্রামার সময় নষ্ট করে। সংকলনের সময় সনাক্তকরণের সাথে আরও ভাল! এবং আপনি যদি C ++ 98 দিয়ে স্থানীয় ধরণের অ্যারে সমর্থন না করে খুশি হন তবে আপনি এটি করতে পারেন:

#include <stddef.h>

typedef ptrdiff_t   Size;

template< class Type, Size n >
Size n_items( Type (&)[n] ) { return n; }

#define N_ITEMS( array )       n_items( array )

এই সংজ্ঞাটি সংশোধন করে প্রথম সম্পূর্ণ প্রোগ্রামে প্রতিস্থাপন করা হয়েছে, জি ++ সহ, আমি পেয়েছি…

এম: \ গণনা> জি ++ কমপাইল_টাইম_ডেটিশন.সিপি কম্পাইল_টাইম_ডিটেকশন.
cpp: ফাংশনে 'শূন্য ডিসপ্লে (
কনটেন্ট *)': সংকলন_টাইম_ডেটিশন.সি.পি .: 14: ত্রুটি: 'এন_াইটেমস (কনটেন্ট ইন * *)' তে কল করার জন্য কোনও মিল নেই

এম: \ গণনা> _

এটি কীভাবে কাজ করে: অ্যারে রেফারেন্স দ্বারা পাস করা হয়n_items , এবং তাই এটি প্রথম উপাদানে পয়েন্টার ক্ষয় করে না, এবং ফাংশন শুধু টাইপ দ্বারা নির্দিষ্ট উপাদানের সংখ্যা ফিরে আসতে পারেন।

সি ++ 11 এর সাহায্যে আপনি এটি স্থানীয় টাইপের অ্যারেগুলির জন্যও ব্যবহার করতে পারেন এবং এটি অ্যারের উপাদানগুলির সংখ্যা অনুসন্ধান করার জন্য এটি নিরাপদ সি ++ আইডিয়াম

5.4 সি ++ 11 এবং সি ++ 14 পিটফল: একটি constexprঅ্যারের আকার ফাংশন ব্যবহার করে।

সি ++ 11 এর পরে এবং এটি পরে প্রাকৃতিক তবে আপনি সি ++ 03 ফাংশনটি প্রতিস্থাপন করতে বিপজ্জনক দেখবেন!

typedef ptrdiff_t   Size;

template< class Type, Size n >
Size n_items( Type (&)[n] ) { return n; }

সঙ্গে

using Size = ptrdiff_t;

template< class Type, Size n >
constexpr auto n_items( Type (&)[n] ) -> Size { return n; }

যেখানে উল্লেখযোগ্য পরিবর্তনটি হ'ল ব্যবহার constexpr, যা এই ফাংশনটিকে একটি সংকলন সময় ধ্রুবক উত্পাদন করতে দেয় ।

উদাহরণস্বরূপ, C ++ 03 ফাংশনের বিপরীতে, এই জাতীয় সংকলনের সময় ধ্রুবকটিকে একই আকারের অ্যারেটিকে অন্য হিসাবে ঘোষণা করতে ব্যবহার করা যেতে পারে:

// Example 1
void foo()
{
    int const x[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
    constexpr Size n = n_items( x );
    int y[n] = {};
    // Using y here.
}

তবে constexprসংস্করণটি ব্যবহার করে এই কোডটি বিবেচনা করুন :

// Example 2
template< class Collection >
void foo( Collection const& c )
{
    constexpr int n = n_items( c );     // Not in C++14!
    // Use c here
}

auto main() -> int
{
    int x[42];
    foo( x );
}

দুর্যোগ: জুলাই ২০১৫ -pedantic-errorsপর্যন্ত উপরেরটি MinCW -64 5.1.0 এর সাথে সংকলন করে , এবং gcc.godbolt.org/ এ অনলাইন সংকলকগুলির সাথে পরীক্ষার জন্য , ক্ল্যাং 3.0 এবং ক্ল্যাং 3.2 সহ, তবে ঝনঝন 3.3, 3.4 দিয়ে নয়। 1, 3.5.0, 3.5.1, 3.6 (আরসি 1) বা 3.7 (পরীক্ষামূলক)। এবং উইন্ডোজ প্ল্যাটফর্মের জন্য গুরুত্বপূর্ণ এটি ভিজ্যুয়াল সি ++ 2015 এর সাথে সংকলন করে না The কারণটি রেফারেন্সের ব্যবহার সম্পর্কে একটি সি ++ 11 / সি ++ 14 বিবৃতিconstexpr অভিব্যক্তিগুলিতে :

সি ++ 11 সি ++ 14 $ 5.19 / 2 নয় ড্যাশ

একটি শর্তাধীন প্রকাশ e একটি হল কোর ধ্রুবক অভিব্যক্তি যদি না মূল্যায়ন e, বিমূর্ত মেশিন (1.9), নিম্নলিখিত এক্সপ্রেশন এক মূল্যায়ন করবেন নিয়ম নিম্নলিখিত:
        ⋮

  • একটি আইডি-এক্সপ্রেশন যা রেফারেন্সের পূর্ববর্তী এবং না হয় রেফারেন্স প্রকারের একটি পরিবর্তনশীল বা ডেটা সদস্যকে বোঝায়
    • এটি একটি ধ্রুবক অভিব্যক্তি বা দিয়ে শুরু করা হয়
    • এটি কোনও অবজেক্টের অ-স্থিতিশীল ডেটা সদস্য, যার জীবনকাল ই এর মূল্যায়নের মধ্য দিয়ে শুরু হয়েছিল;

একজন সর্বদা আরও ভার্বোস লিখতে পারেন

// Example 3  --  limited

using Size = ptrdiff_t;

template< class Collection >
void foo( Collection const& c )
{
    constexpr Size n = std::extent< decltype( c ) >::value;
    // Use c here
}

… তবে এটি ব্যর্থ হয় যখন Collectionকোনও কাঁচা অ্যারে না থাকে।

অ-অ্যারে হতে পারে এমন সংগ্রহগুলি নিয়ে কাজ করার জন্য একটি n_itemsফাংশনের ওভারলোডযোগ্যতা প্রয়োজন , তবে সংকলন সময় ব্যবহারের জন্য অ্যারের আকারের একটি সংকলন সময় উপস্থাপনা প্রয়োজন। এবং ক্লাসিক সি ++ 03 সমাধান, যা সি ++ 11 এবং সি ++ 14 এও সূক্ষ্মভাবে কাজ করে, তা হ'ল ফাংশনটি তার ফলাফলকে কোনও মান হিসাবে নয় তবে তার ফাংশন ফলাফলের ধরণের মাধ্যমে রিপোর্ট করতে দেয় । উদাহরণস্বরূপ:

// Example 4 - OK (not ideal, but portable and safe)

#include <array>
#include <stddef.h>

using Size = ptrdiff_t;

template< Size n >
struct Size_carrier
{
    char sizer[n];
};

template< class Type, Size n >
auto static_n_items( Type (&)[n] )
    -> Size_carrier<n>;
// No implementation, is used only at compile time.

template< class Type, size_t n >        // size_t for g++
auto static_n_items( std::array<Type, n> const& )
    -> Size_carrier<n>;
// No implementation, is used only at compile time.

#define STATIC_N_ITEMS( c ) \
    static_cast<Size>( sizeof( static_n_items( c ).sizer ) )

template< class Collection >
void foo( Collection const& c )
{
    constexpr Size n = STATIC_N_ITEMS( c );
    // Use c here
    (void) c;
}

auto main() -> int
{
    int x[42];
    std::array<int, 43> y;
    foo( x );
    foo( y );
}

এর জন্য রিটার্নের ধরণের পছন্দ সম্পর্কে static_n_items: এই কোডটি ব্যবহার করে না std::integral_constant কারণ std::integral_constantফলাফলটি সরাসরি একটি constexprমান হিসাবে উপস্থাপিত হয়, মূল সমস্যাটির পুনরায় উত্থাপন করে। Size_carrierক্লাসের পরিবর্তে একটিকে ফাংশনটি সরাসরি অ্যারেতে একটি রেফারেন্স ফিরিয়ে দিতে দেয়। তবে, সবাই এই সিনট্যাক্সের সাথে পরিচিত নয়।

নামকরণ সম্পর্কে: constexpr-অনুপাতিক-কারণে-রেফারেন্স সমস্যার এই সমাধানের অংশটি হ'ল সংকলন সময়ের পছন্দটি ধ্রুবক স্পষ্ট করে তোলা।

আশা করি উফ-সেখানে-একটি-রেফারেন্স-জড়িত আপনার-ই- constexprইস্যুটি সি ++ 17 দিয়ে স্থির করা হবে তবে ততক্ষণ পর্যন্ত STATIC_N_ITEMSউপরের ফলন বহনযোগ্যতার মতো ম্যাক্রো যেমন ক্ল্যাং এবং ভিজ্যুয়াল সি ++ সংকলক, রক্ষণাবেক্ষণের ধরণ নিরাপত্তা।

সম্পর্কিত: ম্যাক্রোস স্কোপগুলিকে সম্মান করে না, তাই নামের সংঘর্ষ এড়ানোর জন্য একটি নামের উপসর্গ ব্যবহার করা ভাল ধারণা হতে পারে, যেমন MYLIB_STATIC_N_ITEMS


1
+1 গ্রেট সি কোডিং পরীক্ষা: আমি ভিসি ++ 10.0 এবং জিসিসি 4.1.2 এ 15 মিনিট সময় ঠিক করার চেষ্টা Segmentation faultকরেছি ... আপনার ব্যাখ্যাগুলি পড়ে শেষ পর্যন্ত আমি খুঁজে পেয়েছি / বুঝতে পেরেছি! দয়া করে আপনার §5.2 বিভাগটি লিখুন :-) চিয়ার্স

ভাল. একটি নিট - কাউন্টঅফের জন্য রিটার্নের ধরণ পিটিআরডিফ_t এর পরিবর্তে আকার_t হওয়া উচিত। এটি সম্ভবত উল্লেখযোগ্য যে সি ++ 11/14 এ এটি কনটেক্সট্রপ এবং নয়েজিবদ্ধ হওয়া উচিত।
রিকি 65

@ রিকি 65: সি ++ 11 বিবেচনা উল্লেখ করার জন্য ধন্যবাদ। এই বৈশিষ্ট্যগুলির জন্য সমর্থন ভিজ্যুয়াল সি ++ এর জন্য আসতে দেরি করেছে। সম্পর্কিত size_t, এর আধুনিক কোনও প্ল্যাটফর্মগুলির জন্য আমি যে সুবিধাগুলি জানি তা নেই তবে সি এবং সি ++ এর অন্তর্নিহিত ধরণের রূপান্তর নিয়মের কারণে এতে বেশ কিছু সমস্যা রয়েছে। এটি হ'ল ptrdiff_tসমস্যাগুলি এড়াতে খুব ইচ্ছাকৃতভাবে ব্যবহৃত হয় size_t। তবে একটি সচেতন হওয়া উচিত যে জি ++ এর সাথে টেমপ্লেট প্যারামিটারের সাথে অ্যারের আকারের সাথে মিল থাকা সমস্যা রয়েছে size_t(যদি না আমি মনে করি না যে এই সংকলক-নির্দিষ্ট সমস্যাটি নন- এর size_tসাথে গুরুত্বপূর্ণ তবে ওয়াইএমএমভি)।
চিয়ার্স এবং এইচটিএইচ - আলফ

@Alf। স্ট্যান্ডার্ড ওয়ার্কিং ড্রাফ্টে (N3936) 8.3.4 আমি পড়েছি - একটি অ্যারের গণ্ডি হ'ল ... "স্ট্যান্ডার্ড :: আকার_t টাইপের রূপান্তরিত ধ্রুবক অভিব্যক্তি এবং এর মান শূন্যের চেয়ে বড় হবে"।
রিকি 65

@Ricky: আপনি অসঙ্গতি উল্লেখ করে থাকেন, তবে এই বিবৃতি না সেখানে বর্তমান সি ++ 11 মান তাই এটি প্রসঙ্গ অনুমান করা কঠিন হয়, কিন্তু অসঙ্গতি (ক পরিবর্তনশীল বরাদ্দ অ্যারের করতে প্রতি সি + বাউন্ড 0 হও, +11 §5.3.4 / 7) সম্ভবত সি ++ 14 এ শেষ হবে না। খসড়াগুলি কেবলমাত্র: খসড়াগুলি। আপনি যদি এর পরিবর্তে "এর" কী বোঝায় সে সম্পর্কে জিজ্ঞাসা করেন তবে এটি মূল রূপটি বোঝায়, রূপান্তরিত নয়। যদি তৃতীয় দিকে আপনি এটি উল্লেখ করেন কারণ আপনি মনে করেন যে এই জাতীয় বাক্যটির অর্থ হ'ল size_tআকারের অ্যারেগুলি বোঝাতে ব্যবহার করা উচিত , অবশ্যই তা তা নয়।
চিয়ার্স এবং এইচটিএইচ - আলফ

72

অ্যারে তৈরি এবং সূচনা

অন্য যে কোনও ধরণের সি ++ অবজেক্টের মতো, অ্যারেগুলি সরাসরি নামযুক্ত ভেরিয়েবলগুলিতে সংরক্ষণ করা যেতে পারে (তারপরে আকারটি অবশ্যই একটি সংকলন-ধ্রুবক ধ্রুবক হতে হবে; সি ++ ভিএলএ সমর্থন করে না ), বা এগুলি গোপনে বেনামে সংরক্ষণ করা যেতে পারে এবং অপ্রত্যক্ষভাবে তার মাধ্যমে প্রবেশ করা যেতে পারে পয়েন্টার (কেবল তখনই রানটাইমে আকারটি গণনা করা যেতে পারে)।

স্বয়ংক্রিয় অ্যারে

অটোমেটিক অ্যারে ভেরিয়েবলের সংজ্ঞা দিয়ে প্রতিটি সময় নিয়ন্ত্রণের প্রবাহটি অটোমেটিক অ্যারে ("স্ট্যাকের উপরে থাকা অ্যারেগুলি") তৈরি করা হয়:

void foo()
{
    int automatic_array[8];
}

সূচনাটি আরোহী ক্রমে সঞ্চালিত হয়। লক্ষ করুন যে প্রাথমিক মানগুলি উপাদান ধরণের উপর নির্ভর করে T:

  • যদি Tকোনও পিওডি হয় ( intউপরের উদাহরণের মতো), কোনও সূচনা হয় না।
  • অন্যথায়, ডিফল্ট-নির্মাণকারী Tসমস্ত উপাদানকে আরম্ভ করে।
  • যদি Tকোনও অ্যাক্সেসযোগ্য ডিফল্ট-কনস্ট্রাক্টর সরবরাহ না করে তবে প্রোগ্রামটি সংকলন করে না।

বিকল্পভাবে, প্রাথমিক মানগুলি অ্যারে ইনিশিয়ালাইজারে স্পষ্টভাবে নির্দিষ্ট করা যেতে পারে , কোঁকড়া বন্ধনী দ্বারা বেষ্টিত একটি কমা-বিচ্ছিন্ন তালিকা:

    int primes[8] = {2, 3, 5, 7, 11, 13, 17, 19};

যেহেতু এই ক্ষেত্রে অ্যারে ইনিশিয়ালাইজারের উপাদানগুলির সংখ্যা অ্যারের আকারের সমান, ম্যানুয়ালি মাপটি নিরর্থক বলে উল্লেখ করে। এটি স্বয়ংক্রিয়ভাবে সংকলক দ্বারা বিয়োগ করা যেতে পারে:

    int primes[] = {2, 3, 5, 7, 11, 13, 17, 19};   // size 8 is deduced

আকার নির্দিষ্ট করতে এবং একটি সংক্ষিপ্ত অ্যারে প্রারম্ভকালীন সরবরাহ করাও সম্ভব:

    int fibonacci[50] = {0, 1, 1};   // 47 trailing zeros are deduced

সেক্ষেত্রে বাকী উপাদানগুলি শূন্য-সূচনাযুক্ত । নোট করুন যে সি ++ একটি খালি অ্যারে ইনিশিয়ালাইজারকে অনুমতি দেয় (সমস্ত উপাদান শূন্য-ইনিশিয়ালাইজড), যেখানে সি 98 নেই (কমপক্ষে একটি মান প্রয়োজন)। এছাড়াও মনে রাখবেন অ্যারের initializers শুধুমাত্র ব্যবহার করা যেতে পারে আরম্ভ অ্যারে; সেগুলি পরে অ্যাসাইনমেন্টে ব্যবহার করা যাবে না।

স্ট্যাটিক অ্যারে

স্ট্যাটিক অ্যারে (ডেটা বিভাগে "অ্যারে লিভিং") হ'ল স্থানীয় স্থান অ্যারে ভেরিয়েবল staticএবং নেমস্পেস স্কোপ ("গ্লোবাল ভেরিয়েবল") তে মূল শব্দ এবং অ্যারে ভেরিয়েবলের সাহায্যে সংজ্ঞা দেওয়া হয় :

int global_static_array[8];

void foo()
{
    static int local_static_array[8];
}

(নোটস্পেসের স্কোপে ভেরিয়েবলগুলি সুস্পষ্টভাবে স্থিতিশীল Note নোট করুন staticdefinition মূল সংজ্ঞাটি কীওয়ার্ড যুক্ত করার সাথে সম্পূর্ণ আলাদা, অবহিত অর্থ রয়েছে ))

স্থিতিস্থ অ্যারেগুলি স্বয়ংক্রিয় অ্যারে থেকে আলাদাভাবে আচরণ করে তা এখানে:

  • অ্যারে ইনিশিয়ালাইজার ছাড়াই স্থির অ্যারেগুলি আরও সম্ভাব্য আরম্ভের আগে শূন্য-আরম্ভ হয়।
  • স্ট্যাটিক পিওডি অ্যারেগুলি ঠিক একবার শুরু করা হয় এবং প্রাথমিক মানগুলি সাধারণত সম্পাদনযোগ্য হিসাবে বেক করা হয়, সেই ক্ষেত্রে রানটাইমটিতে কোনও আরম্ভের ব্যয় নেই। এটি সর্বদা সর্বাধিক স্থান-দক্ষ সমাধান নয় তবে এটি স্ট্যান্ডার্ডের দ্বারা প্রয়োজন হয় না।
  • স্থিতিশীল নন-পিওডি অ্যারেগুলি প্রথমবার শুরু হয় যখন নিয়ন্ত্রণের প্রবাহটি তাদের সংজ্ঞা দিয়ে যায়। স্থানীয় স্ট্যাটিক অ্যারেগুলির ক্ষেত্রে, যদি ফাংশনটি কখনও না বলা হয় তবে এটি কখনও ঘটতে পারে না।

(উপরের কোনওটি অ্যারেতে নির্দিষ্ট নয় These এই বিধিগুলি অন্যান্য ধরণের স্থির বস্তুর ক্ষেত্রেও সমানভাবে কার্যকর হয়))

অ্যারে ডেটা সদস্য

অ্যারে ডেটা সদস্য তৈরি করা হয় যখন তাদের নিজস্ব বস্তু তৈরি হয়। দুর্ভাগ্যক্রমে, সি ++ 03 সদস্য আরম্ভকারী তালিকায় অ্যারেগুলিকে আরম্ভ করার কোনও উপায় সরবরাহ করে না , সুতরাং আরম্ভকরণ অবশ্যই অ্যাসাইনমেন্ট সহ নকল করা উচিত:

class Foo
{
    int primes[8];

public:

    Foo()
    {
        primes[0] = 2;
        primes[1] = 3;
        primes[2] = 5;
        // ...
    }
};

বিকল্পভাবে, আপনি কনস্ট্রাক্টর বডিতে একটি স্বয়ংক্রিয় অ্যারে সংজ্ঞায়িত করতে পারেন এবং উপাদানগুলি অনুলিপি করতে পারেন:

class Foo
{
    int primes[8];

public:

    Foo()
    {
        int local_array[] = {2, 3, 5, 7, 11, 13, 17, 19};
        std::copy(local_array + 0, local_array + 8, primes + 0);
    }
};

সি ++ 0x সালে অ্যারে করতে সূচনাকারী তালিকা ধন্যবাদ সদস্য সক্রিয়া করা অভিন্ন আরম্ভের :

class Foo
{
    int primes[8];

public:

    Foo() : primes { 2, 3, 5, 7, 11, 13, 17, 19 }
    {
    }
};

এটি এমন একমাত্র সমাধান যা উপাদান ধরণের সাথে কাজ করে যার কোনও ডিফল্ট নির্মাণকারী নেই।

গতিশীল অ্যারে

গতিশীল অ্যারেগুলির কোনও নাম নেই, সুতরাং পয়েন্টারগুলির মাধ্যমে এগুলি অ্যাক্সেসের একমাত্র উপায়। তাদের কোনও নাম না থাকায় আমি তাদের এখন থেকে "বেনামে অ্যারে" হিসাবে উল্লেখ করব।

সি তে, বেনামে অ্যারে mallocএবং বন্ধুদের মাধ্যমে তৈরি করা হয় । সি ++ এ, বেনামে অ্যারেগুলি new T[size]সিনট্যাক্স ব্যবহার করে তৈরি করা হয় যা কোনও বেনামে অ্যারের প্রথম উপাদানকে একটি পয়েন্টার দেয়:

std::size_t size = compute_size_at_runtime();
int* p = new int[size];

নিম্নলিখিতটি ASCII আর্টটি মেমরির বিন্যাসকে চিত্রিত করে যদি রানটাইমের সময় আকারটি 8 হিসাবে গণনা করা হয়:

             +---+---+---+---+---+---+---+---+
(anonymous)  |   |   |   |   |   |   |   |   |
             +---+---+---+---+---+---+---+---+
               ^
               |
               |
             +-|-+
          p: | | |                               int*
             +---+

স্পষ্টতই, অতিরিক্ত পয়েন্টারের কারণে বেনামে অ্যারে নামের অ্যারেগুলির চেয়ে বেশি মেমরির প্রয়োজন যা পৃথকভাবে সংরক্ষণ করা উচিত। (ফ্রি স্টোরে আরও কিছু অতিরিক্ত ওভারহেড রয়েছে))

মনে রাখবেন যে আছে কোনও অ্যারে-টু-পয়েন্টার ক্ষয় চলছে। যদিও মূল্যায়নের new int[size]নেই আসলে একটি তৈরি অ্যারের পূর্ণসংখ্যার মত প্রকাশের ফলাফলের new int[size]হয় ইতিমধ্যে (প্রথম উপাদান) একটা একক পূর্ণসংখ্যা একটি পয়েন্টার, না পূর্ণসংখ্যার একটি অ্যারের বা অজানা আকারের পূর্ণসংখ্যার একটি অ্যারের একটি পয়েন্টার। এটি অসম্ভব, কারণ স্ট্যাটিক টাইপ সিস্টেমে অ্যারে মাপগুলি সংকলন-সময় ধ্রুবক হতে প্রয়োজন। (অতএব, আমি ছবিতে স্থির ধরনের তথ্য সহ বেনামে অ্যারে টিকিয়েছি না))

উপাদানগুলির জন্য ডিফল্ট মানগুলি সম্পর্কিত, বেনামে অ্যারেগুলি স্বয়ংক্রিয় অ্যারেগুলির অনুরূপ আচরণ করে। সাধারণত, বেনামে পিওড অ্যারেগুলি আরম্ভ করা হয় না, তবে একটি বিশেষ বাক্য গঠন রয়েছে যা মান-সূচনাটি ট্রিগার করে:

int* p = new int[some_computed_size]();

(সেমিকোলনের ঠিক আগে পেরেসেসিসের পেছনের জোড়টি নোট করুন)) আবার, সি ++ 0x নিয়মগুলি সহজতর করে এবং অভিন্ন সূচনা করার জন্য বেনামে অ্যারেগুলির জন্য প্রাথমিক মানগুলি নির্দিষ্ট করার অনুমতি দেয়:

int* p = new int[8] { 2, 3, 5, 7, 11, 13, 17, 19 };

আপনি যদি একটি বেনামে অ্যারে ব্যবহার করে সম্পন্ন করেন তবে আপনাকে এটি সিস্টেমে আবার ছেড়ে দিতে হবে:

delete[] p;

আপনাকে অবশ্যই প্রতিটি বেনামে অ্যারে ঠিক একবার প্রকাশ করতে হবে এবং তারপরে আর কখনও স্পর্শ করবে না। এটিকে কোনও মেমরি ফাঁস (বা আরও সাধারণভাবে উপাদান উপাদান, একটি উত্স ফাঁসের উপর নির্ভর করে) এর ফলাফলগুলিতে প্রকাশ না করা এবং একাধিকবার এটি প্রকাশের চেষ্টা করার ফলে অপরিবর্তিত আচরণের ফলাফল হয়। অ্যারে ছাড়ার পরিবর্তে অ-অ্যারে ফর্ম delete(বা free) ব্যবহার delete[]করাও অপরিজ্ঞাত আচরণ


2
এর থামিয়ে দেওয়া staticনামস্থান সুযোগ ব্যবহার সি ++ 11 সরানো হয়েছে।
কিংবদন্তি 2 কে

যেহেতু newআমি অপারেটর, এটি অবশ্যই রেফারেন্স দ্বারা অনুমোদিত বরাদ্দগুলি ফিরিয়ে দিতে পারে। এটির কোনও অর্থ নেই ...
অনুরাগী

@ ডেডুপ্লিকেটর এটি না, কারণ historতিহাসিকভাবে, newউল্লেখগুলির তুলনায় অনেক পুরানো।
ফ্রেডওভারফ্লো

@ ফ্রেড ওভারফ্লো: সুতরাং এটি একটি কারণ উল্লেখ করতে পারে না এমন কারণ রয়েছে, এটি লিখিত ব্যাখ্যা থেকে একেবারেই আলাদা different
প্রতিলিপি

2
@ উত্সাহীকারক আমি মনে করি না যে অজানা সীমাগুলির একটি অ্যারের রেফারেন্স উপস্থিত রয়েছে। কমপক্ষে জি ++ সংকলন করতে অস্বীকার করেছেনint a[10]; int (&r)[] = a;
ফ্রেডওভারফ্লো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.