৫. অ্যারে ব্যবহার করার সময় সাধারণ সমস্যাগুলি।
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 উভয়ের সাথেই সূক্ষ্ম লিঙ্ক করে।
যেহেতু প্রকারগুলি মেলে না, সুতরাং আপনি যখন এটি চালাবেন তখন প্রোগ্রামটি ক্র্যাশ হয়ে যায়।
আনুষ্ঠানিক ব্যাখ্যা: প্রোগ্রামটির অপরিজ্ঞাত আচরণ (ইউবি) রয়েছে, এবং এটি ক্র্যাশ না করে কেবল ঝুলতে পারে, বা সম্ভবত কিছুই করতে পারে না, বা এটি মার্কিন যুক্তরাষ্ট্র, রাশিয়া, ভারতের রাষ্ট্রপতিদের জন্য হুমকিমূলক ইমেল প্রেরণ করতে পারে, চীন এবং সুইজারল্যান্ড এবং নাক ডিমোনকে আপনার নাক থেকে উড়ে ফেলুন।
অনুশীলন ব্যাখ্যা: main.cpp
অ্যারেতে পয়েন্টার হিসাবে বিবেচনা করা হয়, অ্যারের হিসাবে একই ঠিকানায় স্থাপন করা হয়। 32-বিট এক্সিকিউটেবলের জন্য এর অর্থ int
হ'ল অ্যারেতে প্রথম
মানটি পয়েন্টার হিসাবে বিবেচিত হয়। অর্থাত, এ পরিবর্তনশীল রয়েছে, বা প্রদর্শিত হয় ধারণ, । এর ফলে প্রোগ্রামটি ঠিকানার জায়গার একেবারে নীচে মেমোরি অ্যাক্সেস করতে পারে যা প্রচলিতভাবে সংরক্ষিত এবং ফাঁদে ফেলে। ফলাফল: আপনি একটি ক্রাশ পান।main.cpp
numbers
(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 উপাদান।
- কম্পাইলার নতুন করে লেখা হয়
int const a[7]
শুধু int const a[]
।
- কম্পাইলার নতুন করে লেখা হয়
int const a[]
থেকেint const* a
।
N_ITEMS
সুতরাং একটি পয়েন্টার সহ আহ্বান করা হয়।
- একটি 32-বিট এক্সিকিউটেবলের জন্য
sizeof(array)
(পয়েন্টারের আকার) এর জন্য 4 হয়।
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
।