সি তেমন শক্ত নয়: অকার্যকর (* (* f []) ()) ()


188

আমি আজ একটি ছবি দেখেছি এবং মনে করি আমি ব্যাখ্যাগুলি প্রশংসা করব। সুতরাং এখানে ছবি:

কিছু সি কোড

আমি এটি বিভ্রান্তিকর পেয়েছি এবং ভাবলাম যে এই জাতীয় কোডগুলি কখনও ব্যবহারিক হয় কিনা। আমি ছবিটি গুগল করেছিলাম এবং এই রেডডিট এন্ট্রিতে অন্য একটি ছবি পেয়েছি এবং এখানে সেই চিত্রটি রয়েছে:

কিছু আকর্ষণীয় ব্যাখ্যা

সুতরাং এই "স্পাইরিয়াল পড়া" বৈধ কিছু? সি সংকলকগুলি কী এভাবে পার্স হয়?
এই অদ্ভুত কোডটির জন্য যদি সহজ ব্যাখ্যা থাকে তবে এটি দুর্দান্ত হবে।
সর্বোপরি, এই জাতীয় কোডগুলি কি কার্যকর হতে পারে? যদি তা হয় তবে কোথায় এবং কখন?

নেই একটি প্রশ্ন "সর্পিল নিয়ম" সম্পর্কে, কিন্তু আমি ঠিক কিভাবে তা প্রয়োগ করা হচ্ছে বা কিভাবে অভিব্যক্তি নিয়ম সঙ্গে পড়া সম্পর্কে জিজ্ঞাসা করছি না। আমি এই জাতীয় অভিব্যক্তিগুলির ব্যবহার এবং সর্পিল নিয়মের বৈধতা সম্পর্কেও প্রশ্নবিদ্ধ করছি। এগুলি সম্পর্কে, ইতিমধ্যে কিছু সুন্দর উত্তর পোস্ট করা হয়েছে।


9
ব্যাখ্যাটি কীভাবে সহজ হতে পারে? এটি প্রতিটি মূল পয়েন্টের জন্য fw / কয়েকটি শব্দের সংজ্ঞাটির সমস্ত দিককে অন্তর্ভুক্ত করে।
স্কট হান্টার

30
হয়তো সি শক্ত? এটি আসলে fকোনও যুক্তি গ্রহণ করতে পারে এমন ফাংশনগুলিকে পয়েন্টারগুলির একটি অ্যারের হিসাবে ঘোষণা করে .. যদি এটি হয় void (*(*f[])(void))(void);তবে হ্যাঁ, এটি এমন ফাংশন যা কোনও যুক্তি না নিয়ে ...
txtechhelp

18
অনুশীলনে, এই ধরণের অস্পষ্ট কোড কোড করবেন না। স্বাক্ষরগুলির জন্য
টাইপিডেফ

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

20
আপনি যদি চোখ কুঁচকে যান তবে এটি এলআইএসপির মতো লাগে looks
ব্যবহারকারী 2023861

উত্তর:


117

একটি জটিল ঘোষণার অর্থ খুঁজে পেতে সহায়তা করার জন্য "ক্লকওয়াইজ / সর্পিল রুল" নামে একটি বিধি রয়েছে

থেকে সি-FAQ :

অনুসরণ করার জন্য তিনটি সহজ পদক্ষেপ রয়েছে:

  1. অজানা উপাদান দিয়ে শুরু করে, একটি সর্পিল / ঘড়ির কাঁটার দিকে এগিয়ে যান; নীচের উপাদানগুলি যখন উত্সাহিত করে তখন তাদের সাথে সম্পর্কিত ইংরেজি বিবৃতি দিয়ে প্রতিস্থাপন করুন:

    [X]বা []
    => অ্যারের এক্স আকার ... বা অ্যারের অপরিবর্তিত আকার ...

    (type1, type2)
    => ফাংশন পাসিং টাইপ 1 এবং টাইপ 2 ফিরে ...

    *
    => পয়েন্টার (গুলি) থেকে ...

  2. সমস্ত টোকেন coveredেকে না দেওয়া পর্যন্ত এটি সর্পিল / ঘড়ির কাঁটার দিকে চালিয়ে যান।

  3. সর্বদা প্রথম বন্ধনে প্রথম যে কোনও সমাধান করুন!

উদাহরণস্বরূপ আপনি উপরের লিঙ্কটি চেক করতে পারেন।

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

http://www.cdecl.org

আপনি একটি সি ঘোষণাপত্র প্রবেশ করতে পারেন এবং এটি এর ইংরেজি অর্থ দেবে। জন্য

void (*(*f[])())()

এটি ফলাফল:

রিটার্নিং পয়েন্টারকে ফাংশন রিটার্নিং শূন্য করতে ফাংশনটির পয়েন্টারের অ্যারে হিসাবে এফ ঘোষণা করুন

সম্পাদনা করুন:

র‌্যান্ডম 832- র মন্তব্যে যেমন উল্লেখ করা হয়েছে , সর্পিল নিয়ম অ্যারেগুলির অ্যারেটিকে সম্বোধন করে না এবং এই ঘোষণার (বেশিরভাগ ক্ষেত্রে) একটি ভুল ফলাফল হতে পারে। উদাহরণস্বরূপ int **x[1][2];সর্পিল নিয়মের []চেয়ে বেশি যে প্রাধান্য পেয়েছে তা উপেক্ষা করে *

অ্যারের অ্যারের সামনে থাকা অবস্থায় সর্পিল নিয়ম প্রয়োগের আগে প্রথমে স্পষ্টত প্রথম বন্ধনী যুক্ত করা যায়। উদাহরণস্বরূপ: অগ্রাধিকারের কারণে (বৈধ সিও) int **x[1][2];সমান int **(x[1][2]);এবং সর্পিল নিয়মের পরে সঠিকভাবে এটি পড়ছে "x পয়েন্টার থেকে পয়েন্টার টু ইন্টির 2 অ্যারে 1 এর অ্যারে 1" যা সঠিক ইংরেজি ঘোষণা।

লক্ষ্য করুন এই সমস্যা এই মধ্যে আবৃত করা হয়েছে উত্তর দ্বারা জেমস Kanze (সেই বিষয়টিই তুলে ধরেছিলেন haccks মন্তব্য)।


5
আমার ইচ্ছা cdecl.org আরও ভাল ছিল
গ্রেডি প্লেয়ার

8
কোনও "সর্পিল নিয়ম" নেই ... "ইনট *** ফু" [] [] [] "পয়েন্টারগুলিতে পয়েন্টারগুলিতে পয়েন্টারগুলিতে অ্যারের অ্যারেগুলির একটি অ্যারে সংজ্ঞায়িত করে। "সর্পিল" কেবলমাত্র এই সত্য থেকেই আসে যে এই ঘোষণাটি বন্ধনীগুলির মধ্যে গোষ্ঠীগুলির সাথে এমনভাবে ঘটেছিল যা তাদের বিকল্প হিসাবে নিয়েছিল। এটি প্রথম বন্ধনীর প্রতিটি সেটের মধ্যে ডানদিকে, বামে সবকিছু।
র্যান্ডম 832

1
@ র্যান্ডম 832 একটি "সর্পিল নিয়ম" রয়েছে, এবং এটি আপনার সবেমাত্র উল্লিখিত কেসটিকে বোঝায়, অর্থাত্ প্রথম বন্ধনী / অ্যারেগুলি কীভাবে মোকাবেলা করা যায় ইত্যাদি সম্পর্কে কথা বলা অবশ্যই কোনও স্ট্যান্ডার্ড সি নিয়ম নয়, তবে কীভাবে ডিল করতে হবে তা নির্ধারণের জন্য একটি ভাল স্মৃতিচারণ জটিল ঘোষণা সহ। আইএমএইচও, এটি অত্যন্ত কার্যকর এবং যখন আপনি সমস্যার মুখোমুখি হন বা সিডেকএল.আর.ও . ঘোষণাকে পার্স করতে পারে না তখন আপনাকে বাঁচায় । অবশ্যই এই জাতীয় ঘোষণাকে অপব্যবহার করা উচিত নয়, তবে তারা কীভাবে পার্স হয় তা জেনে রাখা ভাল।
বনামস্ফটকো

5
@vsoftco তবে আপনি যখন প্রথম বন্ধনী পৌঁছেছেন কেবল তখনই যদি আপনি ঘুরিয়ে দেন তবে এটি "সর্পিল / ঘড়ির কাঁটার দিকে অগ্রসর হওয়া" নয়।
র্যান্ডম 832

2
ওহো, আপনার উল্লেখ করা উচিত যে সর্পিল নিয়ম সর্বজনীন নয়
হ্যাক

105

"সর্পিল" নিয়মের ধরণের ধরণের নিম্নলিখিত অগ্রাধিকার নিয়মের বাইরে পড়ে:

T *a[]    -- a is an array of pointer to T
T (*a)[]  -- a is a pointer to an array of T
T *f()    -- f is a function returning a pointer to T
T (*f)()  -- f is a pointer to a function returning T

সাবস্ক্রিপ্ট []এবং ফাংশন কল ()অপারেটরগুলির অ্যানারি থেকে উচ্চতর অগ্রাধিকার রয়েছে *, তাই *f()পার্স করা হয় *(f())এবং *a[]হিসাবে পার্স করা হয় *(a[])

সুতরাং আপনি যদি কোনও অ্যারেতে কোনও পয়েন্টার বা কোনও ফাংশনে একটি পয়েন্টার চান, তবে আপনাকে স্পষ্টতই *শনাক্তকারীটির সাথে গোষ্ঠীটি তৈরি করতে হবে , যেমন (*a)[]বা হিসাবে (*f)()

তারপরে আপনি এটি উপলব্ধি করতে পারেন aএবং fকেবল সনাক্তকারীদের চেয়ে আরও জটিল অভিব্যক্তি হতে পারে; মধ্যে T (*a)[N], aএকটি সহজ শনাক্তকারী হতে পারে, অথবা এটি মত একটি ফাংশন কল হতে পারে (*f())[N]( a-> f()), অথবা এটা পছন্দ একটি অ্যারের হতে পারে (*p[M])[N], ( a-> p[M]), অথবা এটা পছন্দ ফাংশন পয়েন্টার একটি অ্যারের হতে পারে (*(*p[M])())[N]( a-> (*p[M])()), প্রভৃতি

*ইন্ডিয়ারেশন অপারেটরটি আনারির পরিবর্তে পোস্টফিক্স হলে এটি দুর্দান্ত হবে , যা বাম থেকে ডানদিকে ডিক্লোরেশনগুলি কিছুটা সহজ করে দেবে ( void f[]*()*();স্পষ্টতই এর চেয়ে ভাল প্রবাহিত হবে void (*(*f[])())()), তবে তা নয়।

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

         f              -- f
         f[]            -- is an array
        *f[]            -- of pointers  ([] has higher precedence than *)
       (*f[])()         -- to functions
      *(*f[])()         -- returning pointers
     (*(*f[])())()      -- to functions
void (*(*f[])())();     -- returning void

signalমান লাইব্রেরিতে ফাংশন সম্ভবত বাতুলতা এই ধরনের টাইপ নমুনা হল:

       signal                                       -- signal
       signal(                          )           -- is a function with parameters
       signal(    sig,                  )           --    sig
       signal(int sig,                  )           --    which is an int and
       signal(int sig,        func      )           --    func
       signal(int sig,       *func      )           --    which is a pointer
       signal(int sig,      (*func)(int))           --    to a function taking an int                                           
       signal(int sig, void (*func)(int))           --    returning void
      *signal(int sig, void (*func)(int))           -- returning a pointer
     (*signal(int sig, void (*func)(int)))(int)     -- to a function taking an int
void (*signal(int sig, void (*func)(int)))(int);    -- and returning void

এই মুহুর্তে বেশিরভাগ লোক "টাইপডিফ ব্যবহার করুন" বলে, যা অবশ্যই একটি বিকল্প:

typedef void outerfunc(void);
typedef outerfunc *innerfunc(void);

innerfunc *f[N];

কিন্তু ...

আপনি কীভাবে একটি অভিব্যক্তিতে ব্যবহার করবেন f? আপনি জানেন যে এটি পয়েন্টারগুলির একটি অ্যারে, তবে আপনি সঠিক ফাংশনটি সম্পাদন করতে এটি কীভাবে ব্যবহার করবেন? আপনাকে টাইপডেফগুলি দেখতে হবে এবং সঠিক বাক্য গঠনটি ধাঁধা দিতে হবে। বিপরীতে, "নগ্ন" সংস্করণটি বেশ আইস্ট্যাবাই, তবে এটি আপনাকে ঠিক কীভাবে কোনও এক্সপ্রেশনে ব্যবহার করবেন তা বলে দেয় f(যথা (*(*f[i])())();, ধরে নেওয়া যায় না যে দুটি ফাংশনই আর্গুমেন্ট নেয় না)।


7
'সিগন্যাল'-এর উদাহরণ দেওয়ার জন্য ধন্যবাদ, দেখানো হচ্ছে যে এই ধরণের জিনিস বন্যের মধ্যে উপস্থিত হয়।
জাস্টসাল্ট

এটি একটি দুর্দান্ত উদাহরণ।
কেসি

আমি আপনার fঅধঃপতনের গাছটি পছন্দ করেছি , এর উদাহরণটি ব্যাখ্যা করে দিয়েছি ... কিছু কারণে আমি সবসময় ASCII- আর্ট থেকে বেরিয়ে আসি, বিশেষত যখন জিনিসগুলি
বোঝানোর ক্ষেত্রে

1
অনুমান করা না উভয়ই ফাংশন আর্গুমেন্ট গ্রহণ করে : তাহলে আপনাকে voidফাংশন বন্ধনীতে ব্যবহার করতে হবে, অন্যথায় এটি কোনও যুক্তি নিতে পারে।
হ্যাকগুলি

1
@ হ্যাকস: ঘোষণার জন্য, হ্যাঁ; আমি ফাংশন কল সম্পর্কে বলছিলাম।
জন বোদে

57

সি-তে ঘোষণাপত্রের ব্যবহার মিরর — এটিই এটি স্ট্যান্ডার্ডে সংজ্ঞায়িত হয়। ঘোষণা:

void (*(*f[])())()

হ'ল একটি দৃ the়তা যা অভিব্যক্তি (*(*f[i])())()প্রকারের ফলাফল দেয় void। যার অর্থ:

  • f অবশ্যই এটির একটি অ্যারে হতে হবে, যেহেতু আপনি এটি সূচক করতে পারেন:

    f[i]
  • এর উপাদানগুলি fঅবশ্যই পয়েন্টার হওয়া উচিত, যেহেতু আপনি সেগুলি অবনমিত করতে পারেন:

    *f[i]
  • এই পয়েন্টারগুলি অবশ্যই কোনও আর্গুমেন্ট না নিয়ে ফাংশনগুলিতে পয়েন্টার হওয়া উচিত, যেহেতু আপনি তাদের কল করতে পারেন:

    (*f[i])()
  • এই ফাংশনগুলির ফলাফলগুলি অবশ্যই পয়েন্টার হতে হবে, যেহেতু আপনি সেগুলি অবনমিত করতে পারেন:

    *(*f[i])()
  • ঐ পয়েন্টার আবশ্যক এছাড়াও কোন যুক্তি গ্রহণ, যেহেতু আপনি তাদের কল করতে পারেন ফাংশন পয়েন্টার হতে:

    (*(*f[i])())()
  • এই ফাংশন পয়েন্টার অবশ্যই ফিরে আসতে হবে void

"সর্পিল নিয়ম" হ'ল একটি স্মৃতিচারণ যা একই জিনিস বোঝার জন্য আলাদা উপায় সরবরাহ করে।


3
এটি দেখার দুর্দান্ত উপায় যা আমি আগে কখনও দেখিনি। +1
tbodt

4
খুশী হলাম। এইভাবে দেখা, এটি সত্যিই সহজ । প্রকৃতপক্ষে বরং এর চেয়ে সহজ কিছু vector< function<function<void()>()>* > f, বিশেষত যদি আপনি এসটিতে যোগ করেন std::। (কিন্তু ভাল, উদাহরণস্বরূপ হয় কল্পিত ... এমনকি f :: [IORef (IO (IO ()))]অদ্ভুত দেখায়।)
leftaroundabout

1
@ টিমোডেনক: ঘোষণাপত্রটি a[x]ইঙ্গিত দেয় যে কখন প্রকাশটি a[i]কার্যকর হয় i >= 0 && i < x। অন্যদিকে, a[]আকারটি অনির্ধারিত ছেড়ে দেয় এবং তাই এর অনুরূপ *a: এটি ইঙ্গিত করে যে এক্সপ্রেশনটি a[i](বা সমতুল্য *(a + i)) কিছু পরিসরের জন্য বৈধ i
জন পুরে

4
এটি সি প্রকারগুলি সম্পর্কে ভাবার পক্ষে সবচেয়ে সহজ উপায়, এর জন্য ধন্যবাদ
অ্যালেক্স ওজার

4
আমি এটা ভালোবাসি! মূর্খ সর্পিলের চেয়ে বেশি যুক্তিযুক্ত। (*f[])()এমন একটি প্রকার যা আপনি সূচক করতে পারেন, তারপরে ডেরিফারেন্স করতে পারেন, তারপরে কল করতে পারেন, সুতরাং এটি ফাংশনগুলিতে পয়েন্টারের একটি অ্যারে।
লিন

32

সুতরাং এই "স্পাইরিয়াল পড়া" বৈধ কিছু?

সর্পিল নিয়ম প্রয়োগ করা বা সিডিসিল ব্যবহার করা সর্বদা বৈধ নয় not উভয় ক্ষেত্রেই কিছু ক্ষেত্রে ব্যর্থ হয়। সর্পিল নিয়ম অনেক ক্ষেত্রে কাজ করে তবে এটি সর্বজনীন নয়

জটিল ঘোষণার জন্য এই দুটি সহজ নিয়ম মনে রাখবেন:

  • সর্বদা অভ্যন্তরীণ থেকে ঘোষণাগুলি পড়ুন : ভিতরের থেকে শুরু করুন, যদি কোনও হয় তবে প্রথম বন্ধনী। যে শনাক্তকারী ঘোষিত হচ্ছে তা সনাক্ত করুন এবং সেখান থেকে ঘোষণার সিদ্ধান্ত নেওয়া শুরু করুন।

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

এই নিয়মটিতে শনাক্তকারীর একপাশ থেকে অন্য দিকে জিগজ্যাগিং জড়িত ।

এখন একটি সাধারণ ঘোষণার সিদ্ধান্ত গ্রহণ করা

int *a[10];

বিধি প্রয়োগ:

int *a[10];      "a is"  
     ^  

int *a[10];      "a is an array"  
      ^^^^ 

int *a[10];      "a is an array of pointers"
    ^

int *a[10];      "a is an array of pointers to `int`".  
^^^      

এর মত জটিল ঘোষণাকে ডিক্রিফার করি

void ( *(*f[]) () ) ();  

উপরোক্ত বিধি প্রয়োগ করে:

void ( *(*f[]) () ) ();        "f is"  
          ^  

void ( *(*f[]) () ) ();        "f is an array"  
           ^^ 

void ( *(*f[]) () ) ();        "f is an array of pointers" 
         ^    

void ( *(*f[]) () ) ();        "f is an array of pointers to function"   
               ^^     

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer"
       ^   

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer to function" 
                    ^^    

void ( *(*f[]) () ) ();        "f is an array of pointers to function returning pointer to function returning `void`"  
^^^^

আপনি কীভাবে যাচ্ছেন তা এখানে একটি জিআইএফ প্রদর্শন করছে (বৃহত্তর দৃশ্যের জন্য চিত্রটিতে ক্লিক করুন):

এখানে চিত্র বর্ণনা লিখুন


এখানে বর্ণিত বিধিগুলি কে এন কেইংয়ের সি প্রোগ্রামিং এ মডার্ন অ্যাপ্রোচ বইটি থেকে নেওয়া হয়েছে ।


এটি ঠিক স্ট্যান্ডার্ডের পদ্ধতির মতো যেমন "ঘোষণার আয়না ব্যবহার"। যদিও আমি এই মুহুর্তে আরও কিছু জিজ্ঞাসা করতে চাই: আপনি কেএন কিং এর বইয়ের পরামর্শ দিচ্ছেন? আমি বইটি সম্পর্কে প্রচুর সুন্দর পর্যালোচনা দেখছি।
মোটুন

1
হ্যাঁ। আমি সেই বইয়ের পরামর্শ দিই না। আমি সেই বই থেকে প্রোগ্রামিং শুরু করি। ভাল লেখাগুলি এবং সেখানে সমস্যা।
হ্যাক

আপনি কী ঘোষণাটি বুঝতে না পেরে সিডিসিলের উদাহরণ দিতে পারেন? আমি ভেবেছিলাম সিডিএকএলগুলি কম্পাইলার হিসাবে একই পার্সিং বিধিগুলি ব্যবহার করেছে এবং আমি যতদূর বলতে পারি এটি সর্বদা কার্যকর হয়।
ফ্যাবিও

@FabioTurati; একটি ফাংশন অ্যারে বা ফাংশনটি ফিরিয়ে দিতে পারে না। char (x())[5]সিনট্যাক্স ত্রুটির ফলে হওয়া উচিত তবে সিডিসিএলকে এটি পার্স করুন: ফাংশন রিটার্নিং অ্যারে 5 হিসাবে ঘোষণা করুনxchar
হ্যাক

12

এটি কেবলমাত্র "সর্পিল" কারণ এই ঘোষণাপত্রে, প্রথম বন্ধনের প্রতিটি স্তরের মধ্যে প্রতিটি পক্ষের কেবলমাত্র একজন অপারেটর রয়েছে। আপনি "একটি সর্পিল" -এ এগিয়ে যাওয়ার দাবিটি সাধারণত আপনাকে ঘোষণাপত্রে অ্যারে এবং পয়েন্টারগুলির মধ্যে বিকল্প প্রস্তাব দেয় int ***foo[][][]যখন বাস্তবে যখন অ্যারে স্তরগুলির সমস্ত পয়েন্টার স্তরের কোনওটির আগে আসে।


ওয়েল, "সর্পিল অভিগমন", আপনি এখানে রাইট আপনি যা করতে পারেন, তারপর যতটা আপনি পারেন চলে আসা, ইত্যাদি কিন্তু এটা প্রায়ই ভুল করে ব্যাখ্যা হচ্ছে ... যত যেতে
লিন

7

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


3
তবুও, কীভাবে এটি টাইডেফ পার্স করতে হয় তা জানা থাকলেও কীভাবে এটি পার্স করা যায় তা জানা গুরুত্বপূর্ণ!
inetknght

1
@ আইনেটএনকিএনএইচটি, টাইপডিফসের সাহায্যে আপনি যেভাবে এটি করছেন তা হ'ল তাদের যথেষ্ট সহজ করা যাতে কোনও পার্সিংয়ের প্রয়োজন হয় না।
সের্গেইএ

2
সাক্ষাত্কারের সময় এই ধরণের প্রশ্ন জিজ্ঞাসা করা লোকেরা কেবল তাদের ডিমগুলিকে স্ট্রোক করার জন্য তা করে।
কেসি

1
@ জনবোড, এবং আপনি ফাংশনের রিটার্ন মান টাইপডেফিংয়ের মাধ্যমে নিজের পক্ষে আনবেন।
সের্গেইএ

1
@ জনবড, আমি এটি ব্যক্তিগত পছন্দের বিষয়টিকে বিতর্ক করার মতো নয় বলে মনে করি। আমি আপনার পছন্দ দেখতে পাচ্ছি, আমার এখনও আছে।
সের্গেইএ

7

এলোমেলো ট্রিভিয়া ফ্যাক্টয়েড হিসাবে, আপনি এটি জেনে মজাদার মনে করতে পারেন যে সি ঘোষণাগুলি কীভাবে পড়া হয় তা বর্ণনা করার জন্য ইংরেজিতে একটি আসল শব্দ রয়েছে: বুস্টোফেরডোনালি , এটি, বাম থেকে ডানদিকে ডান থেকে বামে পর্যায়ক্রমে।

তথ্যসূত্র: ভ্যান ডের লিন্ডেন, 1994 - পৃষ্ঠা 76


1
যে শব্দ নির্দেশ করে না মধ্যে হিসাবে বাম বন্ধনী দ্বারা বা একটি একক লাইন নেস্টেড। এটি একটি "সাপ" প্যাটার্নটি বর্ণনা করে, একটি এলটিআর লাইন পরে আরটিএল লাইন।
পোটোসওয়টার

5

এর কার্যকারিতা সম্পর্কে, শেলকোডের সাথে কাজ করার সময় আপনি এই নির্মাণটি দেখতে পাবেন:

int (*ret)() = (int(*)())code;
ret();

যদিও সিনট্যাকটিকভাবে বেশ জটিল না হলেও এই বিশেষ ধরণটি অনেকটা সামনে আসে।

এই এসও প্রশ্নের আরও সম্পূর্ণ উদাহরণ ।

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


5

ঘোষণা

void (*(*f[])())()

বলার কেবল একটি অস্পষ্ট উপায়

Function f[]

সঙ্গে

typedef void (*ResultFunction)();

typedef ResultFunction (*Function)();

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


4

আমি ব্রুস একেল দ্বারা বর্ণিত পদ্ধতিটি সহায়ক এবং অনুসরণ করা সহজ বলে খুঁজে পেয়েছি:

একটি ফাংশন পয়েন্টার সংজ্ঞা

কোনও ফাংশনের পয়েন্টার সংজ্ঞায়িত করার জন্য যার কোনও যুক্তি নেই এবং কোনও রিটার্ন মান নেই, আপনি বলেছেন:

void (*funcPtr)();

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

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

আপনি ভাবতে পারেন যে * ফানকপিটিআর কেন বন্ধনী প্রয়োজন। আপনি যদি সেগুলি ব্যবহার না করেন তবে সংকলকটি দেখতে পাবেন:

void *funcPtr();

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

জটিল ঘোষণা এবং সংজ্ঞা

একদিকে যেমন, আপনি যখন বুঝতে পারবেন যে সি এবং সি ++ ডিক্লেয়ারেশন সিনট্যাক্স কীভাবে কাজ করে আপনি আরও জটিল আইটেম তৈরি করতে পারেন। এই ক্ষেত্রে:

//: C03:ComplicatedDefinitions.cpp

/* 1. */     void * (*(*fp1)(int))[10];

/* 2. */     float (*(*fp2)(int,int,float))(int);

/* 3. */     typedef double (*(*(*fp3)())[10])();
             fp3 a;

/* 4. */     int (*(*f4())[10])();


int main() {} ///:~ 

প্রত্যেকটির মধ্য দিয়ে চলুন এবং এটি বের করার জন্য ডান-বাম নির্দেশিকাটি ব্যবহার করুন। সংখ্যা 1 বলেছেন "fp1 হ'ল একটি ফাংশনটির জন্য একটি পয়েন্টার যা একটি পূর্ণসংখ্যার আর্গুমেন্ট নেয় এবং একটি পয়েন্টারটিকে 10 অকার্যকর পয়েন্টারগুলির একটি অ্যারে প্রদান করে” "

সংখ্যা 2 বলছে "fp2 একটি ফাংশনটির জন্য একটি পয়েন্টার যা তিনটি আর্গুমেন্ট নেয় (int, int, এবং ভাসা) এবং একটি ফাংশনটিতে একটি পয়েন্টার দেয় যা একটি পূর্ণসংখ্যার আর্গুমেন্ট নেয় এবং একটি ফ্লোট ফেরত দেয়” "

আপনি যদি অনেক জটিল সংজ্ঞা তৈরি করে থাকেন তবে আপনি একটি টাইপডেফ ব্যবহার করতে পারেন। সংখ্যা 3 দেখায় যে কীভাবে টাইপয়েফ প্রতিটি সময় জটিল বিবরণ টাইপ করে সংরক্ষণ করে। এটি বলে যে "একটি এফপি 3 হ'ল কোনও ফাংশনের পয়েন্টার যা কোনও আর্গুমেন্ট নেয় না এবং কোনও পয়েন্টারকে 10 পয়েন্টারের অ্যারেগুলিতে কোনও ফাংশন দেয় যা কোনও আর্গুমেন্ট নেয় না এবং ডাবল ফেরত দেয়” " তারপরে এটি বলে যে “এফ এফ 3 টাইপের মধ্যে একটি এটি।” টাইপডেফ সাধারণগুলি থেকে জটিল বর্ণনা তৈরির জন্য কার্যকর।

4 নম্বর একটি পরিবর্তনশীল সংজ্ঞা পরিবর্তে একটি ফাংশন ঘোষণা। এটি বলে যে "f4 একটি ফাংশন যা 10 পয়েন্টারের অ্যারেগুলিতে একটি ইঙ্গিত দেয় যা পূর্ণসংখ্যা ফেরত দেয়।"

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

নেওয়া হয়েছে: সি ++ ভলিউম 1, দ্বিতীয় সংস্করণ, 3 য় অধ্যায়, ব্রুস এক্কেলের রচনা "ফাংশন অ্যাড্রেস" ভাবা।


4

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

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

গুরুতরভাবে, এক নজরে এটি করা শিখতে কঠিন নয়; আপনাকে কেবল দক্ষতার অনুশীলন করতে কিছু সময় ব্যয় করতে ইচ্ছুক হতে হবে। আপনি যদি অন্য লোকদের দ্বারা লিখিত সি কোড বজায় রাখতে বা মানিয়ে নিতে যাচ্ছেন তবে অবশ্যই এই সময়টি বিনিয়োগ করা উপযুক্ত। অন্যান্য প্রোগ্রামাররা এটি শিখেনি তাদের বের করে দেওয়ার জন্য এটি একটি মজাদার পার্টি কৌশলও।

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

এটি আরও খারাপ হতে পারে, আপনি জানেন। এখানে একটি আইনি পিএল / আই বিবৃতি ছিল যা এরকম কিছু দিয়ে শুরু হয়েছিল:

if if if = then then then = else else else = if then ...

2
পিএল / আই স্টেটমেন্টটি ছিল IF IF = THEN THEN THEN = ELSE ELSE ELSE = ENDIF ENDIFএবং হিসাবে পার্স করা হয়েছে if (IF == THEN) then (THEN = ELSE) else (ELSE = ENDIF)
কোল জনসন

আমি মনে করি একটি সংস্করণ ছিল যা শর্তসাপেক্ষে IF / THEN / ELSE এক্সপ্রেশন (সি এর সমতুল্য? :) ব্যবহার করে এটি এক ধাপ এগিয়ে নিয়ে গিয়েছিল, যা মিশ্রণের তৃতীয় সেটটি পেয়েছে ... তবে এটি কয়েক দশক হয়ে গেছে এবং হতে পারে ভাষার একটি নির্দিষ্ট উপভাষার উপর নির্ভরশীল। বিন্দুটি থেকে যায় যে কোনও ভাষার কমপক্ষে একটি প্যাথলজিকাল ফর্ম রয়েছে।
কেশলাম

4

আমি অনেক বছর আগে ওহ লিখেছিলাম যে সর্পিল নিয়মের মূল লেখক হয়েছি (যখন আমার প্রচুর চুল ছিল :) এবং যখন সিএফএকে যুক্ত হয়েছিল তখন সম্মানিত হয়েছিলাম।

আমি আমার শিক্ষার্থী এবং সহকর্মীদের জন্য "তাদের মাথার" সি ঘোষণাগুলি পড়া সহজ করার উপায় হিসাবে সর্পিল নিয়মটি লিখেছি; উদাহরণস্বরূপ, cdecl.org ইত্যাদির মতো সফ্টওয়্যার সরঞ্জামগুলি ব্যবহার না করে my যদিও আমি এই দেখে আনন্দিত যে এই বিধি বছরের পর বছর ধরে হাজার হাজার সি প্রোগ্রামিং ছাত্র এবং অনুশীলনকারীকে আক্ষরিক অর্থে সহায়তা করেছে!

রেকর্ড এর জন্য,

লিনাস টরভাল্ডস (যাকে আমি অত্যন্ত শ্রদ্ধা করি) সহ বহু সাইটে এটি বহুবার "সঠিকভাবে" চিহ্নিত হয়েছে, এমন পরিস্থিতি রয়েছে যেখানে আমার সর্পিল নিয়ম "ভেঙে যায়"। সর্বাধিক সাধারণ সত্তা:

char *ar[10][10];

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

char *(ar[10][10]);

এখন, সর্পিল নিয়ম অনুসরণ করে, আমি পেয়ে যাব:

"আরআর একটি 10x10 দ্বি-মাত্রিক অ্যারে যা চরিত্রে নির্দেশক"

আমি আশা করি সর্পিল নিয়মটি সি শিখতে তার উপযোগিতাটি বহন করে!

পুনশ্চ:

আমি "সি হার্ড নয়" চিত্রটি ভালোবাসি :)


3
  • অকার্যকর (*(*f[]) ()) ()

সমাধান void>>

  • (*(*f[]) ()) () = অকার্যকর

পুনরায় জীবিত ()>>

  • (* (*f[]) ()) = ফাংশন রিটার্নিং (অকার্যকর)

সমাধান *>>

  • (*f[]) () = নির্দেশক (ফাংশন রিটার্নিং (অকার্যকর))

সমাধান ()>>

  • (* f[]) = ফাংশন রিটার্নিং (পয়েন্টার টু (ফাংশন রিটার্নিং (অকার্যকর)))

সমাধান *>>

  • f[] = পয়েন্টার (ফাংশন রিটার্নিং (পয়েন্টার টু (ফাংশন রিটার্নিং (অকার্যকর))))

সমাধান [ ]>>

  • f = অ্যারের (পয়েন্টার টু (ফাংশন রিটার্নিং (পয়েন্টার টু (ফাংশন রিটার্নিং (অকার্যকর)))))
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.