Din numărul de întrebări postate aici, l'e clar că oamenii au niște fundemental probleme atunci când se capetele lor în jurul valorii de indicii și aritmetica pointerilor.
Am'm curios să știu de ce. Ei've niciodată nu mi-a cauzat probleme majore (deși am învățat prima dată despre ele înapoi în Neolitic). În scopul de a scrie mai bine răspunsurile la aceste întrebări, am'd vrea să știu de ce oamenii găsesc greu.
Deci, dacă te're se luptă cu pointeri, sau ai recent au fost, dar dintr-o dată "prins", care au fost aspectele legate de indicii care te-a făcut probleme?
Când am început să lucrez cu ei, cea mai mare problemă am avut-o a fost sintaxa.
int* ip;
int * ip;
int *ip;
sunt toate la fel.
dar:
int* ip1, ip2; //second one isn't a pointer!
int *ip1, *ip2;
De ce? pentru că "pointer" o parte din declarația aparține variabilă, și nu de tip.
Și apoi dereferencing lucru, foloseste o foarte similare notație:
*ip = 4; //sets the value of the thing pointed to by ip to '4'
x = ip; //hey, that's not '4'!
x = *ip; //ahh... there's that '4'
Cu excepția cazului când aveți de fapt nevoie pentru a obține un pointer... apoi utilizați un ampersand!
int *ip = &x;
Ura pentru consecvență!
Apoi, se pare că doar pentru a fi ticăloși și să dovedească cât de deștept sunt, o mulțime de bibliotecă dezvoltatorii folosesc pointeri la pointeri la pointeri, și dacă se așteaptă o serie de aceste lucruri, ei bine, de ce nu treci doar un pointer la asta.
void foo(****ipppArr);
pentru a apela acest lucru, am nevoie de adresa de matrice de pointeri la pointeri la pointeri de int:
foo(&(***ipppArr));
În șase luni, când trebuie să mențină acest cod, voi petrece mai mult timp încercând să dau seama ce înseamnă asta decât rescrierea de la zero. (da, probabil că sintaxa greșită-l's fost un timp de când am'am făcut nimic în C. mi-a cam lipsit, dar apoi m-am'm un pic de o massochist)
Bănuiesc că oamenii sunt un pic prea adânc în răspunsurile lor. O înțelegere de programare, real CPU operațiuni, sau de asamblare la nivel de management al memoriei e't într-adevăr necesare.
Când eram la școală, am găsit următoarele găuri în elevii' înțelegerea să fie cea mai comună sursă de probleme:
Cele mai multe dintre studenții mei au fost capabili să înțeleagă un desen simplificat de o bucată de memorie, în general, variabilele locale secțiunea a stivei la domeniul de aplicare actual. În general, oferindu-explicit fictiv adrese de la diferite locații de-a ajutat.
Cred că în rezumat, am'm a spune că, dacă vrei să înțelegi indicii, trebuie să înțelegi variabile, și ceea ce sunt de fapt în arhitecturi moderne.
Înțelegerea corectă de indicatori necesită cunoștințe despre mașină de fond al sistemului's arhitectură.
Mulți programatori astăzi don't știu cum funcționează mașina, doar ca cei mai multi oameni care știu cum să conducă o mașină don't știu nimic despre motor.
Atunci când se ocupă cu indicii, oamenii care obține confuz sunt pe scară largă în una din cele două tabere. Am'am fost (sunt?) în ambele.
matrice[]
mulțimeaAcest lucru este mulțimea asta direct nu't știu cum să traduc din notație pointer la matrice notație (sau nu't știu chiar că ele sunt chiar legate). Aici sunt patru moduri de a accesa elementele unei matrice:
int vals[5] = {10, 20, 30, 40, 50};
int *ptr;
ptr = vals;
array element pointer
notation number vals notation
vals[0] 0 10 *(ptr + 0)
ptr[0] *(vals + 0)
vals[1] 1 20 *(ptr + 1)
ptr[1] *(vals + 1)
vals[2] 2 30 *(ptr + 2)
ptr[2] *(vals + 2)
vals[3] 3 40 *(ptr + 3)
ptr[3] *(vals + 3)
vals[4] 4 50 *(ptr + 4)
ptr[4] *(vals + 4)
Ideea de aici este că accesarea tablouri prin pointeri pare destul de simplă și directă, dar o tona de foarte complicată și lucruri inteligente poate fi făcut în acest fel. Dintre care unele pot pleca cu experiență C/C++ programatori beat, darămite fără experiență începători.
Acest este o mare de articol care explică diferența și care nu'll fi invocând și fură un cod de la :)
Ca un mic exemplu, poate fi foarte dificil de a vedea exact ceea ce autorul a vrut să facă dacă ați dat peste ceva de genul asta:
//function prototype
void func(int*& rpInt); // I mean, seriously, int*& ??
int main()
{
int nvar=2;
int* pvar=&nvar;
func(pvar);
....
return 0;
}
Sau, într-o mai mică măsură, ceva de genul asta:
//function prototype
void func(int** ppInt);
int main()
{
int nvar=2;
int* pvar=&nvar;
func(&pvar);
....
return 0;
}
Deci, la sfârșitul zilei, ceea ce putem rezolva într-adevăr, cu toate prostiile astea? Nimic.
Acum am văzut sintaxa ptr-să-ptr ca și ref-a-ptr. Sunt acolo orice avantajele una peste alta? mi-e teamă, nu. Utilizarea de unul dintre ambele, pentru unii programatori sunt doar preferințe personale. Unele care folosesc ref-a-ptr spune sintaxa este "curat" în timp ce unii care folosesc ptr-să-ptr, spune ptr-să-ptr sintaxă face mai clară a pentru cei care citesc ceea ce faci.
Această complexitate și aparent (bold aparenta) interschimbabilitate cu referințe ,care este de multe ori un alt avertisment de indicatori și o eroare de nou-veniți, face o înțelegere indicii de greu. L's, de asemenea, important să se înțeleagă, pentru finalizarea's sake, că indicii de referință sunt ilegale în C și C++ pentru confuze motive pe care le ia în lvalue
-rvalue
semantica.
Ca răspunsul anterior a remarcat, de multe ori nu'll trebuie doar astea smechere programatori care cred că sunt inteligent prin utilizarea ******awesome_var->lol_im_so_clever()
și cele mai multe dintre noi sunt, probabil, vinovat de a scrie astfel de atrocități la ori, dar's nu cod, și-l's, cu siguranță, nu întreținut.
Ei bine acest răspuns s-a dovedit a fi mai mult decât am sperat...
Eu dau vina pe calitatea materialelor de referință și oameni care fac didactice, personal; cele mai multe concepte în C (dar mai ales pointer) sunt pur și simplu învățați prost. Mă tot amenință că o să-mi scriu propriul C cartea (intitulat Ultimul Lucru De care Lumea are Nevoie De o Altă Carte Despre Limbajul de Programare C), dar nu't au timp sau răbdare să facă acest lucru. Deci, eu stau aici si arunca citate aleatoare de la Standard la oameni.
Nu's, de asemenea, faptul că, atunci când C a fost conceput inițial, acesta a fost presupune ai înțeles mașină de arhitectură la o destul de detaliat nivel, doar pentru că nu a fost nici o modalitate de a evita în viața de zi cu zi de lucru (memorie a fost atât de strâns și procesoare au fost atât de lent că a trebuit să înțeleg ce ai scris afectate de performanță).
Există o mare de articol sprijinind ideea că indicii sunt greu de pe Joel Spolsky's site - ul - Pericolele de JavaSchools.
[Disclaimer - nu sunt un Java-hater sine.]
Cele mai multe lucruri sunt greu de înțeles dacă te're nu se bazează pe cunoștințele pe care's "sub". Când am învățat CS a devenit mult mai ușor atunci când mi-am început studenți pe programare foarte simplu "masina", o simulare de zecimale calculator cu zecimale opcodes a cărui memorie a constat în zecimal registre și zecimale adrese. Ei ar pune în foarte scurt, de programe, de exemplu, se adaugă o serie de numere pentru a obține un total. Apoi vor singur pas pentru a viziona ceea ce se întâmplă. Acestea ar putea deține în jos "enter" cheie și urmăriți-l rula "rapid".
Am'sunt sigur că aproape toată lumea de pe DECI se întreabă de ce este util pentru a obține atât de bază. Am uitat cum e să nu știe cum să program. Se joacă cu o jucărie calculator pune în locul concepte fără de care nu se poate't program, cum ar fi ideile de calcul este un pas-cu-pas proces, folosind un număr mic de primitive de bază pentru a construi programe și conceptul de variabile de memorie ca locuri în care numerele sunt stocate, în care adresa sau numele variabilei este diferit de numărul conține. Există o distincție între momentul în care intri în program, și momentul în care "conduce". Am asemăna învăța să program ca trecere de o serie de "limitatoare de viteză", cum ar fi foarte simplu de programe, apoi bucle și subrutine, atunci tablouri, apoi secvențial I/O, atunci indicii și date structura. Toate acestea sunt mult mai ușor de a învăța prin trimitere la ceea ce un computer este într-adevăr face dedesubt.
În cele din urmă, atunci când ajunge la C, indicii sunt confuz, deși K&R a făcut o treabă foarte bună de a le explica. Cum le-am învățat în C era să știi cum să le citesc - de la dreapta la stânga. Ca atunci când văd int *p
în capul meu îmi spun " " p " indică o int
". C a fost inventat ca un pas de un limbaj de asamblare și care's ce-mi place la ea - este aproape de "teren". Indicii, ca orice altceva, sunt mai greu de înțeles, dacă nu't au ca împământare.
Am't obține indicii până când am citit descrierea de la K&R. Până în acel moment, indicii n't sens. Am citit o grămadă de lucruri pe care oamenii au spus "Nu't învețe indicii, ele sunt confuze și va doare capul tău și-ți dau anevrisme" așa că m-am îndepărtat de ea pentru o lungă perioadă de timp, și a creat această inutile de aer de dificil-concept.
În caz contrar, cea mai mare parte ceea ce am crezut că a fost, de ce ai vrea o variabilă care trebuie să treacă prin cercuri pentru a obține valoarea, și dacă ai vrut pentru a atribui lucruri la el, ai avut de a face lucruri ciudate pentru a obține valorile pentru a merge în ele. Ideea unei variabile este ceva pentru a stoca o valoare, m-am gândit, deci, de ce cineva a vrut să-l facă complicat a fost dincolo de mine. "Deci cu un pointer trebuie să folosi `` operator pentru a ajunge la valoarea??? Ce fel de nătărău variabilă este asta?"*, m-am gândit. Inutil, nu joc de cuvinte destinate.
Motivul a fost complicat a fost pentru că nu am't înțeleagă că un pointer a fost o adresa a ceva. Dacă vă explicați că aceasta este o adresă, că este ceva care conține o adresă la altceva, și că puteți manipula acea adresă pentru a face lucruri utile, cred că s-ar putea clarifica confuzia.
O clasa care este necesar, folosind indicii pentru a accesa/modifica porturile de pe un PC, folosind aritmetica pointerilor pentru a aborda diferite locații de memorie, și se uită la mai complicat C-codul modificat argumentele lor scap eu de ideea că indicii au fost, de asemenea, inutil.
Aici's un pointer/matrice exemplu care mi-a dat pauză. Să presupunem că aveți două tablouri:
uint8_t source[16] = { /* some initialization values here */ };
uint8_t destination[16];
Și de obiectivul dvs. este de a copia uint8_t conținutul din sursa de destinație folosind memcpy(). Ghici care dintre următoarele realiza acest obiectiv:
memcpy(destination, source, sizeof(source));
memcpy(&destination, source, sizeof(source));
memcpy(&destination[0], source, sizeof(source));
memcpy(destination, &source, sizeof(source));
memcpy(&destination, &source, sizeof(source));
memcpy(&destination[0], &source, sizeof(source));
memcpy(destination, &source[0], sizeof(source));
memcpy(&destination, &source[0], sizeof(source));
memcpy(&destination[0], &source[0], sizeof(source));
Răspunsul (Spoiler Alert!) este TOT de pe ei. "destinație", "&destinație", și "&destinație[0]" sunt toate de aceeași valoare. "&destinație" este un alt tip decât celelalte două, dar este încă aceeași valoare. Același lucru este valabil pentru permutări de "sursa".
Ca o paranteza, eu personal prefer prima versiune.
Că ar trebui să încep prin a spune că C și C++ au fost primele limbaje de programare am invatat. Am început cu C, atunci C++ la scoala, o mulțime, și apoi a mers înapoi la C pentru a deveni fluent în ea. Primul lucru care m-a derutat despre indicii atunci când învățarea C a fost simplu:
char ch;
char str[100];
scanf("%c %s", &ch, str);
Această confuzie a fost înrădăcinată în cea mai mare parte au fost introduse pentru a utiliza o referință la o variabilă de argumente înainte de indicatori au fost introduse în mod corespunzător pentru mine. Îmi amintesc că am trecut peste ce a scris primele exemple într-un fel for Dummies pentru că au fost prea simplu doar pentru a obține primul program am scris la locul de muncă (cel mai probabil din aceasta cauza).
Ceea ce a fost confuz despre acest lucru a fost ceea ce &ch
referea de fapt la fel de bine ca de ce str
n't nevoie de ea.
După ce am devenit familiar cu care am amintesc să fi fost confuz cu privire la alocarea dinamică. Mi-am dat seama la un moment dat că avea pointeri la date a fost't extrem de util, fără alocarea dinamică de un anumit tip, așa că am scris ceva de genul:
char * x = NULL;
if (y) {
char z[100];
x = z;
}
pentru a încerca pentru a aloca dinamic un spațiu. Nu't de lucru. Am fost't de sigur că va funcționa, dar n-am't știu cum altfel s-ar putea lucra.
Mai târziu am aflat despre malloc
și noi
, dar se pare ca magic memory generatoare pentru mine. Nu știam nimic despre cum ar putea funcționa.
Unele timp mai târziu, am fost învățat recursivitate din nou (m-am'd invatat-o pe propria-mi înainte, dar a fost în clasa acum) și m-am întrebat cum a lucrat sub capota ... unde au fost separate variabile stocate. Profesorul meu a spus "pe stiva" și o mulțime de lucruri au devenit clare pentru mine. Am auzit înainte de termen și au pus în aplicare programe stive înainte. Am auzit altele se referă la "stiva" cu mult timp înainte, dar a uitat despre asta.
În acest timp mi-am dat seama că folosind tablouri multidimensionale în C poate deveni foarte confuz. Am știut de cum au lucrat, dar au fost doar atât de ușor să te încurci în care am decis să încerc să lucreze în jurul valorii de a le utiliza ori de câte ori am putut. Cred că problema aici a fost cea mai mare parte sintactică (în special trecerea sau întoarcerea ei din funcții).
De când am fost scris în C++ pentru liceu pentru anul următor sau doi am o mulțime de experiență, folosind indicii pentru structuri de date. Aici am avut un nou set de probleme de-amestecarea indicii. Mi-ar fi mai multe niveluri de indicatori (cum ar fi nod ***ptr;
) mă prindă. Am'd dereference un pointer greșit numărul de ori și în cele din urmă recurge la imaginind cum de multe *
am nevoie de studiu și de eroare.
La un moment dat am aflat cum un program de's heap lucrat (un fel de, dar suficient de bun că nu mai m-a tinut treaz noaptea). Îmi amintesc că am citit că, dacă te uiți la câteva bytes înainte de indicatorul care malloc
pe un anumit sistem de returnare, puteți vedea cât de mult date a fost de fapt alocate. Mi-am dat seama că codul în malloc
ar putea cere mai mult de memorie de sistem de OPERARE și această memorie nu a fost de partea mea fișiere executabile. Având o muncă decente idee de cum malloc
funcționează este foarte util.
Curând după asta mi-am luat un ansamblu de clasă, care nu't să mă învețe cât mai mult despre indicii ca cele mai multe programatori, probabil, cred. Dar m-a făcut să se gândească mai mult despre ceea ce adunării codul meu ar putea fi tradus în. Am încercat mereu să scrie cod eficient, dar acum am avut o idee mai bună.
Am luat, de asemenea, o serie de clase care am avut de a scrie unele lisp. Atunci când scrieți lisp am fost't ca în cauză cu eficiență cât am fost în C. am avut o foarte mică idee ce cod ar putea fi tradus într-dacă compilat, dar am stiut ca se pare ca, folosind o mulțime de locale numit simboluri (variabile) a făcut lucruri mult mai ușor. La un moment dat am scris un arbore AVL rotație cod într-un pic de lisp, că am avut un timp foarte greu de scris în C++ pentru a pointer probleme. Mi-am dat seama că aversiunea mea față de ceea ce credeam că excesul de variabile locale au împiedicat capacitatea mea de a scrie și mai multe alte programe în C++.
Am luat, de asemenea, un compilatoare clasa. În timp ce în această clasă am dat mai departe la materiale avansate și a aflat despre static single assignment (SSA) și mort variabile, care e't atât de important cu excepția faptului că m-a învățat că orice decent, compilatorul va face un loc de muncă decent de-a face cu variabile care nu mai sunt folosite. Știam deja că mai multe variabile (inclusiv indicatori) cu tipurile corecte și de bun nume ar ajuta să-mi țin lucrurile în capul meu, dar acum am, de asemenea, știa că le evita din motive de eficiență a fost chiar mai prost decât mai puțin de micro-optimizarea minte profesori mi-a spus.
Deci, pentru mine, știind un pic de bun despre aspectul memorie al unui program a ajutat foarte mult. Gândesc la ceea ce codul meu înseamnă, atât simbolic și pe hardware-ul, ajută-mă. Folosind locală indicii că au tipul corect ajută foarte mult. De multe ori am scrie cod care arata ca:
int foo(struct frog * f, int x, int y) {
struct leg * g = f->left_leg;
struct toe * t = g->big_toe;
process(t);
așa că dacă o dau în bară un tip pointer este foarte clar de eroare de compilator care este problema. Dacă am făcut-o:
int foo(struct frog * f, int x, int y) {
process(f->left_leg->big_toe);
și a primit orice tip pointer greșit acolo, compilatorul de eroare ar fi mult mai dificil să-mi dau seama. Aș fi tentat să recurgă la încercări și erori schimbări în frustrarea mea, și, probabil, a face lucrurile mai rău.
Am avut "pointer moment" de lucru pe unele programe de telefonie în C. am avut de a scrie o AXE10 schimb emulator folosind un analizor de protocol care numai în înțeles clasic C. Tot cu balamale pe cunoașterea indicii. Am încercat să scriu codul meu fara ei (hei, am fost "pre-pointer" slăbește-mă) și a eșuat complet.
Cheia pentru înțelegerea ei, pentru mine, a fost & (adresa) operator. Odată ce am înțeles că &am vrut să spun "adresa de i" apoi, înțelegând că
i` menit "conținutul de la adresa indicat de am" a venit un pic mai târziu. Ori de câte ori am scris sau citit codul meu întotdeauna mi-am repetat ceea ce "&" a însemnat și ce "" a însemnat și în cele din urmă am venit să le folosească în mod intuitiv.
Spre rușinea mea, am fost fortat sa VB si apoi Java, astfel indicatorul mea de cunoștințe nu este la fel de ascuțită ca a fost odata, dar mă bucur că eu sunt "post-pointer". Don't cere-mi să folosesc o bibliotecă, care-mi cere să înțeleagă ****p, totuși.
Privind înapoi, au fost patru lucruri care într-adevăr m-a ajutat să înțeleg în sfârșit indicii. Înainte de aceasta, aș putea să le folosesc, dar nu le înțelege pe deplin. Asta este, am știut că dacă am urmat forme, aș obține rezultatele dorite, dar nu am înțeles pe deplin 'de ce' de forme. Mi-am dat seama că acest lucru nu este exact ceea ce ai cerut, dar cred că este util un corolar.
Scrie o rutină care a avut un pointer la un întreg și a modificat întreg. Asta mi-a dat formularele necesare pe care să construiască orice modele mentale de modul în care indicii de muncă.
One-dimensional alocarea dinamică a memoriei. Imaginind 1-D alocare de memorie m-a făcut să înțeleagă conceptul de pointer.
De două-dimensional alocarea dinamică a memoriei. Imaginind 2-D alocare de memorie armat cu acest concept, dar, de asemenea, m-a învățat că indicatorul în sine necesită depozitare și trebuie să fie luate în considerare.
Diferențele între stiva de variabile, variabile globale și memorie heap. Imaginind aceste diferențe m-a învățat tipuri de memorie la care indicii punct/se referă.
Fiecare dintre aceste elemente este necesar imaginam ce se întâmplă la un nivel inferior ... construirea unui model mental care a satisfăcut fiecare caz m-am putut gândi de a arunca la ea. A fost nevoie de timp și efort, dar a meritat. Sunt convins că pentru a înțelege indicii, aveți de a construi acest model mental pe modul în care acestea funcționează și modul în care acestea sunt puse în aplicare.
Acum, înapoi la întrebarea inițială. Bazat pe lista anterioară, au fost mai multe elemente care nu au avut dificultăți în înțelegerea inițial.
Aici este un non-răspuns: Utilizarea cdecl (sau c++decl) să-și dea seama:
eisbaw@leno:~$ cdecl explain 'int (*(*foo)(const void *))[3]'
declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int
Principala dificultate cu indicii, cel puțin pentru mine, este că am n't începe cu C. am început cu Java. Noțiunea de indicii au fost într-adevăr străine până la câteva cursuri de la facultate, unde am fost așteptat să știu C. Deci, apoi m-am învățat foarte elementele de bază ale C și cum să utilizați indicii în sensul de bază. Chiar și atunci, de fiecare dată când mă aflu citire cod C, trebuie să se uite în sus indicatorul de sintaxă.
Deci, în experiența mea limitată(1 an lumea reală + 4 în facultate), indicii confunda mine, pentru că m-am'am mai avut să-l folosească altfel decât într-un cadru sală de clasă. Și nu pot simpatiza cu elevii de acum incepand CS cu JAVA in loc de C sau C++. Cum ai spus, ai învățat indicii în 'Neolitic' de vârstă și, probabil, au fost folosind-o de-atunci. Să ne mai noi oamenii, noțiunea de alocarea de memorie și de a face aritmetica pointerilor este chiar străină, pentru că toate aceste limbi au captate asta.
P. S. După ce a citit Spolsky eseu, descrierea lui de 'JavaSchools' a fost nimic de genul prin ce am trecut eu în facultate la Cornell ('05-'09). Mi-am luat structuri și programare funcțională (sml), sisteme de operare (C), algoritmi (pix și hârtie), și o mulțime de alte clase care au fost't predate în java. Cu toate acestea, toate intro clase și cursuri opționale-au făcut în java pentru că nu's valoare în a nu reinventa roata, atunci când sunt încercarea de a face ceva mai mare nivelat decât de punere în aplicare un hashtable cu indicii.
Acestea se adaugă o dimensiune suplimentară la codul fără o schimbare semnificativă a sintaxei. Cred că despre acest lucru:
int a;
a = 5
Nu's doar un singur lucru să se schimbe: "a". Puteți scrie un = 6`, iar rezultatele sunt evidente pentru majoritatea oamenilor. Dar acum ia în considerare:
int *a;
a = &some_int;
Există două lucruri despre " o "care sunt relevante la momente diferite: valoarea reală a "a", indicatorul, și valoarea "spate" indicatorul. Puteți schimba "a":
a = &some_other_int;
...și some_int
este încă în jurul valorii de undeva cu aceeași valoare. Dar puteți schimba, de asemenea, puncte de lucru la:
*a = 6;
Nu's conceptuală diferența dintre a = 6
, care are doar efecte secundare locale, și *a = 6
, care ar putea afecta o grămadă de alte lucruri în alte locuri. Punctul meu de aici e nu conceptul de indirectare este în mod inerent dificil, dar asta pentru că poți să faci ambele imediate, locale, lucru cu " a " sau indirecte lucru cu *un
... asta ar putea fi ceea ce deruteaza oamenii.
Am avut programat in c++ pentru ca de 2 ani și apoi convertit la Java(5 ani) și nu a privit niciodată înapoi. Cu toate acestea, atunci când am avut recent pentru a utiliza unele native chestii, am aflat (cu stupoare) ca nu am't uitat nimic despre indicii și chiar a le găsi ușor de utilizat. Acesta este un contrast puternic cu ceea ce am trăit 7 ani în urmă, când am încercat prima dată să înțeleagă conceptul. Deci, cred că de înțelegere și de simpatie este o problemă de programare maturitate ? :)
SAU
Indicii sunt ca mersul pe bicicletă, odată ce îți dai seama cum să lucreze cu ei, nu's nu-l uita.
Toate în toate, greu de înțeles sau nu, tot pointer ideea este FOARTE educativ și cred că ar trebui să fie înțeles de către fiecare programator, indiferent dacă a programelor pe o limbă cu pointeri sau nu.
Indicii sunt un mod de-a face cu diferența între un mâner la un obiect și un obiect în sine. (ok, nu neapărat obiecte, dar știi ce vreau să spun, precum și în cazul în care mintea mea este)
La un moment dat, probabil că avea de-a face cu diferența dintre cele două. În moderne, limbaj de nivel înalt acest lucru devine distincția între copy-de-valoare și copia de referință. Oricum, este un concept care este de multe ori dificil pentru programatori de a înțelege.
Cu toate acestea, așa cum a fost subliniat, sintaxa pentru manipularea această problemă în C e urât, inconsistente, și confuz. În cele din urmă, dacă într-adevăr încerca să-l înțeleagă, un pointer va face sens. Dar atunci când veți începe de-a face cu pointeri la pointeri, și așa mai departe ad nauseum, devine foarte confuz pentru mine, precum și pentru alte persoane.
Un alt lucru important de retinut despre indicatori este faptul că acestea're periculoase. C este un maestru programator's limba. Se presupune că știu ce naiba te're face și, astfel, vă oferă puterea de a face lucrurile. În timp ce unele tipuri de programe, încă mai trebuie să fie scris în C, cele mai multe programe nu, și dacă ai un limbaj care oferă o mai bună abstracție de diferența între un obiect și mâner, atunci îți sugerez să-l folosească.
Într-adevăr, în multe modern C++ aplicații, este de multe ori cazul în care orice este necesar aritmetica pointerilor este încapsulat și abstracte. Am don't vreau dezvoltatorii de a face aritmetica pointerilor peste tot. Vrem un sistem centralizat, bine testate API care face aritmetica pointerilor la cel mai mic nivel. Efectuarea de modificări la acest cod trebuie să fie făcut cu mare grijă și teste extinse.
Cred că unul dintre motivele C indicii sunt dificil este faptul că acestea combina mai multe concepte care nu sunt chiar echivalente; dar, pentru că toate sunt puse în aplicare cu ajutorul pointerilor, oamenii pot avea un timp de greu delimitarea conceptelor.
În C, indicii sunt utilizate pentru a, printre alte lucruri:
În C, te'd defini o lista inlantuita de numere întregi astfel:
struct node {
int value;
struct node* next;
}
Indicatorul este acolo doar pentru că aceasta este singura modalitate de a defini o structură de date recursivă în C, atunci când conceptul nu are nimic de-a face cu astfel de un nivel scăzut detaliu ca adrese de memorie. Luați în considerare următoarele echivalentul în Haskell, care nu't necesită utilizarea de pointeri:
data List = List Int List | Null
Destul de simplu - o listă este fie gol, sau format dintr-o valoare și restul listei.
Aici's cum s-ar putea aplica o funcție " foo " pentru fiecare caracter al unui șir de caractere în C:
char *c;
for (c = "hello, world!"; *c != '\0'; c++) { foo(c); }
În ciuda de asemenea, folosind un pointer ca un iterator, acest exemplu are foarte puțin în comun cu cel precedent. Crearea unui iterator care puteți incrementa este un concept diferit de definind o structură de date recursivă. Nici conceptul este deosebit de legat de ideea de o adresă de memorie.
Aici este o funcție semnătura găsite în fluent:
typedef struct g_list GList;
void g_list_foreach (GList *list,
void (*func)(void *data, void *user_data),
void* user_data);
Whoa! Ca's destul de o gură de void*
's. Și-l's toate doar pentru a declara o funcție care reiterează o listă care poate conține orice fel de lucru, aplicând o funcție pentru fiecare membru. Compara-l la modul "harta" este declarată în Haskell:
map::(a->b)->[a]->[b]
Ca's mult mai simplu: "harta" este o funcție care are o funcție care convertește un " a " la "b", și se aplică-l la o listă de " a "'s pentru a produce o listă de " b " 's. La fel ca în funcția C g_list_foreach
, harta
nu't nevoie să știu nimic în definiție proprie cu privire la tipurile la care acesta va fi aplicat.
Pentru a rezuma:
Cred C indicii ar fi mult mai puțin confuz dacă oamenii învățat prima dată despre structuri de date recursive, iteratori, polimorfism, etc. ca concepte separate, și apoi a învățat cum pointerii pot fi folosite pentru a implementa aceste idei în C, mai degrabă decât de zdrobire toate aceste concepte împreună într-un singur subiect de "indicii".