এই ধাঁধা তিনটি টুকরা আছে।
প্রথম টুকরাটি হ'ল সি এবং সি ++ এর শ্বেত স্পেসটি সংলগ্ন টোকেনগুলি পৃথক করার বাইরে সাধারণত তাত্পর্যপূর্ণ নয় যা অন্যথায় অবিচ্ছেদ্য।
প্রিপ্রোসেসিং স্টেজের সময়, উত্স পাঠ্যটি টোকেনের একটি ক্রম - শনাক্তকারী, বিরামচিহ্নগুলি, সংখ্যাসূচক, স্ট্রিং লিটারাল ইত্যাদিতে বিভক্ত হয় to টোকেনের এই অনুক্রমটি পরে বাক্য গঠন এবং অর্থের জন্য বিশ্লেষণ করা হয়। টোকনাইজারটি "লোভী" এবং এটি সবচেয়ে দীর্ঘতম বৈধ টোকেন তৈরি করবে that's এমন কিছু লিখলে
inttest;
টোকেনাইজারটি কেবলমাত্র দুটি টোকেন দেখেছে - শনাক্তকারী inttest
পরে সনাক্তকারী ;
। এটি int
এই পর্যায়ে পৃথক কীওয়ার্ড হিসাবে স্বীকৃতি দেয় না (এটি প্রক্রিয়াটি পরে ঘটে)। সুতরাং, নামের একটি পূর্ণসংখ্যার ঘোষণার হিসাবে লাইনটি পড়ার test
জন্য, শনাক্তকারী টোকেনগুলি পৃথক করতে আমাদের হোয়াইটস্পেস ব্যবহার করতে হবে:
int test;
*
চরিত্র কোন শনাক্তকারীর অংশ নয়; এটি নিজস্বভাবে একটি পৃথক টোকেন (বিরামচিহ্ন)। সুতরাং আপনি যদি লিখুন
int*test;
কম্পাইলার 4 পৃথক টোকেন উদ্ধার - int
, *
, test
, এবং ;
। সুতরাং, পয়েন্টার ঘোষণাপত্রগুলিতে সাদা স্থান স্পষ্টতই গুরুত্বপূর্ণ নয় এবং সবগুলিই
int *test;
int* test;
int*test;
int * test;
একইভাবে ব্যাখ্যা করা হয়।
ধাঁধার দ্বিতীয় টুকরোটি হ'ল ঘোষণা কীভাবে বাস্তবে সি এবং সি ++ 1 তে কাজ করে । ঘোষণাগুলি দুটি প্রধান টুকরো টুকরো টুকরো হয়ে গেছে - ঘোষণা স্পেসিফায়ারগুলির একটি ক্রম (স্টোরেজ ক্লাস স্পেসিফায়ার, টাইপ স্পেসিফায়ার, টাইপ কোয়ালিফায়ার ইত্যাদি) এর পরে (সম্ভবত আরম্ভীকৃত) ঘোষণাকারীদের কমা-বিচ্ছিন্ন তালিকা তৈরি করা হবে । প্রজ্ঞাপনে
unsigned long int a[10]={0}, *p=NULL, f(void);
ঘোষণা নির্দিষ্টকরী হয় unsigned long int
এবং declarators হয় a[10]={0}
, *p=NULL
এবং f(void)
। Declarator প্রবর্তন জিনিস নাম ঘোষিত হওয়ার ( a
, p
, এবং f
) যে জিনিস এর অ্যারে-Ness, পয়েন্টার-অন্তরীপ, এবং ফাংশন-অন্তরীপ সম্পর্কে তথ্য সহ। একটি ঘোষণাকারীর একটি সম্পর্কিত সূচক থাকতে পারে।
প্রকারটি a
হ'ল "10-উপাদানের অ্যারে unsigned long int
"। এই ধরণেরটি সম্পূর্ণরূপে ঘোষণাপত্রের স্পেসিফায়ার এবং ডিক্লেয়ারের সংমিশ্রণ দ্বারা নির্দিষ্ট করা হয় এবং প্রাথমিক মানটি আরম্ভকারী সহ নির্দিষ্ট করা হয় ={0}
। একইভাবে, প্রকারটি p
"পয়েন্টার টু unsigned long int
", এবং আবার সেই ধরণেরটি ঘোষণাপত্রের স্পেসিফায়ার এবং ডিক্লেয়ারের সংমিশ্রণ দ্বারা নির্দিষ্ট করা হয় এবং এতে আরম্ভ হয় NULL
। এবং প্রকারভেদ একই যুক্তি দ্বারা f
"ফাংশন রিটার্নিং unsigned long int
"।
এটি কী - কোনও "ফাংশন-রিটার্নিং" টাইপ স্পেসিফার নেই ঠিক তেমন কোনও "পয়েন্টার-টু" টাইপ স্পেসিফায়ার নেই , ঠিক তেমন কোনও "অ্যারে-অফ" টাইপ স্পেসিফায়ার নেই। আমরা আরে হিসাবে ঘোষণা করতে পারি না
int[10] a;
কারণ []
অপারেটরের অপারেন্ড a
, না int
। একইভাবে ঘোষণাপত্রে
int* p;
অপারেন্ড *
হয় p
, না int
। তবে ইন্ডিরিশন অপারেটর অবিচ্ছিন্ন এবং শ্বেত স্থানটি গুরুত্বপূর্ণ নয়, সংক্ষেপক যদি আমরা এইভাবে এটি লিখি তবে অভিযোগ করবে না। তবে এটি সর্বদা হিসাবে ব্যাখ্যা করা হয় int (*p);
।
সুতরাং, আপনি যদি লিখুন
int* p, q;
অপরেন্ড *
হয় p
, তাই এটি হিসাবে ব্যাখ্যা করা হবে
int (*p), q;
সুতরাং, সব
int *test1, test2;
int* test1, test2;
int * test1, test2;
একই জিনিসটি করুন - তিনটি ক্ষেত্রেই এটির test1
অপারেন্ড *
এবং সুতরাং "পয়েন্টার টু int
" test2
টাইপ রয়েছে , যখন টাইপ রয়েছে int
।
ঘোষকগণ যথেচ্ছ জটিল হয়ে উঠতে পারেন। আপনার পয়েন্টারের অ্যারে থাকতে পারে:
T *a[N];
অ্যারেগুলিতে আপনার পয়েন্টার থাকতে পারে:
T (*a)[N];
আপনি পয়েন্টার রিটার্ন ফাংশন থাকতে পারে:
T *f(void);
ফাংশনগুলিতে আপনার পয়েন্টার থাকতে পারে:
T (*f)(void)
ফাংশনে আপনার পয়েন্টারের অ্যারে থাকতে পারে:
T (*a[N])(void)
অ্যারেগুলিতে পয়েন্টার ফেরত দেওয়ার জন্য আপনার ফাংশন থাকতে পারে:
T (*f(void))[N];
পয়েন্টারগুলিতে ফাংশনগুলিতে পয়েন্টারগুলির অ্যারেগুলিতে রিটার্নিং ফাংশন থাকতে পারে T
:
T *(*(*f(void))[N])(void)
এবং তারপরে আপনার কাছে রয়েছে signal
:
void (int)))(int);
যা পড়ছে
signal -- signal
signal( ) -- is a function taking
signal( ) -- unnamed parameter
signal(int ) -- is an int
signal(int, ) -- unnamed parameter
signal(int, (*) ) -- is a pointer to
signal(int, (*)( )) -- a function taking
signal(int, (*)( )) -- unnamed parameter
signal(int, (*)(int)) -- is an int
signal(int, void (*)(int)) -- returning void
(*signal(int, void (*)(int))) -- returning a pointer to
(*signal(int, void (*)(int)))( ) -- a function taking
(*signal(int, void (*)(int)))( ) -- unnamed parameter
(*signal(int, void (*)(int)))(int) -- is an int
void (*signal(int, void (*)(int)))(int)
এবং এটি সবেমাত্র যা সম্ভব তার পৃষ্ঠকে স্ক্র্যাচ করে। তবে লক্ষ্য করুন যে অ্যারে-নেস, পয়েন্টার-নেস এবং ফাংশন-নেস সর্বদা ঘোষকের অংশ, না টাইপ স্পেসিফায়ার।
নজর রাখার জন্য একটি জিনিস - const
পয়েন্টার টাইপ এবং পয়েন্ট-টু টাইপ উভয়কে সংশোধন করতে পারে:
const int *p;
int const *p;
উপরের উভয়ই p
একটি const int
বস্তুর পয়েন্টার হিসাবে ঘোষণা করে । আপনি p
এটির জন্য একটি নতুন মান লিখতে পারেন এটি কোনও ভিন্ন অবজেক্টের দিকে নির্দেশ করতে:
const int x = 1;
const int y = 2;
const int *p = &x;
p = &y;
তবে আপনি পয়েন্ট-টু অবজেক্টে লিখতে পারবেন না:
*p = 3;
যাহোক,
int * const p;
অ-কনস্ট্যান্টকে পয়েন্টার p
হিসাবে ঘোষণা করে ; আপনি জিনিসটি নির্দেশ করতে পারেনconst
int
p
int x = 1;
int y = 2;
int * const p = &x;
*p = 3;
তবে আপনি p
কোনও ভিন্ন অবজেক্টের দিকে নির্দেশ করতে পারবেন না :
p = &y
যা আমাদের ধাঁধার তৃতীয় অংশে নিয়ে আসে - কেন ঘোষণাগুলি এইভাবে গঠন করা হয়।
উদ্দেশ্য হ'ল একটি ঘোষণার কাঠামোর কোডে একটি অভিব্যক্তির কাঠামোটি খুব কাছাকাছিভাবে আয়না করা উচিত ("ডিক্লেয়ারেশন মিমিক্স ব্যবহার")। উদাহরণস্বরূপ, ধরুন আমরা int
নামকরণের জন্য পয়েন্টারগুলির একটি অ্যারে রেখেছি ap
এবং আমরা 'তম উপাদানটির int
দ্বারা নির্দেশিত মানটি অ্যাক্সেস করতে চাই i
। আমরা নিম্নরূপে সেই মানটি অ্যাক্সেস করব:
printf( "%d", *ap[i] );
অভিব্যক্তি *ap[i]
টাইপ হয়েছে int
; সুতরাং, ঘোষণা ap
হিসাবে লেখা হয়
int *ap[N];
ঘোষণাকারীর *ap[N]
মত প্রকাশের মতো কাঠামো থাকে *ap[i]
। অপারেটার *
এবং []
ঘোষণা করে যে তারা একটি অভিব্যক্তি না একই এইরকম আচরণ - []
ইউনারী বেশী প্রাধান্য রয়েছে *
, তাই এর প্রতীক *
হল ap[N]
(এটা যেমন পার্স এর *(ap[N])
)।
অন্য একটি উদাহরণ হিসাবে, ধরুন আমাদের int
নামের একটি অ্যারের কাছে একটি পয়েন্টার রয়েছে pa
এবং আমরা i
'তম উপাদানটির মানটি অ্যাক্সেস করতে চাই । আমরা যে হিসাবে লিখতে চাই
printf( "%d", (*pa)[i] )
মত প্রকাশের টাইপ (*pa)[i]
হয় int
, তাই ঘোষণা হিসেবে লেখা হয়
int (*pa)[N];
আবার, প্রাধান্য এবং সাহসিকতার একই নিয়ম প্রযোজ্য। এই ক্ষেত্রে, আমরা এর i
'তম উপাদানটির অবলম্বন করতে চাই না pa
, আমরা i
কীসের দিকে pa
ইঙ্গিত করব তার ' তম উপাদানটি অ্যাক্সেস করতে চাই , সুতরাং আমাদের *
অপারেটরটিকে স্পষ্টভাবে গ্রুপিং করতে হবে pa
।
*
, []
এবং ()
অপারেটরদের সমস্ত অংশ অভিব্যক্তি কোডে, তাই তারা সমস্ত অংশ declarator ঘোষণায়। ঘোষণাকারী আপনাকে কীভাবে একটি অভিব্যক্তিতে অবজেক্টটি ব্যবহার করতে হয় তা বলে। যদি আপনার মতো কোনও ঘোষণা থাকে int *p;
, তবে এটি আপনাকে বলে যে *p
আপনার কোডে প্রকাশটি একটি int
মান অর্জন করবে। এক্সটেনশন দ্বারা, এটা আপনাকে বলে যে অভিব্যক্তি p
"থেকে পয়েন্টার ধরনের একটি মান উৎপাদ int
", বা int *
।
সুতরাং, castালাই এবং sizeof
এক্সপ্রেশনগুলির মতো জিনিসগুলির বিষয়ে কী , যেখানে আমরা এই জাতীয় জিনিস (int *)
বা এর মতো জিনিস ব্যবহার sizeof (int [10])
করি? আমি কীভাবে এমন কিছু পড়ি
void foo( int *, int (*)[10] );
কোনও ঘোষক নেই, *
এবং []
অপারেটররা কি সরাসরি টাইপটি পরিবর্তন করছে?
ঠিক আছে, না - খালি শনাক্তকারী (একটি বিমূর্ত ঘোষণাকারী হিসাবে পরিচিত ) সহ এখনও একটি ঘোষক রয়েছে । আমরা যদি প্রতীক λ সহ একটি খালি আইডেন্টিফায়ার প্রতিনিধিত্ব, তাহলে আমরা সেগুলো পড়তে পারেন যেমন (int *λ)
, sizeof (int λ[10])
এবং
void foo( int *λ, int (*λ)[10] );
এবং তারা অন্য কোনও ঘোষণার মতোই আচরণ করে। int *[10]
10 টি পয়েন্টারের অ্যারে উপস্থাপন করে, যখন int (*)[10]
একটি অ্যারেতে একটি পয়েন্টার উপস্থাপন করে।
এবং এখন এই উত্তর মতামত অংশ। আমি সাধারণ পয়েন্টার হিসাবে ঘোষণা করার সি ++ কনভেনশন পছন্দ করি না
T* p;
এবং নিম্নলিখিত কারণে এটি খারাপ অভ্যাস বিবেচনা করুন :
- এটি সিনট্যাক্সের সাথে সামঞ্জস্যপূর্ণ নয়;
- এটি বিভ্রান্তির পরিচয় দেয় (এই প্রশ্নের প্রমাণ হিসাবে, এই প্রশ্নের সমস্ত সদৃশ, এর অর্থ সম্পর্কে প্রশ্নগুলি, সেই সমস্ত প্রশ্নের
T* p, q;
নকল ইত্যাদি);
- এটি অভ্যন্তরীণভাবে সামঞ্জস্যপূর্ণ নয় - পয়েন্টারগুলির একটি অ্যারে হিসাবে
T* a[N]
ব্যবহারের সাথে অসম্পূর্ণ হিসাবে ঘোষণা করা (আপনি লেখার অভ্যাস না থাকলে * a[i]
);
- এটি পয়েন্টার-থেকে-অ্যারে বা পয়েন্টার-টু-ফাংশন প্রকারগুলিতে প্রয়োগ করা যাবে না (আপনি যদি টাইপডেফ তৈরি না করেন তবে আপনি
T* p
কনভেনশনটি পরিষ্কারভাবে প্রয়োগ করতে পারেন , যা ... না );
- এটি করার কারণ - "এটি বস্তুর পয়েন্টার-নেসে জোর দেয়" - উত্সাহিত। এটি অ্যারে বা ফাংশন ধরণের ক্ষেত্রে প্রয়োগ করা যায় না এবং আমি মনে করি যে এই গুণগুলি জোর দেওয়া ঠিক ততটাই গুরুত্বপূর্ণ।
শেষ পর্যন্ত, এটি কেবল দুটি ভাষার টাইপ সিস্টেমগুলি কীভাবে কাজ করে সে সম্পর্কে বিভ্রান্ত চিন্তার ইঙ্গিত দেয়।
আইটেমগুলি আলাদাভাবে ঘোষণার উপযুক্ত কারণ রয়েছে; একটি খারাপ অনুশীলন ( T* p, q;
) এর মধ্যে কাজ করা তাদের মধ্যে একটি নয়। আপনি যদি আপনার ডিক্লেটারকে সঠিকভাবে লিখেন ( T *p, q;
) তবে আপনার বিভ্রান্তির সম্ভাবনা কম।
আমি আপনার সমস্ত সরল for
লুপগুলি ইচ্ছাকৃতভাবে লেখার অনুরূপ হিসাবে বিবেচনা করি
i = 0;
for( ; i < N; )
{
...
i++
}
কৃত্রিমভাবে বৈধ, তবে বিভ্রান্তিকর, এবং উদ্দেশ্যটি ভুল ব্যাখ্যা করা হতে পারে। যাইহোক, T* p;
কনভেনশনটি সি ++ সম্প্রদায়ে আবদ্ধ এবং আমি এটি নিজের সি ++ কোডে ব্যবহার করি কারণ কোড বেস জুড়ে ধারাবাহিকতা একটি ভাল জিনিস, তবে আমি যতবার করি ততবারে এটি আমাকে চুলকায়।
- আমি সি পরিভাষা ব্যবহার করব - সি ++ পরিভাষাটি কিছুটা আলাদা তবে ধারণাগুলি মূলত একই রকম।