সি-তে ফাংশন পয়েন্টার নিয়ে ইদানীং আমার কিছু অভিজ্ঞতা হয়েছিল
সুতরাং আপনার নিজের প্রশ্নের উত্তর দেওয়ার traditionতিহ্য ধরে রেখে, আমি সিদ্ধান্ত নিয়েছি যে এই বিষয়টিতে দ্রুত ডাইভ-ইন প্রয়োজন তাদের জন্য খুব বেসিকগুলির একটি ছোট সংক্ষিপ্তসার তৈরি করব।
সি-তে ফাংশন পয়েন্টার নিয়ে ইদানীং আমার কিছু অভিজ্ঞতা হয়েছিল
সুতরাং আপনার নিজের প্রশ্নের উত্তর দেওয়ার traditionতিহ্য ধরে রেখে, আমি সিদ্ধান্ত নিয়েছি যে এই বিষয়টিতে দ্রুত ডাইভ-ইন প্রয়োজন তাদের জন্য খুব বেসিকগুলির একটি ছোট সংক্ষিপ্তসার তৈরি করব।
উত্তর:
আসুন একটি প্রাথমিক ফাংশন দিয়ে শুরু করি যা আমরা নির্দেশ করব :
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;
}
pshufb
এটি ধীর গতির, সুতরাং আগের বাস্তবায়ন এখনও আরও দ্রুত। x264 / x265 এটিকে ব্যাপকভাবে ব্যবহার করুন এবং এটি ওপেন সোর্স।
সি-তে ফাংশন পয়েন্টারগুলি সি-তে অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং করতে ব্যবহৃত হতে পারে
উদাহরণস্বরূপ, নিম্নলিখিত লাইনগুলি সিতে লিখিত হয়েছে:
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
ফিরে আসার পদ্ধতির আচরণটি পরিবর্তন করতে চেয়েছিলাম তবে যা করতে হবে তা হ'ল:0
ImmutableString
length
পদ্ধতি হিসাবে পরিবেশন করতে যাচ্ছে এমন একটি ফাংশন যুক্ত করুন ।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
পদ্ধতির জন্য অভিন্ন আচরণ করার পরিবর্তে এখন পদ্ধতিটি ফাংশনে সংজ্ঞায়িত আচরণকে নির্দেশ করবে ।ImmutableString
String
length
lengthOverrideMethod
আমার অবশ্যই একটি দাবি অস্বীকার করতে হবে যা আমি এখনও সিতে কোনও অবজেক্ট-ভিত্তিক প্রোগ্রামিং স্টাইল দিয়ে কীভাবে লিখতে শিখছি, তাই সম্ভবত সেখানে এমন পয়েন্ট রয়েছে যা আমি ভালভাবে ব্যাখ্যা করিনি, বা ওওপি বাস্তবায়নের ক্ষেত্রে সবচেয়ে ভালভাবে কীভাবে চিহ্নিত হতে পারে? সি তে তবে আমার উদ্দেশ্যটি ছিল ফাংশন পয়েন্টারগুলির অনেকগুলি ব্যবহারের মধ্যে একটি বর্ণনা করার চেষ্টা করা।
সি তে কীভাবে অবজেক্ট ভিত্তিক প্রোগ্রামিং করবেন সে সম্পর্কে আরও তথ্যের জন্য, দয়া করে নীচের প্রশ্নগুলি দেখুন:
ClassName_methodName
ফাংশন নামকরণের কনভেনশনে লেগে থাকে। তবেই আপনি সি ++ এবং পাসকালে যেমন করেন তেমন রানটাইম এবং স্টোরেজ ব্যয় পাবেন।
বরখাস্ত হওয়ার গাইড: আপনার কোডটি হাতে করে সংকলন করে x86 মেশিনে জিসিসিতে ফাংশন পয়েন্টারগুলিকে কীভাবে আপত্তি জানাতে হবে:
এই স্ট্রিং লিটারালগুলি 32-বিট x86 মেশিন কোডের বাইট। 0xC3
এটি একটি x86 ret
নির্দেশ ।
আপনি সাধারণত এগুলি হাত দিয়ে লিখবেন না, আপনি সমাবেশের ভাষায় লিখবেন এবং তারপরে কোনও nasm
এসেম্বলারকে এটি ফ্ল্যাট বাইনারিতে জড়ো করতে পছন্দ করুন যা আপনি সি স্ট্রিং আক্ষরিক হিসাবে হেক্সডাম্প করে।
EAX রেজিস্টারটিতে বর্তমান মান প্রদান করে
int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
একটি অদলবদল ফাংশন লিখুন
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);
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
আপনি এমনকি 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 সমস্ত সাধারণ কলিং কনভেনশনে কল-সংরক্ষিত থাকে, তাই এটি সংরক্ষণ / পুনরুদ্ধার না করে স্ক্র্যাচ রেজিস্ট্রার হিসাবে ব্যবহার করা কলার ক্রাশকে সহজেই পরিণত করতে পারে।
ফাংশন পয়েন্টারগুলির জন্য আমার প্রিয় ব্যবহারগুলির মধ্যে একটি সস্তা এবং সহজ পুনরাবৃত্তি -
#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);
}
int (*cb)(void *arg, ...)
। পুনরুদ্ধারের ফেরতের মানটি আমাকে তাড়াতাড়ি থামাতে দেয় (যদি ননজারো হয়)।
ফাংশন পয়েন্টারগুলি একবার আপনার কাছে প্রাথমিক ডিক্লেয়ারের পরে ঘোষণা করা সহজ হয়ে যায়:
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 অপারেটরগুলি সনাক্তকারী ব্যবহার করে যদি কোনও অভিব্যক্তিতে ব্যবহার করা হয়, তবে এটি খুব বাম দিকে টাইপ দেয়। এটি অ্যারের জন্যও এর মতো।
আশা করি আপনি এই ছোট টিউটোরিয়ালটি পছন্দ করেছেন! লোকেরা যখন ফাংশনগুলির অদ্ভুত ঘোষণা সিনট্যাক্স সম্পর্কে অবাক হয় তখন আমরা এর সাথে লিঙ্ক করতে পারি। আমি যতটা সম্ভব কম সি ইন্টার্নাল দেওয়ার চেষ্টা করেছি। এতে কোনও জিনিস সম্পাদনা / ঠিক করতে নির্দ্বিধায় হন।
আপনি বিভিন্ন সময়ে বিভিন্ন সময়ে বা বিকাশের বিভিন্ন ধাপে যখন চান তখন এগুলি ব্যবহার করতে খুব সহজ're উদাহরণস্বরূপ, আমি একটি হোস্ট কম্পিউটারে একটি অ্যাপ্লিকেশন বিকাশ করছি যার একটি কনসোল রয়েছে তবে সফ্টওয়্যারটির চূড়ান্ত প্রকাশটি একটি অ্যাভনেট জেডবোর্ডে লাগানো হবে (এতে প্রদর্শন এবং কনসোলের জন্য পোর্ট রয়েছে, তবে তাদের প্রয়োজন নেই / চেয়েছিল চূড়ান্ত মুক্তি)। সুতরাং বিকাশের সময়, আমি printf
স্থিতি এবং ত্রুটি বার্তাগুলি দেখতে ব্যবহার করব , তবে আমার কাজ শেষ হয়ে গেলে, আমি কোনও মুদ্রিত চাই না। আমি যা করেছি তা এখানে:
// 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
#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 কেবল ফিরে আসে এবং অপ্রয়োজনীয় কোড চালাবে না
কোডটি চালানো এইরকম দেখতে পাবেন:
#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
এবং কোডটি বাকীটি করবে!
ফাংশন পয়েন্টার সাধারণত দ্বারা সংজ্ঞায়িত করা হয় 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 ∑
}
// test - use function pointer as variable,
void test_pointer_as_variable() {
// create a pointer to function,
two_num_operation sum_p = ∑
// 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;
}
সি এর ফাংশন পয়েন্টারগুলির জন্য বড় ব্যবহারগুলির মধ্যে একটি হ'ল রান-টাইমে নির্বাচিত কোনও ফাংশনকে কল করা। উদাহরণস্বরূপ, সি রান-টাইম লাইব্রেরিতে দুটি রুটিন রয়েছে 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
স্ক্র্যাচ ফাংশন থেকে শুরু করার কিছু স্মৃতি ঠিকানা রয়েছে সেখান থেকে তারা চালানো শুরু করে। এসেম্বলি ল্যাঙ্গুয়েজে তাদেরকে ডাকা হয় ("ফাংশনটির মেমরি ঠিকানা"))
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");
}
এর পরে দেখা যাক যে মেশিন কীভাবে তাদের বোঝে .২ বিট আর্কিটেকচারে উপরের প্রোগ্রামটির মেশিন নির্দেশের ঝলক।
লাল চিহ্নের অঞ্চলটি দেখিয়ে দিচ্ছে যে ঠিকানায় কীভাবে আদান-প্রদান করা হচ্ছে এবং ইক্সে সংরক্ষণ করা হচ্ছে। তারপরে এগুলি হল ইক্সের কল নির্দেশনা। ইক্সে ফাংশনের কাঙ্ক্ষিত ঠিকানা রয়েছে।
একটি ফাংশন পয়েন্টার একটি ভেরিয়েবল যা কোনও ফাংশনের ঠিকানা রাখে। যেহেতু এটি একটি পয়েন্টার ভেরিয়েবল যদিও কিছু সীমাবদ্ধ বৈশিষ্ট্য সহ, আপনি এটির মতো ব্যবহার করতে পারেন যেমন আপনি ডাটা স্ট্রাকচারে অন্য কোনও পয়েন্টার ভেরিয়েবল চান।
একমাত্র ব্যতিক্রম সম্পর্কে আমি ভাবতে পারি যে ফাংশন পয়েন্টারটিকে একটি একক মান ব্যতীত অন্য কোনও কিছুর প্রতি নির্দেশ করে 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);
অন্য একটি উদাহরণ জিইউআই সোর্স কোডের সাথে রয়েছে যেখানে কোনও নির্দিষ্ট ইভেন্টের জন্য কোনও হ্যান্ডলার একটি ফাংশন পয়েন্টার সরবরাহ করে নিবন্ধিত হয় যা ঘটনাটি ঘটে আসলে বলা হয়। মাইক্রোসফ্ট এমএফসি এর কাঠামো এর বার্তা মানচিত্রগুলি উইন্ডো বা থ্রেডে বিতরণ করা উইন্ডোজ বার্তাগুলি পরিচালনা করতে অনুরূপ কিছু ব্যবহার করে।
কলব্যাকের জন্য অ্যাসিঙ্ক্রোনাস ফাংশন ইভেন্ট হ্যান্ডলারের অনুরূপ। অ্যাসিনক্রোনাস ফাংশনের ব্যবহারকারী অ্যাসিঙ্ক্রোনাস ফাংশনটিকে কিছু ক্রিয়াকলাপ শুরু করার জন্য কল করে এবং একটি ফাংশন পয়েন্টার সরবরাহ করে যা অ্যাসিঙ্ক্রোনাস ফাংশন কল হয়ে গেলে ক্রিয়া শেষ হয়ে যায়। এই ক্ষেত্রে ইভেন্টটি হ'ল অ্যাসিনক্রোনাস ফাংশনটি তার কাজ শেষ করে।
যেহেতু ফাংশন পয়েন্টারগুলি প্রায়শই কলব্যাক টাইপ করা হয়, আপনি এটি একবার দেখতে চান নিরাপদ কলব্যাকগুলি টাইপ । এটি কলব্যাক নয় এমন ফাংশনগুলির এন্ট্রি পয়েন্ট ইত্যাদির ক্ষেত্রেও একই প্রযোজ্য।
সি একই সাথে বেশ চঞ্চল এবং ক্ষমাশীল :)