සී හි ක්රියාකාරී දර්ශකයන් සමඟ මට මෑතකදී යම් අත්දැකීමක් තිබුණි.
එබැවින් ඔබේම ප්රශ්නවලට පිළිතුරු සැපයීමේ සම්ප්රදාය අනුව යමින්, විෂයයට ඉක්මන් කිමිදීමක් අවශ්ය අය සඳහා, මූලික කරුණු පිළිබඳ කුඩා සාරාංශයක් කිරීමට මම තීරණය කළෙමි.
සී හි ක්රියාකාරී දර්ශකයන් සමඟ මට මෑතකදී යම් අත්දැකීමක් තිබුණි.
එබැවින් ඔබේම ප්රශ්නවලට පිළිතුරු සැපයීමේ සම්ප්රදාය අනුව යමින්, විෂයයට ඉක්මන් කිමිදීමක් අවශ්ය අය සඳහා, මූලික කරුණු පිළිබඳ කුඩා සාරාංශයක් කිරීමට මම තීරණය කළෙමි.
Answers:
අපි පෙන්වා දෙන මූලික ශ්රිතයකින් පටන් ගනිමු :
int addInt(int n, int m) {
return n+m;
}
පළමුවෙන්ම, 2 int
s ලබාගෙන නැවත ලබා දෙන ශ්රිතයකට දර්ශකයක් අර්ථ දක්වමු 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 මෙය පුළුල් ලෙස භාවිතා කරන අතර විවෘත මූලාශ්ර වේ.
C හි වස්තු-නැඹුරු වැඩසටහන්කරණය සිදු කිරීම සඳහා C හි ක්රියාකාරී දර්ශක භාවිතා කළ හැකිය.
උදාහරණයක් ලෙස, පහත දැක්වෙන රේඛා C හි ලියා ඇත:
String s1 = newString();
s1->set(s1, "hello");
ඔව්, ->
සහ new
ක්රියාකරුගේ lack නතාවය මළ දීමකි, නමුත් එය නිසැකවම අප යම් String
පන්තියක පෙළ සැකසෙන බව අඟවයි "hello"
.
ශ්රිත දර්ශක භාවිතා කිරීමෙන්, C හි ක්රම අනුකරණය කළ හැකිය .
මෙය සිදු කරන්නේ කෙසේද?
මෙම 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
ක්රමවේදය සඳහා වන ප්රකාශය නොපවතී, එබැවින් එය a 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
සී හි වස්තු-නැඹුරුවන ක්රමලේඛ ශෛලියක් සමඟ ලිවීමට මම තවමත් ඉගෙන ගනිමින් සිටින බව මම ප්රතික්ෂේප කිරීමක් එකතු කළ යුතුය, එබැවින් බොහෝ විට මා හොඳින් පැහැදිලි නොකළ කරුණු තිබිය හැකිය, නැතහොත් ඕඕපී ක්රියාවට නැංවිය හැකි හොඳම ආකාරය අනුව ලකුණු රහිත විය හැකිය. සී හි. නමුත් මගේ අරමුණ වූයේ ශ්රිත දර්ශකයන්ගේ බොහෝ භාවිතයන්ගෙන් එකක් නිදර්ශනය කිරීමට උත්සාහ කිරීමයි.
C හි වස්තු-නැඹුරු වැඩසටහන්කරණය කරන්නේ කෙසේද යන්න පිළිබඳ වැඩි විස්තර සඳහා කරුණාකර පහත සඳහන් ප්රශ්න වෙත යොමු වන්න:
ClassName_methodName
කාර්යයන් නම් කිරීමේ සම්මුතියට ඇලී සිටීම . එවිට පමණක් ඔබට C ++ සහ පැස්කල් හි සිදු වන ආකාරයටම ධාවන කාලය සහ ගබඩා පිරිවැය ලැබේ.
සේවයෙන් පහ කිරීමට මාර්ගෝපදේශය: ඔබේ කේතය අතින් සම්පාදනය කිරීමෙන් x86 යන්ත්රවල GCC හි ක්රියාකාරී දර්ශකයන් අනිසි ලෙස භාවිතා කරන්නේ කෙසේද:
මෙම වචන වචනාර්ථය 32-බිට් x86 යන්ත්ර කේතයේ බයිට් වේ. 0xC3
ය , x86 ret
උපදෙස් .
ඔබ සාමාන්යයෙන් මේවා අතින් ලියන්නේ නැත, ඔබ එකලස් කිරීමේ භාෂාවෙන් ලියන අතර පසුව nasm
එය පැතලි ද්විමය තුළට එක්රැස් කිරීමට කැමති එකලස් කරන්නෙකු භාවිතා කරන්න .
EAX ලේඛනයේ වත්මන් අගය ලබා දෙයි
int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
Swap ශ්රිතයක් ලියන්න
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
වින්ඩෝස් මත) ස්ථානගත කර ඇති බව සලකන්න , එය පෙළ කොටසේ කොටසක් ලෙස සම්බන්ධ කර ඇත (කාර්යයන් සඳහා කේතය සමඟ).
පෙළ කොටසට Read + Exec අවසරය ඇත, එබැවින් ක්රියාකාරී දර්ශකයන්ට වචන ලිවීම අවශ්ය නොවී ක්රියා කරයි 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
, අපට එකලස් කිරීම ලබා ගත හැකි අතර, මෙම කේතය ඊබීඑක්ස් (ඇමතුම් සංරක්ෂිත ලේඛනයක්) ක්ලෝබර් කිරීම මගින් ඒබීඅයි උල්ලං lates නය කරන බවත් සාමාන්යයෙන් අකාර්යක්ෂම බවත් සොයා ගනී.
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 ඇමතුම් ආරක්ෂා කර ඇති බැවින් එය සුරැකීම / ප්රතිෂ් oring ාපනය නොකර සීරීම් ලේඛනයක් ලෙස භාවිතා කිරීමෙන් ඇමතුම බිඳ වැටෙනු ඇත.
ක්රියාකාරී දර්ශකයන් සඳහා මගේ ප්රියතම භාවිතයක් වන්නේ ලාභ සහ පහසු ක්රියාකාරක වේ -
#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, ...)
. පුනරාවර්තකයේ ප්රතිලාභ අගය ද මට කලින් නැවැත්වීමට ඉඩ දෙයි (nonzero නම්).
ඔබට මූලික ප්රකාශකයන් සිටින විට ක්රියාකාරී දර්ශකයන් ප්රකාශ කිරීම පහසු වේ:
ID
: ID එක, ය*D
: D හේතුවමD(<parameters>)
: D කාර්යය ගනිමින් <
පරාමිතීන් >
නැවතD යනු එම නීති රීති භාවිතා කරමින් ගොඩනඟන ලද තවත් ප්රකාශකයෙකි. අවසානයේදී, කොතැනක හෝ එය අවසන් වේ ID
(නිදසුනක් සඳහා පහත බලන්න), එය ප්රකාශිත වස්තුවේ නමයි. කිසිවක් නොගෙන නැවත int වෙත ශ්රිතයක් වෙත දර්ශකයක් ගෙන ශ්රිතයක් ගොඩනඟා ගැනීමට අපි උත්සාහ කරමු. Type-defs සමඟ එය මේ වගේ ය
typedef int ReturnFunction(char);
typedef int ParameterFunction(void);
ReturnFunction *f(ParameterFunction *p);
ඔබ දකින පරිදි, යතුරු ලියනය භාවිතයෙන් එය ගොඩනගා ගැනීම පහසුය. යතුරු ලියනයකින් තොරව, ඉහත ප්රකාශක නීති රීති සමඟ අනුකූල නොවේ. ඔබ දකින පරිදි, දර්ශකය පෙන්වා දෙන කොටස සහ ක්රියාකාරීත්වය නැවත ලැබෙන දෙය මට මග හැරුණි. ප්රකාශයේ වම්පස පෙනෙන දේ එයයි, එය උනන්දුවක් නොදක්වයි: දැනටමත් ප්රකාශකයා ගොඩනඟා ඇත්නම් එය අවසානයේ එකතු වේ. අපි ඒක කරමු. එය නිරන්තරයෙන් ගොඩනඟා ගැනීම, පළමු වචනාර්ථය - භාවිතා කරමින් ව්යුහය පෙන්වීම [
සහ ]
:
function taking
[pointer to [function taking [void] returning [int]]]
returning
[pointer to [function taking [char] returning [int]]]
ඔබ දකින පරිදි, ප්රකාශකයන් එකින් එක එකතු කිරීමෙන් කෙනෙකුට වර්ගයක් සම්පූර්ණයෙන්ම විස්තර කළ හැකිය. ඉදිකිරීම් ආකාර දෙකකින් කළ හැකිය. එකක් පහළ සිට ඉහළට, ඉතා නිවැරදි දේ (කොළ) වලින් ආරම්භ කර හඳුනාගැනීමේ යන්ත්රය දක්වා ක්රියා කරයි. අනෙක් ක්රමය වන්නේ ඉහළ සිට පහළට, හඳුනාගැනීමෙන් ආරම්භ වී කොළ දක්වා පහළට වැඩ කිරීමයි. මම ක්රම දෙකම පෙන්වන්නම්.
ඉදිකිරීම් ආරම්භ වන්නේ දකුණු පස ඇති දෙයෙනි: ආපසු ලබා දුන් දෙය, එය වර්ග ලබා ගැනීමේ ශ්රිතයයි. ප්රකාශකයින් පැහැදිලිව තබා ගැනීම සඳහා, මම ඔවුන් අංකනය කිරීමට යන්නේ:
D1(char);
එය ඉතා සුළු බැවින් වර්ග පරාමිතිය කෙලින්ම ඇතුළත් කරන්න. ප්රතිස්ථාපනය D1
කිරීමෙන් ප්රකාශකයාට දර්ශකයක් එක් කිරීම *D2
. අපි වරහන් වර්ග වටා ඔතා තිබිය යුතු බව සලකන්න *D2
. *-operator
සහ ක්රියාකාරී ඇමතුම් ක්රියාකරුගේ ප්රමුඛතාවය සොයා බැලීමෙන් එය දැනගත හැකිය ()
. අපගේ වරහන් නොමැතිව, සම්පාදකයා එය කියවනු ඇත *(D2(char p))
. නමුත් එය *D2
තවදුරටත් D1 වෙනුවට ආදේශ කිරීමක් නොවනු ඇත . වරහන් වර්ග සෑම විටම ප්රකාශකයන් වටා අවසර දෙනු ලැබේ. එබැවින් ඔබ ඒවායින් වැඩි ප්රමාණයක් එකතු කළහොත් ඔබ කිසි වරදක් නොකරන්න.
(*D2)(char);
ආපසු පැමිණීමේ වර්ගය සම්පූර්ණයි! දැන්, අපි වෙනුවට ඉඩ D2
උත්සවයට declarator විසින් ගෙන කාර්යය <parameters>
නැවත වන, D3(<parameters>)
අපි දැන් වන.
(*D3(<parameters>))(char)
වර්ගයන් අවශ්ය නොවන බව සලකන්න, මන්ද අපට අවශ්ය D3
වන්නේ ක්රියාකාරී ප්රකාශකයෙකු වීමට මිස දර්ශක ප්රකාශකයෙකු වීමට නොවේ. නියමයි, ඉතිරිව ඇත්තේ ඒ සඳහා වන පරාමිතීන් පමණි. පරාමිතිය අපි සමග, නැවත වර්ගය කරලා තියෙන්නෙ ලෙස හරියටම එම සිදු char
වෙනුවට void
. එබැවින් මම එය පිටපත් කරමි:
(*D3( (*ID1)(void)))(char)
මම එම පරාමිතිය සමඟ අවසන් කර ඇති බැවින් මම එය ප්රතිස්ථාපනය D2
කර ඇත්තෙමි ID1
(එය දැනටමත් ශ්රිතයක් සඳහා දර්ශකයක් - වෙනත් ප්රකාශකයෙකු අවශ්ය නොවේ). ID1
පරාමිතියේ නම වනු ඇත. දැන්, මම ඉහත කී පරිදි අවසානයේ එක් ප්රකාශකයෙකු වෙනස් කරන වර්ගය එක් කරයි - සෑම ප්රකාශයකම වම්පස දිස්වන එක. කාර්යයන් සඳහා, එය ආපසු එන වර්ගය බවට පත්වේ. යනාදිය ටයිප් කිරීමට යොමු කර ඇති කරුණු සඳහා ... වර්ගය ලිවීමේදී එය සිත්ගන්නා සුළුය, එය ප්රතිවිරුද්ධ අනුපිළිවෙලින් දිස්වනු ඇත, හරි දකුණේ :) කෙසේ වෙතත්, එය ආදේශ කිරීමෙන් සම්පූර්ණ ප්රකාශය ලැබේ. 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
අපි පහළ සිට පහළට කළාක් මෙන් ප්රකාශකයන් ඉදිරියේ තබන්න , අපි අවසන්
int (*ID0(int (*ID1)(void)))(char)
පහළට හෝ ඉහළට පහළට වඩා හොඳද? මම පහළට පහළට පුරුදු වී සිටිමි, නමුත් සමහර අය ඉහළට පහළට වඩා පහසු විය හැකිය. එය මා සිතන රසයේ කාරණයක්. අහම්බෙන්, ඔබ එම ප්රකාශයේ සියලුම ක්රියාකරුවන් යොදන්නේ නම්, ඔබට int එකක් ලැබෙනු ඇත:
int v = (*ID0(some_function_pointer))(some_char);
එය සී හි ප්රකාශනවල හොඳ ගුණාංගයකි: ප්රකාශය මගින් කියා සිටින්නේ එම ක්රියාකරුවන් හඳුනාගැනීමක් භාවිතා කරමින් ප්රකාශනයක භාවිතා කරන්නේ නම් එය වම්පස වර්ගය ලබා දෙන බවයි. අරා සඳහා ද එය එසේ ය.
ඔබ මෙම කුඩා නිබන්ධනයට කැමති යැයි සිතමි! ශ්රිතවල අමුතු ප්රකාශන වාක්ය ඛණ්ඩය ගැන මිනිසුන් කල්පනා කරන විට දැන් අපට මේ හා සම්බන්ධ විය හැකිය. මම හැකි තරම් කුඩා C අභ්යන්තරයන් දැමීමට උත්සාහ කළෙමි. එහි ඇති දේවල් සංස්කරණය කිරීමට / නිවැරදි කිරීමට නිදහස් වන්න.
ඔබට විවිධ වේලාවන්හි විවිධ කාර්යයන් හෝ සංවර්ධනයේ විවිධ අවධීන් අවශ්ය විට ඒවා භාවිතා කිරීම ඉතා පහසුය. උදාහරණයක් ලෙස, මම කොන්සෝලයක් ඇති ධාරක පරිගණකයක යෙදුමක් සංවර්ධනය කරමින් සිටිමි, නමුත් මෘදුකාංගයේ අවසාන නිකුතුව අව්නෙට් සෙඩ්බෝඩ් එකක තබනු ඇත (එහි දර්ශන සහ කොන්සෝල සඳහා වරායන් ඇත, නමුත් ඒවා අවශ්ය නොවේ / අවශ්ය නොවේ අවසාන නිකුතුව). එබැවින් සංවර්ධනයේදී, 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;
}
ශ්රිත දර්ශකය මූලාකෘතිගත කර ඇති version.h
ආකාරය සැලකිල්ලට ගන්න
void (* zprintf)(const char *, ...);
යෙදුමෙහි එය සඳහන් කළ විට, එය යොමු කරන ඕනෑම තැනක එය ක්රියාත්මක කිරීමට පටන් ගනී, එය තවමත් අර්ථ දක්වා නොමැත.
දී version.c
දී දැනුම්, board_init()
එහිදී උත්සවය zprintf
අර්ථ දක්වා ඇත බව අනුවාදය මත පදනම්ව (එහි කාර්යය අත්සන තරග) සුවිශේෂී කාර්යය පැවරී ඇතversion.h
zprintf = &printf;
නිදොස් කිරීමේ අරමුණු සඳහා zprintf printf අමතයි
හෝ
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;
}
C හි ශ්රිත දර්ශක සඳහා විශාල භාවිතයක් වන්නේ ධාවන වේලාවේදී තෝරාගත් ශ්රිතයක් ඇමතීමයි. උදාහරණයක් ලෙස, සී ලකුණු කාලීන පුස්තකාල දෙකක් චර්යාවන්, ඇත qsort
හා bsearch
හිඳගෙන, ලබන භාණ්ඩ දෙකක් සන්සන්දනය කිරීම ලෙස හැඳින්වේ කරන කාර්යය කිරීමට අවධානය යොමුකළ ගත කරන,; ඔබ භාවිතා කිරීමට කැමති ඕනෑම නිර්ණායකයක් මත පදනම්ව පිළිවෙලින් ඕනෑම දෙයක් වර්ග කිරීමට හෝ සෙවීමට මෙය ඔබට ඉඩ දෙයි.
ඉතා මූලික නිදසුනක් නම්, එක් ශ්රිතයක් නම් ශ්රිතයක් 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
මුල සිටම ශ්රිතයෙන් ආරම්භ වන විට ඒවා ක්රියාත්මක කිරීමට පටන් ගන්නා ස්ථානයෙන් යම් මතක ලිපිනයක් ඇත. එකලස් කිරීමේ භාෂාවෙන් ඒවා හැඳින්වෙන්නේ ("ශ්රිතයේ මතක ලිපිනය" අමතන්න) දැන් C වෙත නැවත පැමිණෙන්න. ශ්රිතයට මතක ලිපිනයක් තිබේ නම් ඒවා C හි දර්ශකයන් විසින් හැසිරවිය හැක.
1. පළමුව ඔබ ක්රියාකාරී වීමට දර්ශකයක් ප්රකාශ කළ යුතුය 2. අපේක්ෂිත ශ්රිතයේ ලිපිනය යොදන්න
**** සටහන-> ශ්රිත එකම වර්ගයේ විය යුතුය ****
මෙම සරල වැඩසටහන සෑම දෙයක්ම නිදර්ශනය කරයි.
#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");
}
ඉන්පසු යන්ත්රය ඒවා තේරුම් ගන්නේ කෙසේදැයි බලන්න. බිට් 32 ගෘහ නිර්මාණ ශිල්පයෙහි ඉහත වැඩසටහනේ යන්ත්ර උපදෙස් පිළිබඳ අවබෝධය.
රතු සලකුණු ප්රදේශය මඟින් ලිපිනය හුවමාරු වන ආකාරය පෙන්වයි. එවිට ඒවා eax පිළිබඳ ඇමතුම් උපදෙස් වේ. eax හි ශ්රිතයේ අපේක්ෂිත ලිපිනය අඩංගු වේ.
ශ්රිත දර්ශකය යනු ශ්රිතයක ලිපිනය අඩංගු විචල්යයකි. එය සීමිත ගුණාංග සහිත වුවද එය දර්ශක විචල්යයක් බැවින්, දත්ත ව්යුහයන්හි වෙනත් ඕනෑම දර්ශක විචල්යයක් මෙන් ඔබට එය භාවිතා කළ හැකිය.
මට සිතිය හැකි එකම ව්යතිරේකය වන්නේ ශ්රිත දර්ශකය තනි අගයක් හැර වෙනත් දෙයකට යොමු කිරීමක් ලෙස සැලකීමයි. ශ්රිත දර්ශකයක් වැඩි කිරීමෙන් හෝ අඩු කිරීමෙන් හෝ ශ්රිත දර්ශකයට ඕෆ්සෙට් එකක් එකතු කිරීමෙන් / අඩු කිරීමෙන් දර්ශක අංක ගණිතය කිරීම ඇත්ත වශයෙන්ම කිසිදු උපයෝගීතාවයක් නොවේ.
ශ්රිත දර්ශක විචල්යයක ප්රමාණය, විචල්යය විසින් අල්ලාගෙන ඇති බයිට් ගණන, යටින් පවතින ගෘහ නිර්මාණ ශිල්පය අනුව වෙනස් විය හැකිය, උදා: x32 හෝ x64 හෝ කුමක් හෝ.
ශ්රිත දර්ශක විචල්යයක් සඳහා වන ප්රකාශය, සී සම්පාදකයා සාමාන්යයෙන් කරන ආකාරයේ චෙක්පත් සිදු කිරීම සඳහා ශ්රිත ප්රකාශයක් ලෙස එකම ආකාරයේ තොරතුරු නියම කළ යුතුය. ශ්රිත දර්ශකයේ ප්රකාශනයේ / අර්ථ දැක්වීමේදී ඔබ පරාමිති ලැයිස්තුවක් සඳහන් නොකරන්නේ නම්, සී සම්පාදකයාට පරාමිති භාවිතය පරීක්ෂා කිරීමට නොහැකි වනු ඇත. මෙම පරීක්ෂා කිරීමේ lack නතාවය ප්රයෝජනවත් විය හැකි අවස්ථා තිබේ, කෙසේ වෙතත් ආරක්ෂිත දැලක් ඉවත් කර ඇති බව මතක තබා ගන්න.
උදාහරණ කිහිපයක්:
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
යනු a int
සහ a 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
C ++ හි ක්රියාකාරීත්වයට සමාන යමක් සැපයීමේ ක්රමයක් ලෙස මෙය ගෝලීය විචල්යයකට පැවරිය හැකිය.
ශීර්ෂ ගොනුවක අපගේ නාම අවකාශය වන ගෝලීය විචල්යයක් සමඟ එය භාවිතා කරන ව්යුහයක් අර්ථ දක්වන්න.
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;
C ප්රභව ගොනුවේ:
#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);
යටින් පවතින දෘඩාංගයේ විශේෂිත ආකෘතියක් භාවිතා කරන කේත සඳහා වියුක්ත දෘඩාංග ස්ථරයක් අර්ථ දැක්වීමට එකම ප්රවේශය භාවිතා කළ හැකිය. වියුක්ත දෘඩාංග ආකෘතියේ නිශ්චිතව දක්වා ඇති කාර්යයන් ක්රියාත්මක කරන දෘඩාංග විශේෂිත ක්රියාකාරිත්වය සැපයීම සඳහා කර්මාන්තශාලාවක් මඟින් ක්රියාකාරී දර්ශක දෘඩාංග විශේෂිත කාර්යයන් වලින් පුරවා ඇත. නිශ්චිත දෘඩාංග ශ්රිත අතුරුමුහුණත ලබා ගැනීම සඳහා කර්මාන්තශාලා ශ්රිතයක් ලෙස හඳුන්වන මෘදුකාංග භාවිතා කරන වියුක්ත දෘඩාංග ස්ථරයක් සැපයීමට මෙය භාවිතා කළ හැකි අතර පසුව නිශ්චිත ඉලක්කය පිළිබඳ ක්රියාත්මක කිරීමේ තොරතුරු දැන ගැනීමකින් තොරව යටින් පවතින දෘඩාංග සඳහා ක්රියා කිරීමට ලබා දී ඇති ශ්රිත දර්ශක භාවිතා කරයි. .
නියෝජිතයින්, හසුරුවන්නන් සහ ඇමතුම් ලබා ගැනීම සඳහා ක්රියාකාරී දර්ශක
කිසියම් කාර්යයක් හෝ ක්රියාකාරීත්වයක් පැවරීමේ ක්රමයක් ලෙස ඔබට ක්රියාකාරී දර්ශක භාවිතා කළ හැකිය. සී සම්භාව්ය උදාහරණයක් ස්ටෑන්ඩර්ඩ් C පුස්තකයන් කාර්යයන් සඳහා භාවිත වන සංසන්දනය නියෝජිතයා කාර්යය පහිටුම් දක්වනය වේ qsort()
හා bsearch()
භාණ්ඩ ලැයිස්තුවක් වර්ග කිරීම ෙහෝ භාණ්ඩ ක හරිම ලැයිස්තුව පුරා ද්විමය සෝදිසි සිදු කිරීම සඳහා මෙම සංචයනය සඳහා ලබා දීමට. සංසන්දනය කිරීමේ ශ්රිත නියෝජිතයා වර්ග කිරීම හෝ ද්විමය සෙවුම සඳහා භාවිතා කරන සංයුක්ත ඇල්ගොරිතම නියම කරයි.
තවත් භාවිතයක් C ++ සම්මත ආකෘති පුස්තකාල බහාලුමකට ඇල්ගොරිතමයක් යෙදීමට සමාන වේ.
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);
තවත් උදාහරණයක් නම්, GUI ප්රභව කේතය සමඟ, යම් සිදුවීමක් සඳහා හසුරුවන්නෙකු ලියාපදිංචි කර ඇති අතර, එය සිදුවූ විට සැබවින්ම හැඳින්වෙන ශ්රිත දර්ශකයක් සපයයි. මයික්රොසොෆ්ට් එම්එෆ්සී රාමුව එහි පණිවිඩ සිතියම් සමඟ වින්ඩෝස් පණිවිඩ හැසිරවීමට සමාන දෙයක් කවුළුවකට හෝ නූලකට යවයි.
නැවත කැඳවීමක් අවශ්ය වන අසමමුහුර්ත කාර්යයන් සිදුවීම් හසුරුවන්නෙකුට සමාන වේ. අසමමුහුර්ත ශ්රිතයේ පරිශීලකයා කිසියම් ක්රියාවක් ආරම්භ කිරීම සඳහා අසමමුහුර්ත ශ්රිතය අමතන අතර ක්රියාව අවසන් වූ පසු අසමමුහුර්ත ශ්රිතය කැඳවන ශ්රිත දර්ශකයක් සපයයි. මෙම අවස්ථාවෙහිදී සිදුවීම යනු එහි කාර්යය සම්පූර්ණ කරන අසමමුහුර්ත ශ්රිතයයි.
ක්රියාකාරී දර්ශකයන් බොහෝ විට ටයිප් කළ ඇමතුම් ආපසු ලබා දෙන බැවින්, ඔබට ආරක්ෂිත ඇමතුම් ආපසු ලබා ගැනීම අවශ්ය වේ. ඇමතුම් ලබා නොගන්නා කාර්යයන් සඳහා ඇතුළත් වීමේ ස්ථාන ආදිය සඳහා ද මෙය අදාළ වේ.
සී එකවරම තරමක් චංචල සහ සමාව දෙන :)