tutorial - the c++ programming language




আমি C++ এ অ্যারে কিভাবে ব্যবহার করব? (4)

5. অ্যারে ব্যবহার করার সময় সাধারণ ক্ষতি।

5.1 Pitfall: নির্ভরযোগ্য টাইপ-অনিরাপদ লিঙ্কিং।

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

// [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};

উইন্ডোজ 7 এ এটি MINIW G ++ 4.4.1 এবং ভিজ্যুয়াল সি ++ 10.0 উভয়ই সংশোধন করে এবং লিঙ্কগুলিকে সূক্ষ্ম করে।

যেহেতু ধরনগুলি মিলছে না, প্রোগ্রামটি ক্র্যাশ করলে ক্র্যাশ হয়।

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

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

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

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

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

... একটি অ্যারের বস্তুর ঘোষণাগুলি অ্যারের প্রকার নির্দিষ্ট করে যা একটি প্রধান অ্যারের আবদ্ধ বা অনুপস্থিতির দ্বারা পৃথক থাকে (8.3.4)।

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

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

এখনও লিখিত না

5.3 Pitfall: C Idiom ব্যবহার করে উপাদান সংখ্যা পেতে।

গভীর সি অভিজ্ঞতা দিয়ে লিখতে স্বাভাবিক ...

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

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

মুখ্য pitfall: সি idiom টাইপসাফ হয় না। উদাহরণস্বরূপ, কোড ...

#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[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 টি উপাদান, কলিং ডিসপ্লে ...
অ্যাসেসন ব্যর্থ হয়েছে: ("N_ITEMS কে যুক্তি হিসাবে একটি প্রকৃত অ্যারের প্রয়োজন", typeid (a)! = Typeid (& * a)), ফাইল runtime_detect ion.cpp, line 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 )

G ++ এর সাথে প্রথম সম্পূর্ণ প্রোগ্রামে প্রতিস্থাপিত এই সংজ্ঞাটি কম্পাইল করা, আমি পেয়েছিলাম ...

M: \ count> g ++ compile_time_detection.cpp
compile_time_detection.cpp: ফাংশন 'অকার্যকর প্রদর্শন (কনস int *)':
compile_time_detection.cpp: 14: ত্রুটি: 'n_items' (const int int * &) এ কল করার জন্য কোন মিলযুক্ত ফাংশন নেই

M: \ count> _

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

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

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

C ++ 11 এবং পরে এটি প্রাকৃতিক, কিন্তু আপনি বিপজ্জনক দেখতে পাবেন, C ++ 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, যা এই ফাংশনটিকে কম্পাইল সময় ধ্রুবক উত্পাদন করতে দেয় ।

উদাহরণস্বরূপ, সি ++ 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 );
}

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

সি ++ 11 সি ++ 14 $ 5,19 / 2 নয়টি তম ড্যাশ

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

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

এক সবসময় আরো verbose লিখতে পারেন

// 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-invalid-due-to-reference সমস্যাটির এই সমাধানটির অংশটি কম্পাইল সময়কে স্পষ্টভাবে সুনির্দিষ্ট করে তুলতে হয়।

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

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

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

এই প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী পাঁচ ভাগে বিভক্ত করা হয়:

  1. টাইপ স্তরের অ্যারে এবং উপাদান অ্যাক্সেস
  2. অ্যারে সৃষ্টি এবং প্রাথমিককরণ
  3. বরাদ্দ এবং পরামিতি পাস
  4. বহুমুখী অ্যারে এবং পয়েন্টার অ্যারে
  5. অ্যারে ব্যবহার করার সময় সাধারণ ক্ষতি

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

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

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


অ্যারে সৃষ্টি এবং প্রাথমিককরণ

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

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

স্বয়ংক্রিয় অ্যারে (স্ট্যাকে "জীবন্ত অ্যারে" থাকা) প্রতিটি সময় নিয়ন্ত্রণের প্রবাহ একটি অ স্ট্যাটিক স্থানীয় অ্যারে পরিবর্তনশীলের সংজ্ঞা দিয়ে পাস করে:

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

সূচনা ascending আদেশ সঞ্চালিত হয়। উল্লেখ্য প্রাথমিক মানের উপাদানটি T তে নির্ভর করে:

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

অন্যথায়, প্রাথমিক মানগুলি অ্যারে প্রাথমিক সূত্রের মধ্যে স্পষ্টভাবে উল্লেখ করা যেতে পারে, কোঁকড়া বন্ধনী দ্বারা ঘিরে একটি কমা দ্বারা পৃথক তালিকা:

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

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

    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

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

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

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

int global_static_array[8];

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

(মনে রাখবেন যে নামস্পেস স্কোপের ভেরিয়েবলগুলি স্থিরভাবে স্ট্যাটিক। তাদের সংজ্ঞাতে static কীওয়ার্ড যোগ করা একটি সম্পূর্ণ ভিন্ন, অব্যবহৃত অর্থ রয়েছে ।)

স্ট্যাটিক অ্যারে স্বয়ংক্রিয় অ্যারে থেকে পৃথকভাবে আচরণ করে কিভাবে এখানে:

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

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

Array তথ্য সদস্য

তাদের নিজস্ব বস্তু তৈরি করা হয় যখন অ্যারে তথ্য সদস্য তৈরি করা হয়। দুর্ভাগ্যবশত, সি ++ 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);
    }
};

C ++ 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] অভিব্যক্তিটির ফলাফল ইতিমধ্যে একটি পূর্ণসংখ্যা (প্রথম উপাদান) এর একটি পয়েন্টার, পূর্ণসংখ্যা বা পয়েন্টারের অ্যারে নয়। অজানা আকার পূর্ণসংখ্যা অ্যারে। এটি অসম্ভব, কারণ স্ট্যাটিক টাইপ সিস্টেমের জন্য অ্যারের মাপগুলিকে কম্পাইল-সময় সংহত করার প্রয়োজন হয়। (তাই, আমি ছবিতে স্ট্যাটিক টাইপ তথ্য সহ বেনামী অ্যারেকে টীকা দিয়েছিলাম না।)

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

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

(সেমিকোলনটির ঠিক পূর্ববর্তী বন্ধনীটির পূর্ববর্তী জোড়াটি লক্ষ্য করুন।) আবার, C ++ 0x নিয়মগুলি সরল করে এবং বেনামী অ্যারেগুলির জন্য প্রাথমিক মানগুলি নির্দিষ্ট করার অনুমতি দেয়, ইউনিফর্ম প্রারম্ভিকতার জন্য ধন্যবাদ:

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

যদি আপনি একটি বেনামী অ্যারে ব্যবহার করে সম্পন্ন করেন, তবে আপনাকে সিস্টেমটিতে এটি পুনরায় প্রকাশ করতে হবে:

delete[] p;

আপনাকে একেবারে একবার প্রতিটি বেনামী অ্যারে মুক্ত করতে হবে এবং তারপরে পরে এটি স্পর্শ করবেন না। একটি মেমরি লিক (অথবা সাধারণভাবে, উপাদান প্রকারের উপর নির্ভর করে, একটি রিসোর্স লিক) এ সমস্ত ফলাফলে এটি প্রকাশ করা হয় না এবং অনির্দিষ্ট আচরণে এটি একাধিক বার ফলাফল প্রকাশ করার চেষ্টা করছে। অ্যারে free পরিবর্তে অ্যারে ফর্মটি delete পরিবর্তে (বা free ) delete[] অনির্ধারিত আচরণও


নিয়োগ

কোন বিশেষ কারণে, অ্যারে একে অপরের জন্য বরাদ্দ করা যাবে না। পরিবর্তে 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 ভাল সঞ্চালন করে। সন্দেহ থাকলে, পরিমাপ।

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

পরামিতি ক্ষণস্থায়ী

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

পয়েন্টার দ্বারা পাস

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

#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);
}

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

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

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);
}

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

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

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


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

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

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

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

নামযুক্ত বহুমাত্রিক অ্যারে ব্যবহার করার সময়, সমস্ত মাত্রা কম্পাইল সময় সময়ে জানা আবশ্যক:

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: |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+
              |   |   |   |   |   |   |   |
              +---+---+---+---+---+---+---+

উল্লেখ্য যে উপরে যেমন 2 ডি গ্রিড নিছক সহায়ক ভিজ্যুয়ালাইজেশন। C ++ এর দৃষ্টিকোণ থেকে মেমরি বাইটগুলির একটি "সমতল" ক্রম। একটি বহুমাত্রিক অ্যারের উপাদান সারি-প্রধান ক্রম সংরক্ষিত হয়। অর্থাৎ, 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 পয়েন্টারগুলির একটি অ্যারের প্রথম উপাদানটির একটি পয়েন্টার হতে পারে (প্রতিটিটি মূল 2 ডি অ্যারের লাইনের প্রথম উপাদানটিতে নির্দেশ করে), তবে পয়েন্টার অ্যারের অস্তিত্ব নেই মেমরি এখনো যে কোন জায়গায়। আপনি যদি এমন রূপান্তর চান তবে আপনাকে অবশ্যই প্রয়োজনীয় পয়েন্টার অ্যারের তৈরি এবং পূরণ করতে হবে:

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;






c++-faq