සී හි ක්‍රියාකාරී දර්ශක ක්‍රියා කරන්නේ කෙසේද?


1246

සී හි ක්‍රියාකාරී දර්ශකයන් සමඟ මට මෑතකදී යම් අත්දැකීමක් තිබුණි.

එබැවින් ඔබේම ප්‍රශ්නවලට පිළිතුරු සැපයීමේ සම්ප්‍රදාය අනුව යමින්, විෂයයට ඉක්මන් කිමිදීමක් අවශ්‍ය අය සඳහා, මූලික කරුණු පිළිබඳ කුඩා සාරාංශයක් කිරීමට මම තීරණය කළෙමි.


35
එසේම: සී පොයින්ටර්ස් පිළිබඳ ගැඹුරු විශ්ලේෂණයක් සඳහා, blogs.oracle.com/ksplice/entry/the_ksplice_pointer_challenge බලන්න . එසේම, බිම් මට්ටමේ සිට වැඩසටහන්කරණය මඟින් ඒවා යන්ත්‍ර මට්ටමින් ක්‍රියා කරන ආකාරය පෙන්වයි. අවබෝධය සී ගේ "මතක ආකෘතිය" සී වැඩ සූචක ආකාරය තේරුම් ගැනීම සඳහා ඉතා ප්රයෝජනවත් වේ.
අබ්බාෆේ

8
නියම තොරතුරු. මාතෘකාව අනුව, "ක්‍රියාකාරී දර්ශකයන් ක්‍රියා කරන්නේ කෙසේද" යන්න පැහැදිලි කිරීමක් දැකීමට මම බලාපොරොත්තු වන්නෙමි, ඒවා කේතගත කර ඇති ආකාරය නොවේ :)
බොග්ඩන් ඇලෙක්සැන්ඩ්‍රු

Answers:


1496

සී හි ක්‍රියාකාරී දර්ශක

අපි පෙන්වා දෙන මූලික ශ්‍රිතයකින් පටන් ගනිමු :

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

පළමුවෙන්ම, 2 ints ලබාගෙන නැවත ලබා දෙන ශ්‍රිතයකට දර්ශකයක් අර්ථ දක්වමු 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;
}

20
විශිෂ්ට තොරතුරු වලට ස්තූතියි. ශ්‍රිත දර්ශක භාවිතා කරන්නේ කොතැනද යන්න හෝ විශේෂයෙන් ප්‍රයෝජනවත් වන්නේ කෙසේද යන්න පිළිබඳව ඔබට යම් අවබෝධයක් එක් කළ හැකිද?
පොහොසත් කාර්පෙන්ටර්

328
"functionPtr = & addInt;" "functionPtr = addInt;" ලෙසද ලිවිය හැකිය (බොහෝ විට); මෙම සන්දර්භය තුළ ශ්‍රිත නාමයක් ශ්‍රිතයේ ලිපිනය බවට පරිවර්තනය වන බව ප්‍රමිතිය පවසන බැවින් එය ද වලංගු වේ.
hlovdal

22
hlovdal, මෙම සන්දර්භය තුළ කෙනෙකුට ශ්‍රිතය ලිවීමට හැකියාව ඇත්තේ මෙය බව පැහැදිලි කිරීම සිත්ගන්නා කරුණකි. Ptr = ****************** addInt;
ජොහැන්නස් ෂෝබ් - litb

106
@ පොහොසත් කාර්පෙන්ටර් මෙය වසර 4 ක් ප්‍රමාද බව මම දනිමි, නමුත් අනෙක් පුද්ගලයින්ට මෙයින් ප්‍රයෝජන ගත හැකි යැයි මම සිතමි: ශ්‍රිත දර්ශක වෙනත් කාර්යයන් සඳහා පරාමිතීන් ලෙස ක්‍රියා කිරීම සඳහා ප්‍රයෝජනවත් වේ . කිසියම් අමුතු හේතුවක් නිසා එම පිළිතුර සොයා ගැනීමට මා සෙවීමට බොහෝ දේ අවශ්‍ය විය. එබැවින් මූලික වශයෙන් එය සී ව්‍යාජ පළමු පන්තියේ ක්‍රියාකාරිත්වය ලබා දෙයි.
යෝධ 91

24
@ පොහොසත් කාර්පෙන්ටර්: ධාවන කාල CPU හඳුනාගැනීම සඳහා ක්‍රියාකාරී දර්ශක හොඳයි. SSE, popcnt, AVX යනාදියෙහි වාසිය ලබා ගැනීම සඳහා සමහර කාර්යයන් වල බහු සංස්කරණ තබා ගන්න. ආරම්භයේදීම, වර්තමාන CPU සඳහා එක් එක් ශ්‍රිතයේ හොඳම අනුවාදයට ඔබේ ක්‍රියාකාරී දර්ශකයන් සකසන්න. ඔබගේ අනෙක් කේතයේ, සෑම තැනකම CPU විශේෂාංග මත කොන්දේසි සහිත ශාඛා තිබීම වෙනුවට ශ්‍රිත දර්ශකය හරහා අමතන්න. එවිට ඔබට එය හොඳින් තීරණය කිරීම පිළිබඳ සංකීර්ණ තර්කනයක් කළ හැකිය, මෙම CPU සහාය දැක්වුවද pshufb, එය මන්දගාමී වේ, එබැවින් කලින් ක්‍රියාත්මක කිරීම තවමත් වේගවත්ය. x264 / x265 මෙය පුළුල් ලෙස භාවිතා කරන අතර විවෘත මූලාශ්‍ර වේ.
පීටර් කෝර්ඩ්ස්

304

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.getString.lengthbaseString

ශ්‍රිත දර්ශකයක් භාවිතා කිරීමෙන් සුපිරි පන්තියකින් ක්‍රමයක උරුමය ලබා ගත හැකිය.

සී හි බහුඅවයවිකතාව තවදුරටත් අපට කළ හැකිය .

උදාහරණයක් ලෙස අපි හැසිරීම වෙනස් කිරීමට අවශ්ය නම්, lengthනැවත ක්රමය 0තුළ සියලු කාලය ImmutableStringයම් හේතුවක් නිසා පන්ති, සිදු කළ යුතු බව සියලු වන්නේ:

  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

සී හි වස්තු-නැඹුරුවන ක්‍රමලේඛ ශෛලියක් සමඟ ලිවීමට මම තවමත් ඉගෙන ගනිමින් සිටින බව මම ප්‍රතික්ෂේප කිරීමක් එකතු කළ යුතුය, එබැවින් බොහෝ විට මා හොඳින් පැහැදිලි නොකළ කරුණු තිබිය හැකිය, නැතහොත් ඕඕපී ක්‍රියාවට නැංවිය හැකි හොඳම ආකාරය අනුව ලකුණු රහිත විය හැකිය. සී හි. නමුත් මගේ අරමුණ වූයේ ශ්‍රිත දර්ශකයන්ගේ බොහෝ භාවිතයන්ගෙන් එකක් නිදර්ශනය කිරීමට උත්සාහ කිරීමයි.

C හි වස්තු-නැඹුරු වැඩසටහන්කරණය කරන්නේ කෙසේද යන්න පිළිබඳ වැඩි විස්තර සඳහා කරුණාකර පහත සඳහන් ප්‍රශ්න වෙත යොමු වන්න:


23
මෙම පිළිතුර භයානක ය! OO කෙසේ හෝ තිත් අංකනය මත රඳා පවතින බව එයින් ගම්‍ය වනවා පමණක් නොව, එය ඔබේ වස්තූන් තුළට කුණු දැමීම ද දිරිගන්වයි!
ඇලෙක්සි අවර්චෙන්කෝ

27
මෙය OO හරි, නමුත් C-style OO අසල කොතැනකවත් නැත. ඔබ බිඳී ගොස් ක්‍රියාත්මක කර ඇත්තේ ජාවාස්ක්‍රිප්ට් විලාසිතාවේ මූලාකෘති පදනම් කරගත් ඕ.ඕ. 1. එක් එක් ක අථත්ය වගුව සඳහා const struct ඇත: හමුවීමක් C ++ / පැස්කල් ආකාරයේ OO කිරීම සඳහා, ඔබට අවශ්ය කියලා පන්තියේ අථත්ය සාමාජිකයන් සමග. 2. බහු අවයවික වස්තූන් තුළ එම ව්‍යුහයට යොමු වන්න. 3. අථත්ය වගු හරහා අථත්ය ක්රම අමතන්න, සහ අනෙකුත් සියලුම ක්රම කෙලින්ම - සාමාන්යයෙන් සමහර ClassName_methodNameකාර්යයන් නම් කිරීමේ සම්මුතියට ඇලී සිටීම . එවිට පමණක් ඔබට C ++ සහ පැස්කල් හි සිදු වන ආකාරයටම ධාවන කාලය සහ ගබඩා පිරිවැය ලැබේ.
මොනිකා

19
OO වීමට අදහස් නොකරන භාෂාවක් සමඟ OO වැඩ කිරීම සැමවිටම නරක අදහසකි. ඔබට OO අවශ්‍ය නම් සහ තවමත් C තිබේ නම් C ++ සමඟ වැඩ කරන්න.
rbaleksandar

20
brbaleksandar ලිනක්ස් කර්නල් සංවර්ධකයින්ට එය කියන්න. "සෑම විටම නරක අදහසක්" යනු ඔබේ මතය වන අතර මම තරයේ එකඟ නොවෙමි.
ජොනතන් රයින්හාර්ට්

6
මම මේ පිළිතුරට කැමතියි, නමුත් malloc දමන්න එපා
cat

231

සේවයෙන් පහ කිරීමට මාර්ගෝපදේශය: ඔබේ කේතය අතින් සම්පාදනය කිරීමෙන් x86 යන්ත්‍රවල GCC හි ක්‍රියාකාරී දර්ශකයන් අනිසි ලෙස භාවිතා කරන්නේ කෙසේද:

මෙම වචන වචනාර්ථය 32-බිට් x86 යන්ත්‍ර කේතයේ බයිට් වේ. 0xC3, x86 retඋපදෙස් .

ඔබ සාමාන්‍යයෙන් මේවා අතින් ලියන්නේ නැත, ඔබ එකලස් කිරීමේ භාෂාවෙන් ලියන අතර පසුව nasmඑය පැතලි ද්විමය තුළට එක්රැස් කිරීමට කැමති එකලස් කරන්නෙකු භාවිතා කරන්න .

  1. EAX ලේඛනයේ වත්මන් අගය ලබා දෙයි

    int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
  2. 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);
    
  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වින්ඩෝස් මත) ස්ථානගත කර ඇති බව සලකන්න , එය පෙළ කොටසේ කොටසක් ලෙස සම්බන්ධ කර ඇත (කාර්යයන් සඳහා කේතය සමඟ).

පෙළ කොටසට 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 ාපනය නොකර සීරීම් ලේඛනයක් ලෙස භාවිතා කිරීමෙන් ඇමතුම බිඳ වැටෙනු ඇත.


8
සටහන: දත්ත ක්‍රියාත්මක කිරීම වැළැක්වීම සක්‍රීය කර ඇත්නම් මෙය ක්‍රියා නොකරයි (උදා: වින්ඩෝස් එක්ස්පී එස්පී 2 + මත), මන්ද සී නූල් සාමාන්‍යයෙන් ක්‍රියාත්මක කළ හැකි ලෙස සලකුණු කර නොමැත.
SecurityMatt

5
හායි මැට්! ප්‍රශස්තිකරණ මට්ටම මත පදනම්ව, GCC බොහෝ විට TEXT කොටසට නූල් නියතයන් යොදවනු ඇත, එබැවින් ඔබ මෙම වර්ගයේ ප්‍රශස්තිකරණයට ඉඩ නොදෙන ලෙස සපයා ඇති නවතම කවුළු වල පවා මෙය ක්‍රියා කරයි. (IIRC, මීට වසර දෙකකට පෙර මා තනතුර දැරූ අවස්ථාවේ MINGW අනුවාදය පෙරනිමි ප්‍රශස්තිකරණ මට්ටමින් වචන වචනාර්ථය දක්වයි)
ලී

10
කරුණාකර මෙහි සිදුවන්නේ කුමක්දැයි යමෙකුට පැහැදිලි කළ හැකිද? අමුතු පෙනුමක් ඇති නූල් වචන මොනවාද?
අජේ

58
jajay ඔහු අමු ෂඩාස්රාකාර අගයන් ලියන බවක් පෙනේ (නිදසුනක් ලෙස '\ x00' යනු '/ 0' ට සමාන වේ, ඒවා දෙකම 0 ට සමාන වේ) නූලකට, ඉන්පසු නූල සී ශ්‍රිත දර්ශකයකට දමා ඉන්පසු ක්‍රියාත්මක කරයි සී ක්‍රියාකාරී දර්ශකය ඔහු යක්ෂයා නිසා.
ejk314

3
හායි FUZxxl, මම හිතන්නේ එය සම්පාදකයා සහ මෙහෙයුම් පද්ධති අනුවාදය මත පදනම්ව වෙනස් විය හැකිය. ඉහත කේතය codepad.org හි හොඳින් ක්‍රියාත්මක වන බව පෙනේ; 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
එකඟ විය. මගේ සියලු ක්‍රියාකාරකයෝ මේ වගේ ය : int (*cb)(void *arg, ...). පුනරාවර්තකයේ ප්‍රතිලාභ අගය ද මට කලින් නැවැත්වීමට ඉඩ දෙයි (nonzero නම්).
ජොනතන් රයින්හාර්ට්

24

ඔබට මූලික ප්‍රකාශකයන් සිටින විට ක්‍රියාකාරී දර්ශකයන් ප්‍රකාශ කිරීම පහසු වේ:

  • id: 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 අභ්‍යන්තරයන් දැමීමට උත්සාහ කළෙමි. එහි ඇති දේවල් සංස්කරණය කිරීමට / නිවැරදි කිරීමට නිදහස් වන්න.


24

ක්‍රියාකාරී දර්ශකයන් සඳහා තවත් හොඳ භාවිතයක්:
අනුවාද අතර වේදනා රහිතව මාරු වීම

ඔබට විවිධ වේලාවන්හි විවිධ කාර්යයන් හෝ සංවර්ධනයේ විවිධ අවධීන් අවශ්‍ය විට ඒවා භාවිතා කිරීම ඉතා පහසුය. උදාහරණයක් ලෙස, මම කොන්සෝලයක් ඇති ධාරක පරිගණකයක යෙදුමක් සංවර්ධනය කරමින් සිටිමි, නමුත් මෘදුකාංගයේ අවසාන නිකුතුව අව්නෙට් සෙඩ්බෝඩ් එකක තබනු ඇත (එහි දර්ශන සහ කොන්සෝල සඳහා වරායන් ඇත, නමුත් ඒවා අවශ්‍ය නොවේ / අවශ්‍ය නොවේ අවසාන නිකුතුව). එබැවින් සංවර්ධනයේදී, 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;
}

ශ්‍රිත දර්ශකය මූලාකෘතිගත කර ඇති version.hආකාරය සැලකිල්ලට ගන්න

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

යෙදුමෙහි එය සඳහන් කළ විට, එය යොමු කරන ඕනෑම තැනක එය ක්‍රියාත්මක කිරීමට පටන් ගනී, එය තවමත් අර්ථ දක්වා නොමැත.

දී version.cදී දැනුම්, board_init()එහිදී උත්සවය zprintfඅර්ථ දක්වා ඇත බව අනුවාදය මත පදනම්ව (එහි කාර්යය අත්සන තරග) සුවිශේෂී කාර්යය පැවරී ඇතversion.h

zprintf = &printf; නිදොස් කිරීමේ අරමුණු සඳහා zprintf printf අමතයි

හෝ

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

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


10

මුල සිටම ශ්‍රිතයෙන් ආරම්භ වන විට ඒවා ක්‍රියාත්මක කිරීමට පටන් ගන්නා ස්ථානයෙන් යම් මතක ලිපිනයක් ඇත. එකලස් කිරීමේ භාෂාවෙන් ඒවා හැඳින්වෙන්නේ ("ශ්‍රිතයේ මතක ලිපිනය" අමතන්න) දැන් 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 හි ශ්‍රිතයේ අපේක්ෂිත ලිපිනය අඩංගු වේ.


9

ශ්‍රිත දර්ශකය යනු ශ්‍රිතයක ලිපිනය අඩංගු විචල්‍යයකි. එය සීමිත ගුණාංග සහිත වුවද එය දර්ශක විචල්‍යයක් බැවින්, දත්ත ව්‍යුහයන්හි වෙනත් ඕනෑම දර්ශක විචල්‍යයක් මෙන් ඔබට එය භාවිතා කළ හැකිය.

මට සිතිය හැකි එකම ව්‍යතිරේකය වන්නේ ශ්‍රිත දර්ශකය තනි අගයක් හැර වෙනත් දෙයකට යොමු කිරීමක් ලෙස සැලකීමයි. ශ්‍රිත දර්ශකයක් වැඩි කිරීමෙන් හෝ අඩු කිරීමෙන් හෝ ශ්‍රිත දර්ශකයට ඕෆ්සෙට් එකක් එකතු කිරීමෙන් / අඩු කිරීමෙන් දර්ශක අංක ගණිතය කිරීම ඇත්ත වශයෙන්ම කිසිදු උපයෝගීතාවයක් නොවේ.

ශ්‍රිත දර්ශක විචල්‍යයක ප්‍රමාණය, විචල්‍යය විසින් අල්ලාගෙන ඇති බයිට් ගණන, යටින් පවතින ගෘහ නිර්මාණ ශිල්පය අනුව වෙනස් විය හැකිය, උදා: 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ගොනු විෂය පථය නම් ශ්‍රිතයක් නියම කිරීමට ඔබට යතුරු පදය භාවිතා කළ හැකි අතර පසුව namespaceC ++ හි ක්‍රියාකාරීත්වයට සමාන යමක් සැපයීමේ ක්‍රමයක් ලෙස මෙය ගෝලීය විචල්‍යයකට පැවරිය හැකිය.

ශීර්ෂ ගොනුවක අපගේ නාම අවකාශය වන ගෝලීය විචල්‍යයක් සමඟ එය භාවිතා කරන ව්‍යුහයක් අර්ථ දක්වන්න.

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 ප්‍රභව කේතය සමඟ, යම් සිදුවීමක් සඳහා හසුරුවන්නෙකු ලියාපදිංචි කර ඇති අතර, එය සිදුවූ විට සැබවින්ම හැඳින්වෙන ශ්‍රිත දර්ශකයක් සපයයි. මයික්‍රොසොෆ්ට් එම්එෆ්සී රාමුව එහි පණිවිඩ සිතියම් සමඟ වින්ඩෝස් පණිවිඩ හැසිරවීමට සමාන දෙයක් කවුළුවකට හෝ නූලකට යවයි.

නැවත කැඳවීමක් අවශ්‍ය වන අසමමුහුර්ත කාර්යයන් සිදුවීම් හසුරුවන්නෙකුට සමාන වේ. අසමමුහුර්ත ශ්‍රිතයේ පරිශීලකයා කිසියම් ක්‍රියාවක් ආරම්භ කිරීම සඳහා අසමමුහුර්ත ශ්‍රිතය අමතන අතර ක්‍රියාව අවසන් වූ පසු අසමමුහුර්ත ශ්‍රිතය කැඳවන ශ්‍රිත දර්ශකයක් සපයයි. මෙම අවස්ථාවෙහිදී සිදුවීම යනු එහි කාර්යය සම්පූර්ණ කරන අසමමුහුර්ත ශ්‍රිතයයි.


0

ක්‍රියාකාරී දර්ශකයන් බොහෝ විට ටයිප් කළ ඇමතුම් ආපසු ලබා දෙන බැවින්, ඔබට ආරක්ෂිත ඇමතුම් ආපසු ලබා ගැනීම අවශ්‍ය වේ. ඇමතුම් ලබා නොගන්නා කාර්යයන් සඳහා ඇතුළත් වීමේ ස්ථාන ආදිය සඳහා ද මෙය අදාළ වේ.

සී එකවරම තරමක් චංචල සහ සමාව දෙන :)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.