সি কাজ ফাংশন পয়েন্টার কিভাবে?


1232

সি-তে ফাংশন পয়েন্টার নিয়ে ইদানীং আমার কিছু অভিজ্ঞতা হয়েছিল

সুতরাং আপনার নিজের প্রশ্নের উত্তর দেওয়ার traditionতিহ্য ধরে রেখে, আমি সিদ্ধান্ত নিয়েছি যে এই বিষয়টিতে দ্রুত ডাইভ-ইন প্রয়োজন তাদের জন্য খুব বেসিকগুলির একটি ছোট সংক্ষিপ্তসার তৈরি করব।


34
এছাড়াও: সি পয়েন্টারগুলির গভীর-বিশ্লেষণের জন্য, ব্লগগুলি দেখুন oঅরাকল . com/ksplice/entry/the_ksplice_pointer_challenge । এছাড়াও, গ্রাউন্ড আপ থেকে প্রোগ্রামিং দেখায় যে তারা কীভাবে মেশিন স্তরে কাজ করে। বুঝুন সি এর "মেমরি মডেল" বুঝতে কিভাবে সি কাজ পয়েন্টার জন্য খুবই দরকারী।
আব্বাফাই

8
দুর্দান্ত তথ্য। যদিও শিরোনাম দ্বারা, আমি সত্যিই "ফাংশন পয়েন্টারগুলি কীভাবে কার্য সম্পাদন করে" তার কোড দেওয়া হয় না তার একটি ব্যাখ্যা দেখতে আশা করি :)
বোগদান আলেকজান্দ্রু

উত্তর:


1477

সি তে ফাংশন পয়েন্টার

আসুন একটি প্রাথমিক ফাংশন দিয়ে শুরু করি যা আমরা নির্দেশ করব :

int addInt(int n, int m) {
    return n+m;
}

প্রথম জিনিস, আসুন একটি ফাংশনটির জন্য একটি পয়েন্টার সংজ্ঞায়িত করি যা 2 intটি গ্রহণ করে এবং একটি প্রদান করে int:

int (*functionPtr)(int,int);

এখন আমরা নিরাপদে আমাদের ফাংশনটি নির্দেশ করতে পারি:

functionPtr = &addInt;

এখন আমাদের ফাংশনটির একটি পয়েন্টার রয়েছে, আসুন এটি ব্যবহার করুন:

int sum = (*functionPtr)(2, 3); // sum == 5

অন্য ফাংশনে পয়েন্টারটি পাস করা মূলত:

int add2to3(int (*functionPtr)(int, int)) {
    return (*functionPtr)(2, 3);
}

রিটার্ন মানগুলিতে আমরা ফাংশন পয়েন্টারগুলিও ব্যবহার করতে পারি (চালিয়ে যাওয়ার চেষ্টা করুন, এটি অগোছালো হয়ে যায়):

// this is a function called functionFactory which receives parameter n
// and returns a pointer to another function which receives two ints
// and it returns another int
int (*functionFactory(int n))(int, int) {
    printf("Got parameter %d", n);
    int (*functionPtr)(int,int) = &addInt;
    return functionPtr;
}

তবে এটি ব্যবহার করা খুব সুন্দর typedef:

typedef int (*myFuncDef)(int, int);
// note that the typedef name is indeed myFuncDef

myFuncDef functionFactory(int n) {
    printf("Got parameter %d", n);
    myFuncDef functionPtr = &addInt;
    return functionPtr;
}

19
মহান তথ্যের জন্য ধন্যবাদ। ফাংশন পয়েন্টারগুলি কোথায় ব্যবহৃত হয় বা বিশেষভাবে দরকারী বলে আপনি কিছুটা অন্তর্দৃষ্টি যুক্ত করতে পারেন?
সমৃদ্ধ কার্পেন্টার

326
"ফাংশনপিটিআর = & অ্যাডআইন্ট;" "ফাংশনপিটিআর = অ্যাডইন্ট" হিসাবেও লেখা যেতে পারে (এবং প্রায়শই হয়); মানটি যেহেতু বৈধ তাও প্রমিত যেহেতু এই প্রসঙ্গে কোনও ফাংশনের নামটি ফাংশনের ঠিকানায় রূপান্তরিত হয়।
hlovdal

22
হ্যালোভডাল, এই প্রসঙ্গে এটি ব্যাখ্যা করার জন্য এটি আকর্ষণীয় যে এটি একটি যা ফাংশন লিখতে সক্ষম করে তোলে পিটিআর = ****************** অ্যাড আইন্ট;
জোহানেস স্কাউব - লিটব

105
@ Rich.Carpenter আমি জানি এই 4 বছর খুব দেরি হয়ে গেছে, কিন্তু আমি জন এই থেকে উপকৃত হতে পারে চিন্তা: ফাংশন পয়েন্টার অন্যান্য ফাংশন প্যারামিটার হিসেবে ফাংশন ক্ষণস্থায়ী জন্য দরকারী । কিছু অদ্ভুত কারণে সেই উত্তরটি খুঁজতে আমার অনেক সন্ধান করেছিল। সুতরাং মূলত এটি সি সিউডোকে প্রথম শ্রেণির কার্যকারিতা দেয়।
দৈত্য 91

22
@ রিচ.ক্যারপেন্টার: রানটাইম সিপিইউ সনাক্তকরণের জন্য ফাংশন পয়েন্টারগুলি দুর্দান্ত। এসএসই, পপসেন্ট, অ্যাভিএক্স ইত্যাদির সুবিধা গ্রহণের জন্য কয়েকটি ফাংশনের একাধিক সংস্করণ রয়েছে শুরুতে আপনার ফাংশন পয়েন্টারগুলিকে বর্তমান সিপিইউর জন্য প্রতিটি ফাংশনের সেরা সংস্করণে সেট করুন। আপনার অন্যান্য কোডে, কেবল সর্বত্র সিপিইউ বৈশিষ্ট্যগুলিতে শর্তাধীন শাখা না রেখে ফাংশন পয়েন্টারটির মাধ্যমে কল করুন ter তারপরে আপনি এটি ভাল সিদ্ধান্ত নেওয়ার বিষয়ে জটিল যুক্তি করতে পারেন, যদিও এই সিপিইউ সমর্থন করে তবে pshufbএটি ধীর গতির, সুতরাং আগের বাস্তবায়ন এখনও আরও দ্রুত। x264 / x265 এটিকে ব্যাপকভাবে ব্যবহার করুন এবং এটি ওপেন সোর্স।
পিটার কর্ডস

303

সি-তে ফাংশন পয়েন্টারগুলি সি-তে অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং করতে ব্যবহৃত হতে পারে

উদাহরণস্বরূপ, নিম্নলিখিত লাইনগুলি সিতে লিখিত হয়েছে:

String s1 = newString();
s1->set(s1, "hello");

হ্যাঁ, অপারেটরের ->অভাব এবং অপ্রতুলতা newএকটি মৃত দেওয়াই, তবে এটি নিশ্চিতভাবেই বোঝা যাচ্ছে যে আমরা কিছু Stringশ্রেণির পাঠ্য সেট করে দিচ্ছি "hello"

ফাংশন পয়েন্টার ব্যবহার করে, সি-তে পদ্ধতিগুলি অনুকরণ করা সম্ভব

এটি কীভাবে সম্পন্ন হয়?

Stringবর্গ আসলে একটি হয় structফাংশন পয়েন্টার যা অনুকরণ পদ্ধতি একটি উপায় হিসাবে কাজ একটি গুচ্ছ সঙ্গে। নিম্নলিখিতটি Stringক্লাসের আংশিক ঘোষণা :

typedef struct String_Struct* String;

struct String_Struct
{
    char* (*get)(const void* self);
    void (*set)(const void* self, char* value);
    int (*length)(const void* self);
};

char* getString(const void* self);
void setString(const void* self, char* value);
int lengthString(const void* self);

String newString();

হিসাবে দেখা যায়, Stringক্লাসের পদ্ধতিগুলি আসলে ঘোষিত ফাংশনের ফাংশন পয়েন্টার। দৃষ্টান্তটির প্রস্তুতির ক্ষেত্রে String, newStringফাংশনটি তাদের নিজ নিজ কার্যক্রমে ফাংশন পয়েন্টার সেট আপ করার জন্য ডাকা হয়:

String newString()
{
    String self = (String)malloc(sizeof(struct String_Struct));

    self->get = &getString;
    self->set = &setString;
    self->length = &lengthString;

    self->set(self, "");

    return self;
}

উদাহরণস্বরূপ, পদ্ধতিটি getStringআহ্বান করে যে ফাংশনটি ডাকা হয় getতাকে নিম্নলিখিত হিসাবে সংজ্ঞায়িত করা হয়:

char* getString(const void* self_obj)
{
    return ((String)self_obj)->internal->value;
}

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

সুতরাং, এটি করতে সক্ষম হওয়ার পরিবর্তে s1->set("hello");, ক্রিয়াটি সম্পাদন করতে একজনকে অবশ্যই অবজেক্টে পাস করতে হবে s1->set(s1, "hello")

এই ছোটখাট ব্যাখ্যাটি নিজের কাছে নিজেকে ছাড়িয়ে যাওয়ার পরে, আমরা পরের অংশে চলে যাব, যা উত্তরাধিকারসূত্রে সি

ধরুন আমরা একটি সাবক্লাস তৈরি করতে চাই String, একটি বলুন ImmutableString। অর্ডার স্ট্রিং অপরিবর্তনীয় করার জন্য, setপদ্ধতি, প্রবেশযোগ্য হবে না যখন এক্সেস বজায় রাখার getএবং length, এবং একটি গ্রহণ করতে "কনস্ট্রাকটর" জোর char*:

typedef struct ImmutableString_Struct* ImmutableString;

struct ImmutableString_Struct
{
    String base;

    char* (*get)(const void* self);
    int (*length)(const void* self);
};

ImmutableString newImmutableString(const char* value);

মূলত, সমস্ত সাবক্লাসের জন্য, উপলব্ধ পদ্ধতিগুলি আবার ফাংশন পয়েন্টার। এবার, setপদ্ধতির ঘোষণাপত্রটি উপস্থিত নেই, সুতরাং এটি একটিতে বলা যাবে না ImmutableString

বাস্তবায়নের ক্ষেত্রে ImmutableString, কেবলমাত্র প্রাসঙ্গিক কোডটি হল "কনস্ট্রাক্টর" ফাংশন, এটি newImmutableString:

ImmutableString newImmutableString(const char* value)
{
    ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));

    self->base = newString();

    self->get = self->base->get;
    self->length = self->base->length;

    self->base->set(self->base, (char*)value);

    return self;
}

শুরু করতে গিয়ে সালে ImmutableString, এর ফাংশন পয়েন্টার getএবং lengthপদ্ধতি আসলে পড়ুন String.getএবং String.lengthপদ্ধতি, চোখ বুলিয়ে baseপরিবর্তনশীল যা অভ্যন্তরীণভাবে সঞ্চিত হয় Stringঅবজেক্ট।

একটি ফাংশন পয়েন্টার ব্যবহার একটি সুপারক্লাস থেকে একটি পদ্ধতির উত্তরাধিকার অর্জন করতে পারে।

আমরা আরও সিতে পলিমার্ফিজম চালিয়ে যেতে পারি ।

উদাহরণস্বরূপ যদি আমরা কোনও কারণে ক্লাসে সমস্ত সময় lengthফিরে আসার পদ্ধতির আচরণটি পরিবর্তন করতে চেয়েছিলাম তবে যা করতে হবে তা হ'ল:0ImmutableString

  1. ওভাররাইড lengthপদ্ধতি হিসাবে পরিবেশন করতে যাচ্ছে এমন একটি ফাংশন যুক্ত করুন ।
  2. "কনস্ট্রাক্টর" এ যান এবং ওভাররাইড lengthপদ্ধতিতে ফাংশন পয়েন্টার সেট করুন ।

এতে একটি ওভাররাইড lengthপদ্ধতি ImmutableStringযুক্ত করা একটি যুক্ত করে সম্পাদন করা যেতে পারে lengthOverrideMethod:

int lengthOverrideMethod(const void* self)
{
    return 0;
}

তারপরে, lengthকনস্ট্রাক্টরের পদ্ধতির জন্য ফাংশন পয়েন্টারটি এই পর্যন্ত আবদ্ধ হয় lengthOverrideMethod:

ImmutableString newImmutableString(const char* value)
{
    ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));

    self->base = newString();

    self->get = self->base->get;
    self->length = &lengthOverrideMethod;

    self->base->set(self->base, (char*)value);

    return self;
}

ক্লাস হিসাবে ক্লাসে lengthপদ্ধতির জন্য অভিন্ন আচরণ করার পরিবর্তে এখন পদ্ধতিটি ফাংশনে সংজ্ঞায়িত আচরণকে নির্দেশ করবে ।ImmutableStringStringlengthlengthOverrideMethod

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

সি তে কীভাবে অবজেক্ট ভিত্তিক প্রোগ্রামিং করবেন সে সম্পর্কে আরও তথ্যের জন্য, দয়া করে নীচের প্রশ্নগুলি দেখুন:


22
এই উত্তরটি ভয়াবহ! কেবল এটিই বোঝায় না যে ওও কোনওভাবে বিন্দু চিহ্নিতকরণের উপর নির্ভর করে, এটি আপনার অবজেক্টগুলিতে জাঙ্ক লাগাতেও উত্সাহ দেয়!
আলেক্সি আভেরেঙ্কো

27
এটি ওও ঠিক আছে, তবে সি-স্টাইলের ওও এর কাছাকাছি কোথাও নয়। আপনি যা ভাঙার সাথে প্রয়োগ করেছেন তা হ'ল জাভাস্ক্রিপ্ট-স্টাইলের প্রোটোটাইপ-ভিত্তিক ওও। সি ++ / পাস্কাল স্টাইলের ওও পেতে আপনার প্রয়োজন হবে: 1. ভার্চুয়াল সদস্যদের সাথে প্রতিটি শ্রেণির ভার্চুয়াল টেবিলের জন্য একটি কাঠামো কাঠামো তৈরি করুন । ২. বহুগর্ভস্থ বস্তুগুলিতে সেই কাঠামোর দিকে নির্দেশক থাকুন। ৩. ভার্চুয়াল টেবিলের মাধ্যমে ভার্চুয়াল পদ্ধতিগুলি এবং অন্য সমস্ত পদ্ধতিগুলি সরাসরি কল করুন - সাধারণত কিছু ClassName_methodNameফাংশন নামকরণের কনভেনশনে লেগে থাকে। তবেই আপনি সি ++ এবং পাসকালে যেমন করেন তেমন রানটাইম এবং স্টোরেজ ব্যয় পাবেন।
মনিকা

19
ওও হওয়ার মতো নয় এমন একটি ভাষার সাথে ওও কাজ করা সবসময় খারাপ ধারণা। আপনি যদি ওও চান এবং এখনও সি থাকেন তবে কেবল সি ++ এর সাথে কাজ করুন।
rbaleksandar

20
@rbaleksandar এটি লিনাক্স কার্নেল বিকাশকারীদের বলুন। "সর্বদা একটি খারাপ ধারণা" হ'ল কঠোরভাবে আপনার মতামত, যার সাথে আমি দৃly়ভাবে একমত নই।
জোনাথন রাইনহার্ট

6
আমি এই উত্তরটি পছন্দ করি তবে
বিড়াল

227

বরখাস্ত হওয়ার গাইড: আপনার কোডটি হাতে করে সংকলন করে x86 মেশিনে জিসিসিতে ফাংশন পয়েন্টারগুলিকে কীভাবে আপত্তি জানাতে হবে:

এই স্ট্রিং লিটারালগুলি 32-বিট x86 মেশিন কোডের বাইট। 0xC3এটি একটি x86 retনির্দেশ

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

  1. EAX রেজিস্টারটিতে বর্তমান মান প্রদান করে

    int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
  2. একটি অদলবদল ফাংশন লিখুন

    int a = 10, b = 20;
    ((void(*)(int*,int*))"\x8b\x44\x24\x04\x8b\x5c\x24\x08\x8b\x00\x8b\x1b\x31\xc3\x31\xd8\x31\xc3\x8b\x4c\x24\x04\x89\x01\x8b\x4c\x24\x08\x89\x19\xc3 <- This swaps the values of a and b")(&a,&b);
  3. 1000-এ একটি লুপ কাউন্টার লিখুন, প্রতিবার কিছু ফাংশন কল করে

    ((int(*)())"\x66\x31\xc0\x8b\x5c\x24\x04\x66\x40\x50\xff\xd3\x58\x66\x3d\xe8\x03\x75\xf4\xc3")(&function); // calls function with 1->1000
  4. আপনি এমনকি 100 এ গণনা করা একটি পুনরাবৃত্ত ফাংশন লিখতে পারেন

    const char* lol = "\x8b\x5c\x24\x4\x3d\xe8\x3\x0\x0\x7e\x2\x31\xc0\x83\xf8\x64\x7d\x6\x40\x53\xff\xd3\x5b\xc3\xc3 <- Recursively calls the function at address lol.";
    i = ((int(*)())(lol))(lol);

নোট করুন যে সংকলকরা স্ট্রিং লিটারেলগুলিতে স্থাপন করে .rodata বিভাগে (বা.rdata উইন্ডোজে) , যা পাঠ্য বিভাগের অংশ হিসাবে সংযুক্ত (ফাংশনের কোড সহ)।

পাঠ্য বিভাগটিতে রিড + এক্সিকিউ অনুমতি রয়েছে, সুতরাং ফাংশন পয়েন্টারগুলিতে স্ট্রিং লিটারেলগুলি ingালাই mprotect()বা প্রয়োজন ছাড়াই কাজ করেVirtualProtect() সিস্টেম কল যেমন আপনার গতিশীল বরাদ্দ মেমরির প্রয়োজন হয় তেমন কাজ করে। (বা gcc -z execstackদ্রুত হ্যাক হিসাবে স্ট্যাক + ডেটা বিভাগ + হিপ এক্সিকিউটেবলের সাথে প্রোগ্রামটি লিঙ্ক করে))


এগুলিকে বিচ্ছিন্ন করতে, আপনি বাইটগুলিতে একটি লেবেল লাগাতে এটি সংকলন করতে পারেন, এবং একটি সংযোজনকারী ব্যবহার করতে পারেন।

// at global scope
const char swap[] = "\x8b\x44\x24\x04\x8b\x5c\x24\x08\x8b\x00\x8b\x1b\x31\xc3\x31\xd8\x31\xc3\x8b\x4c\x24\x04\x89\x01\x8b\x4c\x24\x08\x89\x19\xc3 <- This swaps the values of a and b";

সংকলন gcc -c -m32 foo.cএবং সাথে বিচ্ছেদobjdump -D -rwC -Mintel , আমরা পেতে পারি এবং জানতে পারি যে এই কোডটি ক্লিবারবারিং ইবিএক্স (একটি কল-সংরক্ষিত রেজিস্ট্রার) দ্বারা এবিআই লঙ্ঘন করে এবং সাধারণত অকার্যকর।

00000000 <swap>:
   0:   8b 44 24 04             mov    eax,DWORD PTR [esp+0x4]   # load int *a arg from the stack
   4:   8b 5c 24 08             mov    ebx,DWORD PTR [esp+0x8]   # ebx = b
   8:   8b 00                   mov    eax,DWORD PTR [eax]       # dereference: eax = *a
   a:   8b 1b                   mov    ebx,DWORD PTR [ebx]
   c:   31 c3                   xor    ebx,eax                # pointless xor-swap
   e:   31 d8                   xor    eax,ebx                # instead of just storing with opposite registers
  10:   31 c3                   xor    ebx,eax
  12:   8b 4c 24 04             mov    ecx,DWORD PTR [esp+0x4]  # reload a from the stack
  16:   89 01                   mov    DWORD PTR [ecx],eax     # store to *a
  18:   8b 4c 24 08             mov    ecx,DWORD PTR [esp+0x8]
  1c:   89 19                   mov    DWORD PTR [ecx],ebx
  1e:   c3                      ret    

  not shown: the later bytes are ASCII text documentation
  they're not executed by the CPU because the ret instruction sends execution back to the caller

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


8
দ্রষ্টব্য: ডেটা এক্সিকিউশন প্রিভেনশন সক্ষম করা থাকলে এটি কার্যকর হয় না (যেমন উইন্ডোজ এক্সপি এসপি 2 + তে), কারণ সি স্ট্রিংগুলি সাধারণত সম্পাদনযোগ্য হিসাবে চিহ্নিত হয় না।
সিকিউরিটিম্যাট

5
হাই ম্যাট! অপ্টিমাইজেশন স্তরের উপর নির্ভর করে, জিসিসি প্রায়শই পাঠ্য সেগমেন্টগুলিতে স্ট্রিং ধ্রুবকগুলিকে ইনলাইন করবে, সুতরাং এটি উইন্ডোজের নতুন সংস্করণে এমনকি কাজ করবে যে আপনি এই ধরণের অপ্টিমাইজেশনটিকে অস্বীকার করবেন না provided (আইআইআরসি, দু'বছর আগে আমার পোস্টের সময়ে মাইংডাব্লু সংস্করণটি ডিফল্ট অপ্টিমাইজেশনের স্তরে স্ট্রিং লিটারেলগুলি ইনলাইন করে)
লি

10
কেউ দয়া করে এখানে কি ঘটছে তা ব্যাখ্যা করতে পারেন? এই অদ্ভুত চেহারা স্ট্রিং আক্ষরিক কি?
অজয়

56
@ajay দেখে মনে হচ্ছে তিনি কাঁচা হেক্সিডিসিমাল মান লিখছেন (উদাহরণস্বরূপ '\ x00' '/ 0' এর সমান, তারা উভয়ই 0 এর সমান) একটি স্ট্রিংয়ে, তারপরে স্ট্রিংটি সি ফাংশন পয়েন্টারে রেখে exec সি ফাংশন পয়েন্টার কারণ সে শয়তান।
ejk314

3
হাই FUZxxl, আমি মনে করি এটি সংকলক এবং অপারেটিং সিস্টেম সংস্করণের উপর ভিত্তি করে আলাদা হতে পারে। উপরের কোডটি কোডেপ্যাড.অর্গ.এ ঠিকঠাক বলে মনে হচ্ছে; codepad.org/FMSDQ3ME
লি

115

ফাংশন পয়েন্টারগুলির জন্য আমার প্রিয় ব্যবহারগুলির মধ্যে একটি সস্তা এবং সহজ পুনরাবৃত্তি -

#include <stdio.h>
#define MAX_COLORS  256

typedef struct {
    char* name;
    int red;
    int green;
    int blue;
} Color;

Color Colors[MAX_COLORS];


void eachColor (void (*fp)(Color *c)) {
    int i;
    for (i=0; i<MAX_COLORS; i++)
        (*fp)(&Colors[i]);
}

void printColor(Color* c) {
    if (c->name)
        printf("%s = %i,%i,%i\n", c->name, c->red, c->green, c->blue);
}

int main() {
    Colors[0].name="red";
    Colors[0].red=255;
    Colors[1].name="blue";
    Colors[1].blue=255;
    Colors[2].name="black";

    eachColor(printColor);
}

7
আপনি যদি কোনওভাবে পুনরাবৃত্তিগুলি থেকে কোনও আউটপুট বের করতে চান (ক্লোজারগুলি ভাবেন) তবে আপনাকে ব্যবহারকারী-নির্দিষ্ট ডেটাতে একটি পয়েন্টারও দিতে হবে।
আলেক্সি আভেরচেঙ্কো

1
একমত। আমার iterators সব ভালো চেহারা: int (*cb)(void *arg, ...)। পুনরুদ্ধারের ফেরতের মানটি আমাকে তাড়াতাড়ি থামাতে দেয় (যদি ননজারো হয়)।
জোনাথন রেইনহার্ট

24

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

  • আইডি: ID: আইডি হল
  • পয়েন্টার *D: ডি পয়েন্টার
  • ফাংশন: D(<parameters>): D: ফাংশন গ্রহণ <পরামিতি >ফিরে

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

typedef int ReturnFunction(char);
typedef int ParameterFunction(void);
ReturnFunction *f(ParameterFunction *p);

আপনি দেখতে পাচ্ছেন, টাইপডিফ ব্যবহার করে এটি তৈরি করা বেশ সহজ। টাইপিডেফগুলি ছাড়া, উপরের ডিক্লেয়ারের নিয়মগুলির সাথে এটি নিয়মিত প্রয়োগ করা শক্ত নয় hard আপনি দেখতে পয়েন্টার পয়েন্টার অংশটি মিস করেছি এবং ফাংশনটি ফিরে আসে। এটিই ঘোষণার একেবারে বাম দিকে প্রদর্শিত হবে এবং আগ্রহের নয়: এটি ইতিমধ্যে যুক্ত করা হয়েছে যদি কেউ ইতিমধ্যে ঘোষককে গড়ে তোলে। আসুন এটি করা যাক। এটি ধারাবাহিকভাবে তৈরি করা, প্রথম শব্দযুক্ত - ব্যবহার করে কাঠামোটি দেখানো [এবং ]:

function taking 
    [pointer to [function taking [void] returning [int]]] 
returning
    [pointer to [function taking [char] returning [int]]]

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

নীচে আপ

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

D1(char);

চরিত্রের প্যারামিটারটি সরাসরি sertedোকানো হয়েছে, যেহেতু এটি তুচ্ছ। দ্বারা প্রতিস্থাপন D1করে ঘোষককে পয়েন্টার যুক্ত করা *D2। নোট করুন যে আমাদের চারপাশে প্রথম বন্ধনী আবদ্ধ করতে হবে *D2। এটি *-operatorএবং ফাংশন-কল অপারেটরের অগ্রাধিকার অনুসন্ধান করে জানা যাবে ()। আমাদের প্রথম বন্ধনী ছাড়া, সংকলক এটি পড়তে হবে *(D2(char p))। তবে এটি অবশ্যই ডি 1 এর কোনও সরল প্রতিস্থাপন হবে না *D2, অবশ্যই। প্যারেন্থিসগুলি সর্বদা ঘোষকগণের আশেপাশে অনুমোদিত হয়। সুতরাং আপনি যদি এগুলিতে বেশি পরিমাণ যুক্ত করেন তবে আপনি কোনও ভুল করবেন না।

(*D2)(char);

রিটার্ন টাইপ সম্পূর্ণ! এখন, আসুন D2ফাংশন ডিক্লেয়ার দ্বারা ফিরিয়ে নেওয়া<parameters> ফাংশন দ্বারা প্রতিস্থাপন করা যাক , D3(<parameters>)যা আমরা বর্তমানে করছি।

(*D3(<parameters>))(char)

নোট করুন যে কোনও বন্ধনী আবশ্যক নয়, যেহেতু আমরা এবার পয়েন্টার ডিক্লেয়ার নয়, ফাংশন-ডিক্লেয়ারার হতে চাই D3 । দুর্দান্ত, কেবলমাত্র এটির পরামিতিগুলি বাকি। প্যারামিটারটি ঠিক একইভাবে সম্পন্ন হয় যেমন আমরা রিটার্ন টাইপ করেছি ঠিক charতেমনটি প্রতিস্থাপন করে void। সুতরাং আমি এটি অনুলিপি করব:

(*D3(   (*ID1)(void)))(char)

আমি প্রতিস্থাপিত করে থাকেন D2দ্বারা ID1, যেহেতু আমরা যে পরামিতি দিয়ে সমাপ্ত হয় (এটা ইতিমধ্যে একটি ফাংশন একটি পয়েন্টার - অন্য declarator জন্য কোন প্রয়োজন)। ID1প্যারামিটারটির নাম হবে। এখন, আমি উপরের দিকে বলেছি যে প্রকারটি সমস্ত ঘোষণাকারী সংশোধন করে - এগুলি প্রতিটি ঘোষণার একেবারে বাম দিকে উপস্থিত হয়। ফাংশনগুলির জন্য, এটি রিটার্ন টাইপ হয়ে যায়। টাইপ ইত্যাদির জন্য পয়েন্টারগুলির জন্য ... টাইপটি লিখলে এটি আকর্ষণীয়, এটি ঠিক ডানদিকে বিপরীত ক্রমে উপস্থিত হবে :) যাইহোক, এটি স্থির করে সম্পূর্ণ ঘোষণা দেয় yield intঅবশ্যই উভয় সময় ।

int (*ID0(int (*ID1)(void)))(char)

আমি ID0উদাহরণে ফাংশন সনাক্তকারী বলা করেছি ।

আপাদোমোস্তোক

প্রকারের বর্ণনায় একেবারে বাম দিকে শনাক্তকারী থেকে এটি শুরু হয়, ডিক্লেয়ারটিকে আমরা ডানদিকে দিয়ে চলার সময় মোড়ানো। দিয়ে শুরু করুন ফাংশন গ্রহণ <পরামিতি >ফিরে

ID0(<parameters>)

বর্ণনার পরবর্তী জিনিসটি ("প্রত্যাবর্তনের" পরে) নির্দেশক ছিল । আসুন এটি অন্তর্ভুক্ত করা যাক:

*ID0(<parameters>)

তারপরে পরের কথাটি ছিল < প্যারামিটারগুলি >ফিরিয়ে নিয়ে ফান্টন । প্যারামিটারটি একটি সরল চর, সুতরাং আমরা এটি সরাসরি তাত্ক্ষণিকভাবে রেখেছি কারণ এটি সত্যই তুচ্ছ।

(*ID0(<parameters>))(char)

আমরা যুক্ত করা প্রথম বন্ধনীগুলি নোট করুন, যেহেতু আমরা আবার চাই যে *প্রথমে বাঁধাই হয় এবং তারপরে(char) । অন্যথায় পড়তে হবে ফাংশন গ্রহণ <পরামিতি >ফাংশন ফিরে ... । নোস, ফাংশনগুলি ফাংশনগুলি এমনকি অনুমোদিত নয়।

এখন আমাদের কেবল লাগানো দরকার < পরামিতি লাগানো> । আমি ব্যয়টির একটি সংক্ষিপ্ত সংস্করণ দেখাব, যেহেতু আমি মনে করি আপনি ইতিমধ্যে এটি কীভাবে করবেন তা সম্পর্কে ধারণা রয়েছে।

pointer to: *ID1
... function taking void returning: (*ID1)(void)

শুধু করা intহচ্ছে আমরা নীচে-আপ সহ করেনি declarators আগে, এবং আমরা শেষ হয়ে

int (*ID0(int (*ID1)(void)))(char)

সুন্দর জিনিস

ডাউন-আপ বা টপ-ডাউন আরও ভাল? আমি নীচের দিকে অভ্যস্ত, তবে কিছু লোক টপ-ডাউনের সাথে আরও আরামদায়ক হতে পারে। এটি আমার মনে হয় স্বাদের বিষয়। ঘটনাক্রমে, আপনি যদি এই ঘোষণায় সমস্ত অপারেটর প্রয়োগ করেন তবে আপনি একটি অন্তর্দৃষ্টি শেষ করবেন:

int v = (*ID0(some_function_pointer))(some_char);

এটি সি-তে ঘোষণার একটি দুর্দান্ত সম্পত্তি: ঘোষণাপত্রটি দৃser়ভাবে জানায় যে opera অপারেটরগুলি সনাক্তকারী ব্যবহার করে যদি কোনও অভিব্যক্তিতে ব্যবহার করা হয়, তবে এটি খুব বাম দিকে টাইপ দেয়। এটি অ্যারের জন্যও এর মতো।

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


24

ফাংশন পয়েন্টারগুলির জন্য আরেকটি ভাল ব্যবহার:
ব্যথাহীনভাবে সংস্করণগুলির মধ্যে স্যুইচ করা

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

version.h

// First, undefine all macros associated with version.h
#undef DEBUG_VERSION
#undef RELEASE_VERSION
#undef INVALID_VERSION


// Define which version we want to use
#define DEBUG_VERSION       // The current version
// #define RELEASE_VERSION  // To be uncommented when finished debugging

#ifndef __VERSION_H_      /* prevent circular inclusions */
    #define __VERSION_H_  /* by using protection macros */
    void board_init();
    void noprintf(const char *c, ...); // mimic the printf prototype
#endif

// Mimics the printf function prototype. This is what I'll actually 
// use to print stuff to the screen
void (* zprintf)(const char*, ...); 

// If debug version, use printf
#ifdef DEBUG_VERSION
    #include <stdio.h>
#endif

// If both debug and release version, error
#ifdef DEBUG_VERSION
#ifdef RELEASE_VERSION
    #define INVALID_VERSION
#endif
#endif

// If neither debug or release version, error
#ifndef DEBUG_VERSION
#ifndef RELEASE_VERSION
    #define INVALID_VERSION
#endif
#endif

#ifdef INVALID_VERSION
    // Won't allow compilation without a valid version define
    #error "Invalid version definition"
#endif

ভিতরে version.c আমি 2 ফাংশন নির্ধারণ হবে বর্তমানে এগুলির নমুনাversion.h

version.c

#include "version.h"

/*****************************************************************************/
/**
* @name board_init
*
* Sets up the application based on the version type defined in version.h.
* Includes allowing or prohibiting printing to STDOUT.
*
* MUST BE CALLED FIRST THING IN MAIN
*
* @return    None
*
*****************************************************************************/
void board_init()
{
    // Assign the print function to the correct function pointer
    #ifdef DEBUG_VERSION
        zprintf = &printf;
    #else
        // Defined below this function
        zprintf = &noprintf;
    #endif
}

/*****************************************************************************/
/**
* @name noprintf
*
* simply returns with no actions performed
*
* @return   None
*
*****************************************************************************/
void noprintf(const char* c, ...)
{
    return;
}

লক্ষ্য করুন কিভাবে ফাংশন পয়েন্টার মধ্যে prototyped হয় version.hযেমন

void (* zprintf)(const char *, ...);

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

ইন version.c, ফাংশনটিতে নোটিশ দেওয়া হয়েছে board_init()যেখানে zprintfসংজ্ঞায়িত সংস্করণটির উপর নির্ভর করে একটি অনন্য ফাংশন (যার ফাংশন স্বাক্ষরের সাথে মেলে) নির্ধারিত হয়েছেversion.h

zprintf = &printf; zprintf ডিবাগিং উদ্দেশ্যে প্রিন্টফ কল করে

অথবা

zprintf = &noprint; zprintf কেবল ফিরে আসে এবং অপ্রয়োজনীয় কোড চালাবে না

কোডটি চালানো এইরকম দেখতে পাবেন:

mainProg.c

#include "version.h"
#include <stdlib.h>
int main()
{
    // Must run board_init(), which assigns the function
    // pointer to an actual function
    board_init();

    void *ptr = malloc(100); // Allocate 100 bytes of memory
    // malloc returns NULL if unable to allocate the memory.

    if (ptr == NULL)
    {
        zprintf("Unable to allocate memory\n");
        return 1;
    }

    // Other things to do...
    return 0;
}

উপরের কোডটি printfডিবাগ মোডে ব্যবহার করবে , বা মুক্তি মোডে থাকলে কিছুই করবে না। পুরো প্রকল্পের মধ্য দিয়ে যাওয়া এবং মন্তব্য করা বা কোড মোছার চেয়ে এটি অনেক সহজ। আমাকে যা করতে হবে তা হ'ল সংস্করণটি পরিবর্তন করা version.hএবং কোডটি বাকীটি করবে!


4
আপনি পারফরম্যান্সের অনেক সময় হারাতে পারেন। পরিবর্তে আপনি এমন ম্যাক্রো ব্যবহার করতে পারেন যা ডিবাগ / রিলিজের ভিত্তিতে কোডের একটি বিভাগ সক্ষম এবং অক্ষম করে।
আলফাগোকু

19

ফাংশন পয়েন্টার সাধারণত দ্বারা সংজ্ঞায়িত করা হয় typedef, এবং প্যারাম ও রিটার্ন মান হিসাবে ব্যবহৃত হয়।

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

#include <stdio.h>

#define NUM_A 1
#define NUM_B 2

// define a function pointer type
typedef int (*two_num_operation)(int, int);

// an actual standalone function
static int sum(int a, int b) {
    return a + b;
}

// use function pointer as param,
static int sum_via_pointer(int a, int b, two_num_operation funp) {
    return (*funp)(a, b);
}

// use function pointer as return value,
static two_num_operation get_sum_fun() {
    return &sum;
}

// test - use function pointer as variable,
void test_pointer_as_variable() {
    // create a pointer to function,
    two_num_operation sum_p = &sum;
    // call function via pointer
    printf("pointer as variable:\t %d + %d = %d\n", NUM_A, NUM_B, (*sum_p)(NUM_A, NUM_B));
}

// test - use function pointer as param,
void test_pointer_as_param() {
    printf("pointer as param:\t %d + %d = %d\n", NUM_A, NUM_B, sum_via_pointer(NUM_A, NUM_B, &sum));
}

// test - use function pointer as return value,
void test_pointer_as_return_value() {
    printf("pointer as return value:\t %d + %d = %d\n", NUM_A, NUM_B, (*get_sum_fun())(NUM_A, NUM_B));
}

int main() {
    test_pointer_as_variable();
    test_pointer_as_param();
    test_pointer_as_return_value();

    return 0;
}

14

সি এর ফাংশন পয়েন্টারগুলির জন্য বড় ব্যবহারগুলির মধ্যে একটি হ'ল রান-টাইমে নির্বাচিত কোনও ফাংশনকে কল করা। উদাহরণস্বরূপ, সি রান-টাইম লাইব্রেরিতে দুটি রুটিন রয়েছে qsortএবংbsearch একটি ফাংশনটিতে একটি পয়েন্টার নেয় যা বলা হয় দুটি আইটেম সাজানোর জন্য তুলনা করার জন্য; এটি আপনাকে ব্যবহার করতে ইচ্ছুক যে কোনও মানদণ্ডের ভিত্তিতে যথাক্রমে যে কোনও কিছুকে বাছাই বা অনুসন্ধান করতে দেয় allows

একটি খুব মৌলিক উদাহরণ, যদি এমন একটি ফাংশন বলা হয় print(int x, int y)যার পরিবর্তে কোনও ফাংশন কল করার প্রয়োজন হতে পারে (হয় add()বা sub()যা একই ধরণের হয়) তবে আমরা কী করব, আমরা ফাংশনে একটি ফাংশন পয়েন্টার যুক্তি যুক্ত করব print()যা নীচে দেখানো হয়েছে :

#include <stdio.h>

int add()
{
   return (100+10);
}

int sub()
{
   return (100-10);
}

void print(int x, int y, int (*func)())
{
    printf("value is: %d\n", (x+y+(*func)()));
}

int main()
{
    int x=100, y=200;
    print(x,y,add);
    print(x,y,sub);

    return 0;
}

আউটপুটটি হ'ল:

মানটি: 410
মানটি: 390


9

স্ক্র্যাচ ফাংশন থেকে শুরু করার কিছু স্মৃতি ঠিকানা রয়েছে সেখান থেকে তারা চালানো শুরু করে। এসেম্বলি ল্যাঙ্গুয়েজে তাদেরকে ডাকা হয় ("ফাংশনটির মেমরি ঠিকানা"))

1. প্রথমটি আপনাকে কাজ করতে পয়েন্টার ঘোষণা করতে হবে 2. পছন্দসই ফাংশনের ঠিকানাটি পাস করুন ass

**** দ্রষ্টব্য-> ফাংশনগুলি একই ধরণের হওয়া উচিত ****

এই সাধারণ প্রোগ্রামটি প্রতিটি বিষয়কে চিত্রিত করবে।

#include<stdio.h>
void (*print)() ;//Declare a  Function Pointers
void sayhello();//Declare The Function Whose Address is to be passed
                //The Functions should Be of Same Type
int main()
{
 print=sayhello;//Addressof sayhello is assigned to print
 print();//print Does A call To The Function 
 return 0;
}

void sayhello()
{
 printf("\n Hello World");
}

এখানে চিত্র বর্ণনা লিখুনএর পরে দেখা যাক যে মেশিন কীভাবে তাদের বোঝে .২ বিট আর্কিটেকচারে উপরের প্রোগ্রামটির মেশিন নির্দেশের ঝলক।

লাল চিহ্নের অঞ্চলটি দেখিয়ে দিচ্ছে যে ঠিকানায় কীভাবে আদান-প্রদান করা হচ্ছে এবং ইক্সে সংরক্ষণ করা হচ্ছে। তারপরে এগুলি হল ইক্সের কল নির্দেশনা। ইক্সে ফাংশনের কাঙ্ক্ষিত ঠিকানা রয়েছে।


8

একটি ফাংশন পয়েন্টার একটি ভেরিয়েবল যা কোনও ফাংশনের ঠিকানা রাখে। যেহেতু এটি একটি পয়েন্টার ভেরিয়েবল যদিও কিছু সীমাবদ্ধ বৈশিষ্ট্য সহ, আপনি এটির মতো ব্যবহার করতে পারেন যেমন আপনি ডাটা স্ট্রাকচারে অন্য কোনও পয়েন্টার ভেরিয়েবল চান।

একমাত্র ব্যতিক্রম সম্পর্কে আমি ভাবতে পারি যে ফাংশন পয়েন্টারটিকে একটি একক মান ব্যতীত অন্য কোনও কিছুর প্রতি নির্দেশ করে treat কোনও ফাংশন পয়েন্টার বৃদ্ধি বা হ্রাস করে পয়েন্টার গাণিতিক করা বা ফাংশন পয়েন্টারটিতে একটি অফসেট যোগ / বিয়োগ করে ফাংশন পয়েন্টার কেবল কোনও জিনিসকে নির্দেশ করে, কোনও ফাংশনের প্রবেশ বিন্দু হিসাবে কোনও কার্যকারিতা নয়।

কোনও ফাংশন পয়েন্টার ভেরিয়েবলের আকার, ভেরিয়েবল দ্বারা অধিগ্রহণ করা বাইটের সংখ্যা, অন্তর্নিহিত আর্কিটেকচারের উপর নির্ভর করে, যেমন x32 বা x64 বা যাই হোক না কেন পৃথক হতে পারে।

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

কিছু উদাহরণ:

int func (int a, char *pStr);    // declares a function

int (*pFunc)(int a, char *pStr);  // declares or defines a function pointer

int (*pFunc2) ();                 // declares or defines a function pointer, no parameter list specified.

int (*pFunc3) (void);             // declares or defines a function pointer, no arguments.

প্রথম দুটি ঘোষণাগুলি এতে কিছুটা সাদৃশ্যপূর্ণ:

  • funcএমন একটি ফাংশন যা একটি intএবং একটি নেয় char *এবং একটি প্রদান করেint
  • pFuncএটি একটি ফাংশন পয়েন্টার যা একটি ফাংশনটির ঠিকানা নির্ধারিত হয় যা একটি intএবং একটি নেয় char *এবং একটি প্রদান করেint

সুতরাং উপরের থেকে আমাদের একটি সোর্স লাইন থাকতে পারে যাতে ফাংশনটির ঠিকানাটি func()ফাংশন পয়েন্টার ভেরিয়েবলের pFuncহিসাবে নির্ধারিত হয় pFunc = func;

কোনও ফাংশন পয়েন্টার ঘোষণা / সংজ্ঞা সহ ব্যবহৃত সিনট্যাক্সটি লক্ষ্য করুন যাতে প্রাকৃতিক অপারেটর অগ্রাধিকার নিয়মগুলি কাটিয়ে উঠতে ব্যবহৃত প্রথম বন্ধনী ব্যবহার করা হয়।

int *pfunc(int a, char *pStr);    // declares a function that returns int pointer
int (*pFunc)(int a, char *pStr);  // declares a function pointer that returns an int

বিভিন্ন ব্যবহারের উদাহরণ

একটি ফাংশন পয়েন্টার ব্যবহারের কয়েকটি উদাহরণ:

int (*pFunc) (int a, char *pStr);    // declare a simple function pointer variable
int (*pFunc[55])(int a, char *pStr); // declare an array of 55 function pointers
int (**pFunc)(int a, char *pStr);    // declare a pointer to a function pointer variable
struct {                             // declare a struct that contains a function pointer
    int x22;
    int (*pFunc)(int a, char *pStr);
} thing = {0, func};                 // assign values to the struct variable
char * xF (int x, int (*p)(int a, char *pStr));  // declare a function that has a function pointer as an argument
char * (*pxF) (int x, int (*p)(int a, char *pStr));  // declare a function pointer that points to a function that has a function pointer as an argument

আপনি কোনও ফাংশন পয়েন্টারের সংজ্ঞাতে পরিবর্তনশীল দৈর্ঘ্যের পরামিতি তালিকা ব্যবহার করতে পারেন।

int sum (int a, int b, ...);
int (*psum)(int a, int b, ...);

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

int  sum ();      // nothing specified in the argument list so could be anything or nothing
int (*psum)();
int  sum2(void);  // void specified in the argument list so no parameters when calling this function
int (*psum2)(void);

সি স্টাইল কাস্ট

আপনি ফাংশন পয়েন্টার সহ সি স্টাইল কাস্ট ব্যবহার করতে পারেন। তবে সচেতন থাকুন যে কোনও সি সংকলক ত্রুটিগুলির চেয়ে চেক সম্পর্কে দুর্বল হতে পারে বা সতর্কতা প্রদান করতে পারে।

int sum (int a, char *b);
int (*psplsum) (int a, int b);
psplsum = sum;               // generates a compiler warning
psplsum = (int (*)(int a, int b)) sum;   // no compiler warning, cast to function pointer
psplsum = (int *(int a, int b)) sum;     // compiler error of bad cast generated, parenthesis are required.

সমতা ফাংশন পয়েন্টার তুলনা করুন

আপনি যাচাই করতে পারেন যে কোনও ফাংশন পয়েন্টারটি কোনও ifবিবৃতি ব্যবহার করে নির্দিষ্ট ফাংশনের ঠিকানার সমান, যদিও আমি নিশ্চিত না যে এটি কতটা কার্যকর। অন্যান্য তুলনা অপারেটরগুলির আরও কম উপযোগিতা রয়েছে বলে মনে হয়।

static int func1(int a, int b) {
    return a + b;
}

static int func2(int a, int b, char *c) {
    return c[0] + a + b;
}

static int func3(int a, int b, char *x) {
    return a + b;
}

static char *func4(int a, int b, char *c, int (*p)())
{
    if (p == func1) {
        p(a, b);
    }
    else if (p == func2) {
        p(a, b, c);      // warning C4047: '==': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
    } else if (p == func3) {
        p(a, b, c);
    }
    return c;
}

ফাংশন পয়েন্টারগুলির একটি অ্যারে

এবং যদি আপনি প্রতিটি ফাংশনের পয়েন্টারগুলির একটি অ্যারে রাখতে চান যার আর্গুমেন্ট তালিকার পার্থক্য রয়েছে তারপরে আপনি আর্গুমেন্ট তালিকাটি অনির্ধারিত ( voidযার অর্থ কোনও আর্গুমেন্ট নয় কেবল অনির্দিষ্ট নয়) দিয়ে একটি ফাংশন পয়েন্টার সংজ্ঞায়িত করতে পারেন যদিও আপনি নীচের মতো কিছু সি সংকলক থেকে সতর্কতা দেখতে পারে। এটি কোনও ফাংশনে ফাংশন পয়েন্টার প্যারামিটারের জন্যও কাজ করে:

int(*p[])() = {       // an array of function pointers
    func1, func2, func3
};
int(**pp)();          // a pointer to a function pointer


p[0](a, b);
p[1](a, b, 0);
p[2](a, b);      // oops, left off the last argument but it compiles anyway.

func4(a, b, 0, func1);
func4(a, b, 0, func2);  // warning C4047: 'function': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
func4(a, b, 0, func3);

    // iterate over the array elements using an array index
for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) {
    func4(a, b, 0, p[i]);
}
    // iterate over the array elements using a pointer
for (pp = p; pp < p + sizeof(p)/sizeof(p[0]); pp++) {
    (*pp)(a, b, 0);          // pointer to a function pointer so must dereference it.
    func4(a, b, 0, *pp);     // pointer to a function pointer so must dereference it.
}

সি স্টাইল ফাংশন পয়েন্টার সহ namespaceগ্লোবাল ব্যবহার করেstruct

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

একটি শিরোনাম ফাইলটিতে এমন একটি কাঠামো সংজ্ঞায়িত করুন যা এটি ব্যবহার করে এমন একটি বৈশ্বিক চলক সহ আমাদের নামস্থান হবে sp

typedef struct {
   int (*func1) (int a, int b);             // pointer to function that returns an int
   char *(*func2) (int a, int b, char *c);  // pointer to function that returns a pointer
} FuncThings;

extern const FuncThings FuncThingsGlobal;

তারপরে সি উত্স ফাইলটিতে:

#include "header.h"

// the function names used with these static functions do not need to be the
// same as the struct member names. It's just helpful if they are when trying
// to search for them.
// the static keyword ensures these names are file scope only and not visible
// outside of the file.
static int func1 (int a, int b)
{
    return a + b;
}

static char *func2 (int a, int b, char *c)
{
    c[0] = a % 100; c[1] = b % 50;
    return c;
}

const FuncThings FuncThingsGlobal = {func1, func2};

এটি তখন গ্লোবাল স্ট্রাক্ট ভেরিয়েবলের সম্পূর্ণ নাম এবং ফাংশন অ্যাক্সেস করার জন্য সদস্যের নাম নির্দিষ্ট করে ব্যবহার করা হবে। constপরিবর্তক বিশ্বব্যাপী তাই ব্যবহার করা হয় এটি দুর্ঘটনা দ্বারা পরিবর্তন করা যাবে না।

int abcd = FuncThingsGlobal.func1 (a, b);

ফাংশন পয়েন্টারগুলির প্রয়োগের অঞ্চল

একটি ডিএলএল লাইব্রেরি উপাদান সি স্টাইল namespaceপদ্ধতির অনুরূপ কিছু করতে পারে যাতে একটি লাইব্রেরি ইন্টারফেসের একটি ফ্যাক্টরি পদ্ধতি থেকে একটি নির্দিষ্ট লাইব্রেরি ইন্টারফেসের জন্য অনুরোধ করা হয় যা একটি structফাংশন পয়েন্টার যুক্ত সমন্বিত করে .. এই লাইব্রেরি ইন্টারফেসটি অনুরোধ করা ডিএলএল সংস্করণ লোড করে, তৈরি করে প্রয়োজনীয় ফাংশন পয়েন্টার সহ একটি স্ট্রাক্ট, এবং তারপরে স্ট্রাক্টটি ব্যবহারের জন্য অনুরোধকারী কলারের কাছে ফেরত দেয়।

typedef struct {
    HMODULE  hModule;
    int (*Func1)();
    int (*Func2)();
    int(*Func3)(int a, int b);
} LibraryFuncStruct;

int  LoadLibraryFunc LPCTSTR  dllFileName, LibraryFuncStruct *pStruct)
{
    int  retStatus = 0;   // default is an error detected

    pStruct->hModule = LoadLibrary (dllFileName);
    if (pStruct->hModule) {
        pStruct->Func1 = (int (*)()) GetProcAddress (pStruct->hModule, "Func1");
        pStruct->Func2 = (int (*)()) GetProcAddress (pStruct->hModule, "Func2");
        pStruct->Func3 = (int (*)(int a, int b)) GetProcAddress(pStruct->hModule, "Func3");
        retStatus = 1;
    }

    return retStatus;
}

void FreeLibraryFunc (LibraryFuncStruct *pStruct)
{
    if (pStruct->hModule) FreeLibrary (pStruct->hModule);
    pStruct->hModule = 0;
}

এবং এটি হিসাবে ব্যবহার করা যেতে পারে:

LibraryFuncStruct myLib = {0};
LoadLibraryFunc (L"library.dll", &myLib);
//  ....
myLib.Func1();
//  ....
FreeLibraryFunc (&myLib);

অন্তর্নিহিত হার্ডওয়্যারটির একটি নির্দিষ্ট মডেল ব্যবহার করে এমন কোডের জন্য একটি বিমূর্ত হার্ডওয়্যার স্তর সংজ্ঞায়িত করতে একই পন্থা ব্যবহার করা যেতে পারে। বিমূর্ত হার্ডওয়্যার মডেলটিতে নির্দিষ্ট ফাংশনগুলি প্রয়োগ করে এমন হার্ডওয়্যার নির্দিষ্ট কার্যকারিতা সরবরাহ করার জন্য একটি কারখানার দ্বারা ফাংশন পয়েন্টারগুলিতে হার্ডওয়্যার নির্দিষ্ট ফাংশনগুলি পূরণ করা হয়। এটি নির্দিষ্ট সফ্টওয়্যার দ্বারা ব্যবহৃত একটি বিমূর্ত হার্ডওয়্যার স্তর সরবরাহ করতে ব্যবহার করা যেতে পারে যা নির্দিষ্ট হার্ডওয়্যার ফাংশন ইন্টারফেস পাওয়ার জন্য কারখানার ফাংশন কল করে তার পরে নির্দিষ্ট টার্গেট সম্পর্কে বাস্তবায়ন বিশদটি জানতে প্রয়োজন ছাড়াই অন্তর্নিহিত হার্ডওয়্যারের জন্য ক্রিয়া সম্পাদনের জন্য সরবরাহ করা ফাংশন পয়েন্টার ব্যবহার করে ।

প্রতিনিধি, হ্যান্ডলার এবং কলব্যাকগুলি তৈরি করতে ফাংশন পয়েন্টার

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

অন্য ব্যবহারটি সি ++ স্ট্যান্ডার্ড টেম্পলেট লাইব্রেরী ধারকটিতে একটি অ্যালগরিদম প্রয়োগ করার মতো।

void * ApplyAlgorithm (void *pArray, size_t sizeItem, size_t nItems, int (*p)(void *)) {
    unsigned char *pList = pArray;
    unsigned char *pListEnd = pList + nItems * sizeItem;
    for ( ; pList < pListEnd; pList += sizeItem) {
        p (pList);
    }

    return pArray;
}

int pIncrement(int *pI) {
    (*pI)++;

    return 1;
}

void * ApplyFold(void *pArray, size_t sizeItem, size_t nItems, void * pResult, int(*p)(void *, void *)) {
    unsigned char *pList = pArray;
    unsigned char *pListEnd = pList + nItems * sizeItem;
    for (; pList < pListEnd; pList += sizeItem) {
        p(pList, pResult);
    }

    return pArray;
}

int pSummation(int *pI, int *pSum) {
    (*pSum) += *pI;

    return 1;
}

// source code and then lets use our function.
int intList[30] = { 0 }, iSum = 0;

ApplyAlgorithm(intList, sizeof(int), sizeof(intList) / sizeof(intList[0]), pIncrement);
ApplyFold(intList, sizeof(int), sizeof(intList) / sizeof(intList[0]), &iSum, pSummation);

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

কলব্যাকের জন্য অ্যাসিঙ্ক্রোনাস ফাংশন ইভেন্ট হ্যান্ডলারের অনুরূপ। অ্যাসিনক্রোনাস ফাংশনের ব্যবহারকারী অ্যাসিঙ্ক্রোনাস ফাংশনটিকে কিছু ক্রিয়াকলাপ শুরু করার জন্য কল করে এবং একটি ফাংশন পয়েন্টার সরবরাহ করে যা অ্যাসিঙ্ক্রোনাস ফাংশন কল হয়ে গেলে ক্রিয়া শেষ হয়ে যায়। এই ক্ষেত্রে ইভেন্টটি হ'ল অ্যাসিনক্রোনাস ফাংশনটি তার কাজ শেষ করে।


0

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

সি একই সাথে বেশ চঞ্চল এবং ক্ষমাশীল :)

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