Jump to content

SUBIECTE NOI
« 1 / 5 »
RSS
Sediment de la pastilele efervesc...

Sfat achizitie FIat Tipo

Card access Business Lounge(s) In...

Gaura prin armatura, cum dam?
 Cand apare Patria mea esti tu?

Program masina spalat / intretinere

Upgrade display Lenovo Ideapad 32...

Atentat Terorist Strasbourg
 Samsung ar avea de gand sa intre ...

PLUS SPORT...HOCKEY

Alegere marca utilaje tamplarie pal

nitrocalcar periculos?
 Recomandare termopan

Sfaturi achizitie Dacia Logan

ABS aprins in bord

"De ce ti-e frica nu scapi"
 

Lucruri pe care orice programator ar trebui să le știe

* * * * * 2 votes
  • Please log in to reply
32 replies to this topic

#1
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 25,058
  • Înscris: 10.08.2006
Acest articol este o încercare de a prezenta o imagine de ansamblu cu destule detalii tehnice, interconectate, pe care am observat ca multi dintre cei ce întreaba pe forum nu le stiu. Contine lucruri pe care mi le-am sintetizat eu singur, de-a lungul timpului. Daca peste 50% din lucruri îti sunt noi, îti recomand citirea sa de doua ori, pentru ca nu vei putea sintetiza toate lucrurile dintr-o singura citire (nimeni nu poate).
  • De ce am nevoie pentru a invata sa programez?
      Cel mai important lucru de care ai nevoie nu este un program sau o carte, ci modul tau de gandire - the mindset.
    • Curiozitatea este un factor foarte important. Trebuie sa fii curios cum functioneaza lucrurile, si sa doresti sa intelegi in adevaratul sens al cuvantului. Punctele urmatoare iti vor numi programe precum "compilator" sau "IDE", dar nu te multumi doar cu atat. Cauta sa intelegi ce implica procesul de compilare, de unde stie un compilator ce trebuie sa faca, din ce pasi este compusa compilarea, ce rezultat are compilarea, s.a.m.d. Probabil stii ca "a programa" inseamna a crea programe - dar te-ai intrebat vreodata ce este defapt un program, ce este defapt un proces? Gandeste, cerceteaza si analizeaza profund termenii si "tehnologiile" de care te lovesti.

        De exemplu, daca ti-e lene, si nu simti dorinta de a intelege procesul de compilare, ci "I just want the damn program to click around", atunci nu are rost sa iti omori timpul si energia cu programarea - nu ai avea succes, incearca altceva care te pasioneaza.
    • Si asa am ajuns la al doilea criteriu - terminologia. Invat-o, bine si corect, si foloseste-o. Daca setul de scule de programare folosite este lancea ta de programator, atunci terminologia este varful lancei. Care e diferenta dintre un toiag tocit, si o lance fara varf? Exact, nici una. Nu te apuca sa folosesti termeni pe care nu-i intelegi, ci documenteaza-te inainte. Cu o lance ascutita:

      • te vei putea intelege mai usor cu alti programatori. Tu ii vei intelege pe ei, si ei pe tine
      • pe masura ce termenii intelesi de tine devin mai complecsi, vei putea acumula cunostinte din ce in ce mai complexe bazate pe cele anterioare, in ritm exponential. La inceput ti se va pare frustrant, insa daca vrei sa devii bun, oricum va trebui sa inveti termenii odata si-odata. Deci de ce sa nu faci totul ca la carte de la bun inceput?
    • Trebuie sa fii inteligent. Inteligenta nu se masoara in notele de la scoala, ci in cat de complexe sunt gandurile pe care ti le faci tu singur, fara impulsuri din exterior. Obisnuiesc sa le numesc "inteligenta reproductiva" si "inteligenta productiva".

        De exemplu, ai incercat vreodata sa-ti argumentezi existenta sau inexistenta divina? Te-ai folosit de citate sau ce ai auzit de la altii? Daca da, atunci aia se numeste "inteligenta reproductiva".

        Deci cauta un subiect care te intereseaza, bulverseaza, sau incita la gandire, si incearca sa argumentezi productiv, dezvoltand un corp de idei propriu, pe care-l intelegi. Cat de departe ajungi? Crezi ca ce citesti acum e mult blabla? In esenta, tot ce am facut a fost sa unific cele trei caracteristici ale modului de gandire necesare in programare mentionate pana acum.

        Nota: alt criteriu este sa nu-ti fie teama de matematica. Sa zicem ca cunostintele tale de matematica nu conteaza, insa cu cat stii mai multa, cu atat vei fi un programator mai bun, scriind algoritmi mai performanti.
    • Si astfel am ajuns la a patra caracteristica: esenta, profunzimea in gandire. In programare vei scrie cod, te vei uita la el, si vei fi fericit "ca merge". Tin sa te dezamagesc, nu codul in sine este opera de care ar trebui sa fii mandru, ci felul in care ai combinat tot ce stii. Pe baza intelegerii termenilor si tehnologiilor, ai reusit sa combini intr-un mod profund concepte pe care initial le-ai acumulat separat. Si nu din greseala am folosit "acumulat" in loc de invatat, ci pentru a uni cele doua caracteristici: curiozitatea si profunzimea.
    • Astfel, voi inlantui a cincea caracteristica a mindset-ului de profunzime: atunci cand iti spui "da, am reusit sa-l fac sa mearga". Ce este gresit cu acest mod de gandire? Incearca sa gandesti profund si sa gasesti singur explicatia, pe baza celor spuse pana acum. Abia apoi verifica-te, citind mai jos.

        Nu este suficient ca un program, scris si gandit profund de tine, pe baza propriei inteligente productive, sa "mearga pentru ca merge", ci pentru ca asa l-ai gandit tu sa functioneze. Altfel spus:
    • nu lasa nimic la voia intamplarii. Programarea este o "stiinta" exacta, calculatoarele sunt automate finite, mai exact spus, "ideile", algoritmii pe care ii poti transmite unui procesor sunt asa.
    • in spiritul punctului anterior, imaginatia joaca un rol important: trebuie sa-ti imaginezi toate felurile de input, chiar si cele pentru care programul nu este gandit defapt.

      In plus, trebuie sa incerci sa-ti dezvolti capacitatea de analiza si sinteza. Este un proces anevoios si continuu, si merge mana-n mana cu imaginatia, insa la un moment dat iti vei dezvolta un simt* pentru cum trebuie impartita o problema mai mare in subprobleme care nu numai ca sunt mai mici, dar sunt si reutilizabile si (cu timpul) fara greseli (bug-free).

      Reutilizarea codului in noi proiecte nu numai ca va conduce la mai putine erori (deoarece codul e bug-free), ci iti va creste si ritmul de rezolvare a problemei, sau vei putea aborda probleme mai complexe fara sa te concentrezi mereu asupra acelorasi (sub)probleme simple si frustrante pe care le-ai rezolvat deja in proiectele trecute.

      Cu ajutorul imaginatiei si in spiritul reutilizarii codului, iti poti imagina ce functii sau clase noi trebuie sa introduci, care nu numai ca-ti rezolva problema curenta, ci pe care le vei putea reutiliza in proiectele urmatoare.
      ---
      * acesta este "secretul meseriei" in programare
    • Invata engleza. Obisnuieste-te sa acumulezi cunostinte in engleza, deoarece, resursele in engleza sunt cele mai actuale si cele mai corecte. Cele in romana pur si simplu sunt inconsistente, datorita lipsei celui mai important aspect: terminologia
    • Sfat pentru liceeni: sunteți în perioada în care vă dezvoltați gândirea, deci faceți exact asta: dezvoltați-vă gândirea inginerească.

      Nu aveți nevoie de îmbuibare cu algoritmi când sunteți în liceu, e suficient să explorați cât mai multe structuri de date și să vă gândiți la utilitatea fiecăreia în natură, apoi să rezolvați probleme cu ele, eventual combinate. Nu probleme din culegere, ci probleme proprii, inventate - chiar dacă e vorba doar despre parcurgerea unui arbore sau a unui graf.

      Învățați în primul rând limbajul foarte bine. Apoi treceți la cel puțin alte două limbaje - învățate foarte bine. Nu contează care, fiecare limbaj îți lărgește orizontul.

      Apoi folosiți diferite biblioteci, librării, vedeți cum rezolvă și alții probleme, cum scriu cod și de ce. Implicați-vă în open-source la chestii simple - exersați înțelegerea și corectarea de cod care n-a fost scris de voi. Obișnuiți-vă să citiți specificații formale și să explorați necunoscutul.

      Acest gen de aptitudini sunt mult mai importante în perioada în care încă ești în formare.

      Când veți intra la facultate, veți avea toate sculele necesare pentru a vă focusa pe problemele cu adevărat interesante, rezolvate prin algoritmică/matematică, fără a mai pierde timpul cu acele lucruri mărunte pe care le știți deja.

      PS: "Nu aveți nevoie de îmbuibare cu algoritmi" nu înseamnă să fugiți de algoritmi. Când rezolvați probleme practice, veți vedea la ce e bună sortarea unui vector sau generarea tuturor permutațiilor, sau care poate fi legătura dintre arbori și O(log(N)), etc - ideea este: dezvoltați-vă intuiția față de acest gen de întrebări, e mult mai importantă decât algoritm peste algoritm peste algoritm. Aveți timp destul pentru algoritmi, și oricum mereu veți da de ceva algoritmi specializați de care n-ați auzit până atunci și pe care trebuie să-i combinați. Iar în acest caz intuiția, soft skills și capacitatea de a lucra cu necunoscutul sunt mult mai de ajutor decât "studierea timp de 4 ani a algoritmilor" (care între noi fie vorba, în școală e o rasoleală oricum).
  • Cu ce sa incep, cu C sau cu C++?
    Cu C. Motive:
    • tot ce inveti intr-unul din limbaje poate fi usor transpus in celalalt, deci ce inveti in C iti va fi util in C++ mai tarziu
    • C-ul pur este mai curat decat "C++ cu cin si cout". In C, vei programa procedural. Treci la C++ cand incepi sa programezi obiectual.
    • C si C++ sunt ambele limbaje foarte puternice si des folosite.
    • C++ nu este neaparat mai bun decat C. Exista lucruri care sunt cel mai bine facute in C si nu in C++
    • Incepand cu C, te poti opri la un moment dat (dupa mine, punctul ideal este dupa intelegerea pointerilor si lucrul dinamic cu memoria) si intelege ce faci cu adevarat: ce este un program, un proces, ce inseamna compilare, linkare, si in mod ideal si un mic excurs in ASM (1-2 saptamani, poate mai mult, dupa interes);
      C++ insa iti ascunde, sau mai bine zis, prin multitudinea de "features" pe care le pune la dispozitie, nu iti stimuleaza curiozitatea catre low-level.
    • Daca in liceu (sau chiar facultate?) ti s-a spus ca programezi in C++, insa nu ai intalnit sau folosit termeni precum clasa, template, STL, atunci tin sa te dezamagesc: limbajul pe care-l folosesti este defapt C, si tot ce ai folosit din C++ sunt cel mai probabil obiectele std::cin si std::cout declarate in iostream (numit mai sus "C cu cin si cout")

      View Postdaimon, on 25th May 2009, 16:21, said:

      O mica obiectie, daca îi pot spune asa, legata de ordinea învatarii limbajelor. La facultatea mea (si din câte stiu, la toate), ordinea de învatare a limbajelor este C - CPP - JAVA. Or, pe mine ca beginner, C m-a înnebunit. Pâna si Pascal avea un mod primitiv pentru a declara obiecte, C în schimb m-a azvârlit în hardware, calcule cu pointeri prin stive, si alte chestii care pe un începator sigur nu-l coafeaza. Nu-i vorba de skill, e vorba de atractivitate. Bun, noi trebuie în final sa stim tot, deci nu "se pune". Însa ..

      Eu unul sugerez oricui sa înceapa cu Java sau Cpp, partea cu obiecte. E intuitiva si pentru tembeli, testat în practica. Poate nu ajungi mai mult de un code-monkey cu asta, dar macar faci primul pas si nu-ti bagi piciorul dupa câteva scanf() mai dure. Plus ca un obiect pot sa ti-l desenez folosind UML, si are logica direct vizual. Nu degeaab un program C, odata ce creste, devine din ce în ce mai greu de depanat, daca nu l-ai împartit de la început prin fisiere si headere, ca sa nu încurci lucrurile.

      Just my two cents..
      Eu unul nu am avut probleme cu C ca primul meu limbaj. E adevarat, teoretic facusem algoritmica in pascal la scoala (in generala).

      Teoretic, practic insa la scoala invatam algoritmii pe de rost si nu pricepeam nici macar de ce se numeste "writeln" si nu "readln", eram de parere ca ar trebui numite invers (nu pricepeam ca sunt numite din perspectiva masinii, nu a omului). Nu radeti, atat de jalnic eram la programare Posted Image

      In timpul liceului m-am apucat de C (ca hobby, nu la scoala), si fara profesor, doar cu carti si resurse de pe web, am inteles zic eu mai mult decat ma asteptam. Deci cred ca asta cu limbajul tine mai mult de cat de copt la minte, dar mai ales cat de entuziasmat esti cand te apuci sa studiezi, si nu de limbajul in sine. Totul poate fi inteles daca te pasioneaza, chiar si pointerii, si chiar si fara profesor (si ca sa plusez, chiar si in limbi straine pe care nu le stapanesti cum trebuie: engleza (teoretic o invatam de 5 ani, practic insa de 2 ani - si la asta eram mizerabil in generala Posted Image) si germana (de 1 an, si totusi am inteles bine multe cu cartea de pe pronix.de - una din cele mai bune carti IT care mi-a cazut in mana si pe care i-o recomand oricarui vorbitor de lb. germana))

      Deci stereotipul cum ca C este greu este doar o chestiune de atitudine si entuziasm, care sunt si abordate in punctul 1. al sfaturilor mele (care au bineinteles o alura de subiectivitate - stim bine ca obiectivitate absoluta nu exista).

      PS: nu degeaba am tinut sa mentionez ce elev mediocru eram in relatia mea cu informatica, ci tocmai pentru a sublinia ca nu sunt cine stie ce geniu si ca entuziasmul chiar conteaza.

      PPS: un debugger este pentru elevul C similar cu UML pentru elevul C++/Java/Delphi - iti arata exact ce se intampla, doar ca low-level. E foarte important sa inveti sa-l manuiesti (cel putin cateva comenzi de baza), chiar daca iti e greu ca incepator.
  • Ce fac defapt cand programez?
    Cand programezi, scrii un text cu o anumita sintaxa si semantica, text care contine descrierea unor algoritmi. Acest text urmeaza sa fie convertit din limbajul folosit si inteles de oameni (C sau C++ de exemplu), intr-un limbaj binar, inteles de CPU.
  • De ce are un compilator nevoie de o sintaxa fixa?
    Deoarece un compilator nu este nimic altceva decat un program, iar programele care sunt executate de procesoarele actuale nu sunt inteligente.
  • Ce inseamna defapt sintaxa?
    Sintaxa unui limbaj de programare este setul de reguli din care este compus un limbaj. De exemplu, o regula sintactica ne spune ca un tip de date (de exemplu "int"), trebuie sa fie urmat de unul sau mai multe spatii sau linii noi, si apoi unul sau mai multi identificatori de variabile separati prin virgula, iar la sfarsit caracterul ";". Alta regula sintactica ne spune ca un identificator este o litera sau caracterul _, urmat de zero sau mai multe litere, caracterul _, sau cifre.

    Atat si nimic mai mult. Nu este corect sa te intrebi "care este sintaxa pentru a afla daca un numar este prim?", ci "care este algoritmul pentru a afla daca un numar este prim sau nu?".
  • Ce inseamna semantica?
    Semantica inseamna semnificatie. "int" de exemplu inseamna "numar intreg", iar "int i;",altfel spus "int" urmat de un identificator inseamna "declara si aloca identificatorul i de tip numar intreg"
  • Ce este un algoritm?
    Un algoritm este un set de instructiuni care rezulta in cod executat de CPU pentru a rezolva o problema. De exemplu, pentru a numara descrescator de la 10 pana la 0, algoritmul ar arata astfel:
    1. initializeaza numarul cu valoarea 10
    2. afiseaza numarul
    3. daca numarul este mai mare decat zero, atunci
    3.1. scade 1 din numar
    3.2 sari la pasul 2
    4. altfel
    4.1 sfarsit program

    Posibile solutii in C ar arata astfel:
    int i;
    for(i=10;i>=0;i--) {
    printf("%d ",i);
    }
    
    sau (tradus "cuvant cu cuvant"):
    int i;
    i=10;
    pas2:
    printf("%d ",i);
    if(i>0) {
    i--;
    goto pas2;
    }
    
    sau (cel mai elegant dupa parerea mea):
    int i;
    i = 10;
    do {
    printf("%d ",i);
    } while(i--);
    

    Atentie: "int i;" nu face parte din algoritm, pentru ca instructiunea e folosita de compilator la compilare, si nu va deveni parte a programului .exe rezultat ca atare, ci intr-un alt mod (vezi mai jos despre alocarea statica a memoriei).
  • Ce inseamna declararea unei noi variabile?
    Atunci cand declari o variabila, ceea ce faci se numeste alocarea statica a memoriei. Dupa cum iti poti imagina, daca exista o alocare statica, atunci exista si una dinamica, dar despre asta mai tarziu.

    Corect spus insa, nu tu aloci memorie, ci ii spui compilatorului sa o faca. Acea variabila va fi "inclusa" in fisierul .exe generat. Din asta deducem ca cu cat declaram mai multe variabile, cu atat marimea fisierului .exe generat creste. Continuarea explicatiei in urmatoarea intrebare...
  • De ce o variabila are nevoie de un tip de date? (precum "int" in exemplul de mai sus)
    Variabila in sine, asa cum va fi folosita in programul .exe binar, nu va avea un tip de date explicit, insa compilatorul are nevoie de tipul de date pentru a sti cati bytes sa aloce, si deci implicit, cu cati bytes sa "mareasca" executabilul*.

    Pentru a afla cu cat creste un executabil** cu fiecare variabila, foloseste operatorul sizeof()***:
    printf("un int ocupa %d bytes",sizeof(int));
    

    ---
    * defapt, tipul de date este folosit de compilator si pentru a decide ce instructiuni binare pentru CPU sa scrie in executabil, pentru ca atribuirea unei valori de tip char are o alta instructiune CPU decat cea pentru atribuirea unei valori de tip integer (e doar un exemplu, dar exemple sunt o gramada)
    ** in practica, trebuie sa aloci static foarte multe variabile pentru a observa o diferenta in marime.
    *** sizeof() este un operator, nu o functie, si este evaluat la compilare, ca orice alt operator;

    intrebare de profunzime: pur teoretic, daca CPU-ul tau si sistemul de operare sunt pe 64 biti, si scrii si compilezi un program care afiseaza marimea unui integer, program pe care i-l trimiti si unui prieten cu un sistem pe 32 biti, de ce pe ambele calculatoare ar afisa "8", chiar daca pe calculatorul prietenului tau marimea unui integer este defapt 4?
  • Ce este defapt o variabila?
    O variabila, asa cum este ea folosita de un programator, este o colectie de patru elemente
    • un nume, sau identificator, util compilatorului si programatorului
    • o valoare
    • un tip de date, util compilatorului pentru a sti cum sa manipuleze acea variabila
    • o adresa de memorie
  • Cum aflu adresa de memorie a unei variabile?
    Folosind operatorul unar &, de exemplu:
    int i;
    printf("adresa variabilei i este %p",&i);
    

    In C, cu siguranta ai folosit deja scanf() pentru a citi valoarea unei variabile:
    int i;
    printf("introduceti i:");
    scanf("%d",&i);
    
    Ei bine, acel apel la scanf() se traduce cam asa: citeste un numar intreg (specificat de "%d") si salveaza-l la adresa variabilei i.
  • Cand foloseste compilatorul valoarea unei variabile, si cand foloseste adresa sa?
    In afara de cazul explicit in care folosesti operatorul &, compilatorul foloseste cele doua caracteristici (din totalul de patru) la compilare astfel:
    • atunci cand atribui o valoare (constanta, precum 10 mai sus, sau variabila) unei variabile i, compilatorul foloseste adresa acesteia:
      i = 10;
      j = n;
      
      prima instructiune se citeste "copiaza constanta 10 la adresa variabilei i", iar a doua "copiaza valoarea variabilei n la adresa variabilei j.
      In termeni tehnici, ceea ce se afla in stanga operatorului de atribuire se numeste left value, sau pe scurt lvalue.
    • In exemplul de mai sus insa, variabila n este tratata ca rvalue, iar pe compilator il intereseaza valoarea sa.
  • Ajutor! Toti termenii astia ma deruteaza!
    compilare, linkare, header, IDE, biblioteca

    Scopul acestei sectiuni este sa iti explice teoria si utilitatea practica a tuturor acestor notiuni. Categoric trebuie sa le intelegi. Vei instala un mediu curat de programare, de la zero, componenta cu componenta, si vei invata cum conlucreaza acestea si ce rol are fiecare.

    Intra pe http://tdm-gcc.tdragon.net/download si descarca "On-Demand Installer". Acest program va instala compilatorul numit gcc. Mai exact, va instala portul gcc pentru windows numit MinGW (minimalist GNU for windows), gcc fiind compilatorul standard pentru lumea GNU/Linux.

    Un port al unui program este o adaptare a acelui program pentru o alta platforma.

    Vrem sa folosim TDM pentru a instala MinGW deoarece MinGW nu dispune de un instalator comun si fara el ar trebui sa instalam totul manual, ceea ce e destul de complicat.

    Muta instalatorul intr-un director nou creat C:\programare. Cu el vei putea si administra instalarile TDM deja existente (ca cea pe care urmeaza sa o creezi).

    Nota: poti instala orice oriunde vrei tu, insa in continuare voi folosi foarte mult aceste cai, deci ti-ai face un bine daca ai urma exact instructiunile mele.

    Daca ceva nu functioneaza cum trebuie si ai instalate alte scule de programare precum Dev-Cpp, Code::Blocks sau altele, dezinstaleaza-le temporar. Nu vei fi nevoit sa dezinstalezi Visual Studio sau Borland C++.

    Alege optiunea "Create" pentru a crea o noua instalare. In urmatoarea pagina, pentru "Installation Directory" introdu "C:\programare\compilator" (fara ghilimele).

    In rest, instalarea cu valorile predefinite ar trebui sa fie perfecta.

    Acum in Start->All programs->MinGW ai o legatura catre interfata CLI a sistemului de operare, cunoscuta sub windows si ca "ms-dos prompt". Ceea ce face aceasta legatura este practic sa importe niste setari prin fisierul "C:\programare\compilator\mingwvars.bat".

    Cand ii dam drumul, ne va pune automat in directorul C:\programare\compilator. Asta nu este neaparat o problema, insa mult mai bine ar fi daca ne-ar pune intr-un director special unde ne vom tine noi proiectele. Insa pentru asta trebuie mai intai sa cream un astfel de director. Deci creaza directorul C:\programare\proiecte.

    Apoi deschide fisierul .bat amintit mai sus cu Notepad si adauga-i la sfarsit instructiunea pentru a schimba directorul curent (comanda cd vine de la change directory) in "proiecte":
    @cd "..\proiecte"
    

    Inchide orice ms-dos prompt daca il mai ai deschis si relanseaza-l. Acum ar trebui sa te afli in C:\programare\proiecte.

    Bun, avem totul setat si organizat intr-un mod curat. Acum hai sa cream un nou proiect.
    Asta nu inseamna nimic mai mult sau mai putin decat a crea un nou subdirector in "proiecte" numit "hello", in care ne vom salva toate fisierele proiectului. Deci introdu comanda necesara:

    mkdir hello
    
    (mkdir = make directory)
    apoi schimba directorul curent in noul subdirector creat:
    cd hello
    
    Hai sa facem ceva ce probabil stii: sa scriem un cod sursa si sa testam in final daca totul functioneaza cum trebuie:
    edit main.c
    
    Apoi introdu codul sursa:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
    printf("hello world");
    return EXIT_SUCCESS;
    }
    
    (nota: la sfarsitul fisierului se afla o linie noua goala. Asta este parte din standardul actual si modern al limbajului C).

    In meniul editorului selecteaza File -> Save si apoi File -> Exit.

    Acum avem un fisier main.c care abia asteapta sa fie compilat. Deci introdu comanda:
    gcc -Wall main.c
    
    "-Wall" este un parametru si ii spune compilatorului sa genereze avertizari ("W" vine de la warnings) pentru toti ("all") pasii ce implica o compilare. Este bine sa pui -Wall de fiecare data, astfel compilatorul te va avertiza despre eventualele greseli pe care le-ai facut.

    Daca nu ai facut nicio greseala, atunci compilatorul nu va afisa nimic. El va genera insa un fisier a.exe.
    Il poti lansa in executie, introducand in prompt:
    a.exe
    

    Ar trebui sa vezi pe ecran textul "hello world".

    Fisierul se numeste din motive istorice "a.exe". Daca vrem ca outputul compilatorului sa se numeasca altfel, trebuie sa-i spunem asta prin parametrul -o <nume program>. "-o" vine de la "output". Asadar, ar trebui sa introducem:
    gcc -Wall -o hello.exe main.c
    

    Am testat deci instalarea si totul functioneaza perfect. Insa noi nu ne-am indeplinit misiunea: sa intelegem sculele pe care le folosim si cum functioneaza acestea, macar cat de cat. Asta ne va face niste programatori mai buni.

    Deci vom face cateva experimente pe care le vom analiza, pentru a ne putea explica cele mai importante concepte. Unele dintre experimente vor genera erori, ceea ce e dorit (eu vreau sa fac lucruri "anormale" pentru a demonstra ceva). Experimentele contin si explicatii teoretice, si demonstratii practice.
    • Experiment 1: compilare si linkare

      Compilatorul nostru, numit gcc.exe (gcc vine de la GNU C Compiler), ne-a compilat anterior codul sursa si a generat direct executabilul. Compilarea este constituita insa din mai multi pasi, de la codul sursa scris in C pana la fisierul executabil care contine instructiuni "executate" de electronica din CPU e drum lung. Pe rand, pasii acestia sunt:
      • a. preprocesare; preprocesorul proceseaza toate instructiunile dedicate lui, adica toate instructiunile care incep cu "#"; asta include si directiva preprocesor "#include" pe care am folosit-o si noi in programul nostru. "#include" practic include un fisier, iar rezultatul arata de ca si cum continutul fisierului inclus ar fi fost scris de noi manual in main.c.
        Pentru a vedea rezultatul acestui pas, trebuie sa ii pasam compilatorului parametrul "-E", care ii spune sa se opreasca imediat dupa pasul de preprocesare:
        gcc -Wall -E -o main.i main.c
        
        Deschide fisierul generat main.i si ... wow, o gramada de cod, sunt peste 600 de linii de cod C foarte valid si necesar pentru ca amarata noastra de functie main() (care se afla la sfarsitul fisierului) sa functioneze cum trebuie. Posted Image
      • b. asamblare; outputul acestui pas este inrudit cu un alt limbaj numit "limbaj de asamblare" (sau scurt: ASM). Instructiunile ASM coincid aproape 1:1 cu instructiunile binare care vor fi executate de CPU. Acesta este ultimul pas care va fi inteligibil destul incat sa ne ofere informatii despre ce va face procesorul. Outputul urmatorului pas (compilarea) este deja mult prea binar pentru un programator ordinar.

        Lui gcc ii spunem sa se opreasca dupa pasul de ansamblare cu parametrul -S:
        Putem refolosi fisierul preprocesat main.i, astfel:
        gcc -Wall -S -o main.s main.i
        
        sau ii putem spune compilatorului sa o ia de la capat, incepand cu main.c, sa salveze temporar un nou fisier preprocesat si sa-l asambleze pe acela, oprindu-se dupa pasul de asamblare:
        gcc -Wall -S -o main.s main.c
        
        In ambele cazuri, comenzile sunt aceleasi. Ceea ce difera este extensia fisierului de input (.i sau .c), in functie de care gcc decide ce este de facut (daca mai este necesara o preprocesare sau nu).

        Aruncand un ochi in fisierul asamblat main.s, ne regasim stringul ascii "hello world\0" care ne arata clar ca un string in C se termina in caracterul NUL (nu NULL), vedem si numele functiilor folosite precum "main" sau "printf", chiar daca acestea sunt precedate de unul sau doua "_", si mai vedem si instructiuni ASM. De exemplu instructiunea ASM
        call __main
        
        apeleaza functia noastra main(). Insa dupa cum observi, ea nu este nicidecum primul lucru executat de procesor (desi cu siguranta ti s-a spus la scoala "main - aici incepe programul"). Compilatorul a adaugat automat pentru noi alte instructiuni inaintea sa, instructiuni care "pregatesc" apelul efectiv. Pentru a intelege cu adevarat ce se intampla aici, este necesara studierea limbajului ASM.
      • c. compilare; in acest pas, datele preprocesate sunt compilate iar outputul se numeste "cod-obiect". Acest cod obiect contine practic instructiunile CPU care vor constitui programul nostru efectiv. Insa el nu contine detaliile care fac un executabil sa fie un executabil recunoscut ca atare de sistemul de operare. El contine insa referinte la alte resurse (mai mult sau mai putin) externe despre care vom vorbi intr-un experiment viitor, referinte pe care le-ai vazut si in fisierul ASM precum "_printf". Codul obiect contine, simplificat vorbind, instructiunile pure binare si nimic altceva.

        Si la acest pas putem refolosi outputul oricaruia dintre pasii anteriori ca input, insa ii putem spune lui gcc sa se opreasca dupa pasul de compilare cu parametrul -c:
        gcc -Wall -c -o main.o main.s
        
        sau
        gcc -Wall -c -o main.o main.i
        
        sau
        gcc -Wall -c -o main.o main.c
        
        Gcc va decide in functie de tipul inputului ce fisiere temporare trebuie sa genereze. Insa cum noi avem deja cel mai apropiat tip de input (fisierul asamblat) de ceea ce vrem (codul obiect), doar prima varianta are sens. Astfel putem creste viteza de compilare (deoarece pasii intermediari de preprocesare si asamblare nu mai sunt necesari).

        Insa de la orice input am pleca, outputul main.o va fi identic.

        Aruncand un ochi pe fisierul rezultat main.o, observam ca nu pricepem mare lucru, deoarece outputul compilarii nu mai este un text, cum a fost in cazul preprocesarii si al asamblarii, ci un fisier binar, cu instructiuni CPU. Acele instructiuni vor fi executate de circuitele electronice din CPU.

        Totusi ne regasim textul "hello world" si vedem si nume precum "printf" sau "main", functii care apar si in program.
        Deci rezultatul compilarii main.o cel putin pare a reflecta in mare ce am scris noi in main.c, si deci secvential si in main.i si main.s.
      • d. linkare
        Acesta este ultimul pas in traducerea lui main.c din limbajul C in limbajul binar al CPU-ului. Deoarece e ultimul pas, lui GCC nu trebuie sa-i spui sa se opreasca la un anumit pas cum ai facut cu -E pentru procesare, -S pentru asamblare sau -c pentru compilare. Nu trebuie decat sa-l hranesti cu input provenit de la oricare output din pasii anteriori sau cu main.c insusi. GCC isi va genera fisierele temporare de care are nevoie. Cum noi avem insa outputul cel mai apropiat de ceea ce vrem, cel mai eficient este sa-l linkam pe acesta:
        gcc -Wall -o hello.exe main.o
        

        A linka inseamna a crea legaturi. Pasul acesta creaza legatura dintre codul obiect din fisierul .o si biblioteca standard C (pe scurt: libc, stdlibc sau sub windows vei mai intalni si numele de "msvcrt"). In experimentul urmator vom vedea ce este aceasta stdlibc si ce rol are.
      • Concluzii
        Am aflat ca ceea ce numeam pana acum "compilare" este defapt o serie de pasi (preprocesare, asamblare, compilare, linkare) ordonati, iar fiecare pas are ca input outputul pasului anterior. Totul pleaca de la codul sursa scris de programator, si se ajunge la fisierul executabil, care este (in principiu) inputul unei alte componente, de data aceasta una hardware: procesorul insusi!

        Compilarea este defapt, tehnic corect vorbind, doar acesta "al treilea pas" din ceea ce intelegeam inainte prin "compilare".

        Compilatorul GCC ne lasa sa ne oprim dupa oricare dintre acesti pasi cu unul dintre parametrii -E, -S, sau -c. Astfel putem vedea exact ce se intampla sub capota.

        In experimentele viitoare vei vedea ca in mod obisnuit nu vei avea de-a face cu datele de iesire ale pasilor de preprocesare si asamblare, insa des vei fi confruntat cu utilizarea parametrului -c, chiar si in viata de zi cu zi de programator.
      Urmatoarele experimente vor vorbi mai indeaproape despre sculele de programa existente, despre biblioteci, IDE-uri, si alte detalii.

      Posted Image Asigura-te ca ai inteles mai intai toate lucrurile de mai sus pentru a castiga o claritate cat mai mare din cele ce urmeaza - altfel nu vei intelege mare lucru din experimentele viitoare. Posted Image
  • Ce este biblioteca standard?

    Biblioteca standard este o biblioteca care ii este pusa implicit la dispozitie unui cod sursa. Limbajul C are o biblioteca standard numita colocvial stdlibc, limbajul C++ are o biblioteca standard numita colocvial stdlibc++.

    Bibliotecile standard vin de obicei la pachet cu setul de scule (compilator, linker, etc) de programare folosit. Astfel se face ca gcc (mingw) ne ofera o biblioteca standard, visual c++ ne ofera o alta biblioteca standard, s.a.m.d.

    Toate aceste biblioteci respecta mai mult sau mai putin un standard comun (standardul ISO C, care specifica intregul limbaj C: http://en.wikipedia....ry#ISO_Standard ), de aceea nu conteaza cu ce compilator compilam un program care foloseste doar lucrurile standard.

    Noi vom folosi ca si pana acum compilatorul gcc cu biblioteca sa standard.

    Compileaza codul sursa anterior main.c al programului "hello world" astfel:

    gcc -nostdlib main.c
    

    Parametrul -nostdlib ii spune linkerului (numit "ld.exe", un program la fel ca "gcc.exe") apelat in spatele cortinei de compilatorul gcc.exe, sa nu includa biblioteca standard in programul nostru.

    In output vedem concret cum gcc a generat acele fisiere temporare cod-obiect (.o), si cum ld ("ld returned 1 exit status") returneaza eroare (acel "return 0" pe care il scrii tu in programe ii spune sistemului de operare ca procesul tau s-a terminat cu succes; ei bine aici programul ld.exe face pe undeva "return 1" pentru a-i spune apelantului (aici gcc.exe, si nu sistemul de operare, pentru ca nu ai introdus tu "ld.exe" pentru a linka manual codul obiect al lui main.o cu biblioteca standard) ca procesul s-a terminat cu o eroare:

    Quote

    Temp\ccD9bI55.o:main.c:(.text+0xa): undefined reference to &#96;__main'
    Temp\ccD9bI55.o:main.c:(.text+0x16): undefined reference to &#96;printf'
    collect2: ld returned 1 exit status

    Hai sa incercam acelasi lucru si manual, generarea de cod obiect, si link-area manuala:

    Introdu:
    gcc -c main.c
    

    Acum pentru a linka, invoca linker-ul ld (toti parametrii trebuiesc scrisi pe o singura linie):

    Quote

    ld -Bdynamic
    c:/programare/compilator/lib/crt2.o c:/programare/compilator/lib/gcc/mingw32/4.4.1/crtbegin.o
    -LC:\programare\compilator\lib\gcc\mingw32\4.4.1 -Lc:/programare/compilator/lib/gcc -LC:\programare\compilator\mingw32\lib -LC:\programare\compilator\lib
    main.o
    -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt -luser32 -lkernel32 -ladvapi32 -lshell32 -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt
    c:/programare/compilator/lib/gcc/mingw32/4.4.1/crtend.o
    (Nota: 4.4.1 ar trebui sa fie inlocuit cu versiunea gcc folosita de tine)

    Asupra parametrului -Bdynamic voi reveni mai tarziu.

    Dupa cum vezi, sunt link-ate mai intai doua fisiere cod-obiect pe care nu le-am scris noi: crt2.o si crtbegin.o. Hai sa vedem ce se afla in crt2.o.
    Introdu:
    cd C:\programare\compilator\lib
    nm crt2.o

    aceasta ultima comanda va afisa toate simbolurile dintr-un fisier cod-obiect (.lib, .o, .a, .la). Printre acele simboluri vedem si simbolul "__main" despre care se plangea ld ca nu-l gaseste atunci cand am folosit parametrul -nostdlib pentru gcc.

    Dupa cum stii din unele din punctele anterioare, simbolul "printf" nu exista, este numit "_printf", deci hai sa ne uitam dupa el (ca sa ne asiguram ca exista si ca nimic nu e magic) in biblioteca msvcrt:

    nm c:\programare\compilator\lib\libmsvcrt.a > libmsvcrt.nm

    apoi deschide fisierul text nou creat libmsvcrt.nm si cauta dupa _printf - vei vedea ca e acolo.

    Parametrul -Bdynamic de mai sus spune ca se va linka dinamic cu biblioteca standard. O linkare dinamica sub windows inseamna ca intr-un final se vor apela functii aflate in fisiere .dll (precum _printf) - de exemplu in msvcrt70.dll (7.0 e versiunea). Asa cum exista o linkare dinamica, exista si una statica - o linkare statica (analog cu o alocare statica a unei variabile) inseamna ca acea biblioteca ar deveni parte a fisierului nostru .exe, si in consecinta marimea lui ar creste cu cativa Mb.

    Parametrii de mai sus care incep cu -L<cale> ii spun linkerului in ce directoare sa se uite dupa biblioteci apoi, atunci cand acestea vor fi specificate cu parametrii -l<nume>.

    De exemplu biblioteca msvcrt se afla in directorul c:/programare/compilator/lib/. Standardul de denumire al fisierelor ce constituie biblioteci este lib<nume>.a, <nume> putand fi apoi folosit la linia de comanda pentru a linka (a lipi unul de altul) mai multe biblioteci si fisiere cod-obiect (precum main.o al nostru - care si el este specificat pe linia de comanda).

    Uita-te atent la acesti parametri pentru ld si exploreaza si singur, pentru ca nu voi mai intra in detalii.
    Toate bibliotecile specificate cu parametrul -l sunt necesare pentru ca programul final sa functioneze sub windows.

    La sfarsit de tot mai adaugam si codul-obiect crtend.o, care se ocupa cu terminarea corecta a procesului.
    Deci contrar credintelor tale de pana acum "return 0" nu este nicidecum ultima instructiune executata de un proces, la fel cum nici "main" nu este primul lucru executat de un proces - se trece mai intai prin functia __main care "pregateste" mai intai functia main() scrisa de tine pentru a fi apelata.

    Concluzia: ceea ce scriem noi, un amarat de fisier main.o, este doar o mica parte din program. Acest cod va fi "inconjurat" de cateva biblioteci in functie de sistemul de operare (aici: windows).
  • O credinta populara

    O credinta populara spune ca fisierele header, precum stdio.h, contin functii. Nimic mai gresit. Functiile sunt implementate in biblioteci, niste fisiere binare, pe care ai invatat sa le inspectezi cu comanda "nm".

    Atunci cand  linkam (ld.exe) doua fisiere cod-obiect .o, (sau .la sau .a), nu stim ce se afla in acele fisiere - doar le "lipim" unul de altul si rezulta un fisier executabil.

    Insa un compilator (gcc.exe) are nevoie sa stie cum arata o functie inainte de a o apela, informatiile necesare fiind tipul de date al parametrilor si ordinea acestora.

    Cumulul intre "tip de date" si "ordine" al parametrilor unei functii constituie semnatura functiei. Multi numesc aceasta semnatura si "antetul functiei", dar acest termen face mai mult aluzie la codul sursa C - "semnatura functiei" in schimb indica natura binara a codului obiect.

    Haideti sa vedem ce se intampla atunci cand compilam un cod sursa fara a-i spune compilatorului semnaturile functiilor (precum _printf din libmsvcrt.a):

    cod:
    int main(void) {
    printf("hello world");
    return EXIT_SUCCESS;
    }
    
    compilare:
    gcc -Wall -o hello.exe main.c

    Ne va spune:

    Quote

    warning: implicit declaration of function 'printf'

    ATENTIE: compilatorul spune foarte corect "declaration", pentru ca intr-adevar este vorba despre declararea functiei printf. Implementarea functiei printf (instructiunile binare CPU ce o definesc) sta bine mersi in libmsvcrt.a

    Deci hai sa ii dam compilatorului ce ne cere. Cod:
    int printf(char* fmt, ...);
    #define EXIT_SUCCESS 0
    
    int main(void) {
    printf("hello world");
    return EXIT_SUCCESS;
    }
    
    compilam
    gcc -Wall -o hello.exe main.c
    

    Dupa cum vezi, in loc sa ne folosim de declaratia functiei printf existenta in stdio.h si a constantei EXIT_SUCCESS din stdlib.h, pur si simplu le-am declarat direct in codul nostru sursa intr-un mod similar in care sunt declarate in acele fisiere.

    Concluzie: mare atentie la cei doi termeni: declararea (antetul, semnatura) unei functii vs. implementarea unei functii (instructiunile CPU din codul obiect sau lista de instructiuni din codul sursa - blocul de instructiuni al functiei - caci si functii ca printf trebuiesc sa fi fost implementate pe undeva, nu pica din cer! - deschide C:\programare\compilator\include\stdio.h si convinge-te ca acolo chiar nu se afla absolut nicio implementare!).
  • Lucrul cu mai multe fisiere cod-obiect intr-un proiect

    Intr-unul din experimentele anterioare am vazut cum codul nostru obiect main.o era linkat cu alte coduri obiecte .o si biblioteci .a pentru a forma fisierul .exe final.

    Ei bine putem crea si noi mai multe fisiere .o (vom vorbi si despre biblioteci intr-un punct urmator) pe care sa le linkam alaturi de acest "main.o". Asta duce la o reutilizare mai usoara a codului.

    Reutilizarea codului se face prin impartirea functionalitatii mari in functionalitati mai mici, prin functii. Aceasta impartire in functii se mai numeste si modularizarea codului. Fisierele .o sau bibliotecile .a ar putea fi deci considerate module de functii.

    Deci vom face un nou proiect "prime", ceea ce nu inseamna nimic mai mult decat un director nou, gol, in workspace-ul nostru "C:\programare\proiecte".

    Comenzi:
    cd C:\programare\proiecte
    mkdir prime
    
    Si gata proiectul :-)

    cream un nou fisier prime.c:

    int is_prime(unsigned int n) {
    unsigned int i;
    for(i=2; i <= n/2; i++) {
    if(n%i == 0) {
    return 0;
    }
    }
    return 1;
    }
    
    (nota: stiu ca exista algoritmi mai buni; aici vreau sa ilustrez altceva, nu sa fac kung-fu in algoritmica).

    Perfect, acum il putem compila:
    gcc -c -Wall prime.c
    
    si ne-am ales cu un fisier prime.o

    scriem main.c:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
    unsigned int n;
    printf("numar: ");
    scanf("%u", &n);
    if(is_prime(n)) {
    printf("%u este prim", n);
    }
    else {
    printf("%u nu este prim", n);
    }
    return EXIT_SUCCESS;
    }
    
    Compilam:
    C:\programare\proiecte\prime> gcc -Wall -c main.c
    Primim eroarea:

    Quote

    main.c: In function 'main':
    main.c:8: warning: implicit declaration of function 'is_prime'

    Dupa cum observam (inca o data), compilatorul nu stie cum arata semnatura functiei is_prime(). Ne da totusi doar o avertizare, nu o eroare, si ne genereaza totusi codul obiect main.o, deci, desi stim ca ar trebui sa adaugam declaratia functiei is_prime inaintea implementarii lui main(), hai sa testam care ii sunt limitele si ce se intampla daca nu o facem.

    Deci trecem la pasul urmator de a linka cele doua fisiere cod-obiect:
    gcc -o prime.exe -Wall main.o prime.o
    
    apoi executam prime.exe

    Procesul prime.exe va cere numarul, insa atunci cand va ajunge la apelul functiei is_prime, va crapa!

    Posted Image Inca o dovada ca chiar si "doar avertizarile" trebuiesc luate in serios si reparate!

    Deci adaugam semnatura functiei inaintea lui main():

    int is_prime(unsigned int);
    
    Atentie: semnatura unei functii nu are nevoie de identificatorii parametrilor, ci doar de tipul de date!

    Acum recompilam main.c (prime.c e deja compilat, si nu am schimbat nimic in acel fisier, deci prime.o e la fel de util ca si inainte):

    gcc -c -Wall main.c
    
    si linkam:
    gcc -o prime.exe -Wall main.o prime.o
    

    Perfect, prime.exe functioneaza. Totusi, avem o problema: pe parcurs vom mai adauga functii in prime.c pentru lucrul cu numerele prime. Problema este ca
    1. in toate celelalte fisiere .c unde vom vrea sa apelam acele functii, va trebui sa declaram functiile cu exact aceleasi semnaturi ca in prime.c. Daca cumva o semnatura a unei functii se schimba in prime.c, va trebui sa cautam in toate celelalte fisiere si sa modificam semnatura
    2. daca vrem sa le dam prietenilor acest fisier prime.c, lucrul pentru ei va deveni si mai dificil, deoarece trebuie sa fie mereu la curent cu schimbarile pe care le faci in prime.c, daca le faci

    Atunci cand tu schimbi semnatura unei functii precum is_prime() care este gandita a fi reutilizabila (de tine si chiar si de alti programatori), iar schimbarea o faci asa incat toti utilizatorii tai vor trebui sa-si schimbe codul lor sursa in care apeleaza acea functie, vorbim despre "breaking the backwards compatibility" (abreviat BC).

    Impotriva BC breaking nu te ajuta decat sa-ti gandesti mai bine antetele functiilor din start. In rest, putem usura munca celorlalti preluand asupra noastra nu numai responsabilitatea asupra codului din prime.c, ci si a semnaturilor functiilor din prime.c.

    Si dupa cum stii, semnaturile functiilor se scriu in fisiere header .h. Deci vom scrie un nou fisier prime.h:

    #ifndef PRIME_H_
    #define PRIME_H_
    //check if a number is prime, return 0 if it isn't, 1 if it is
    int is_prime(unsigned int);
    #endif //double-inclusion guard
    
    definitia unui simbol cu ajutor preprocesorului precum PRIME_H_ mai sus se numeste o garda impotriva includerii duble. Daca vreun utilizator (adica un alt programator) al viitoarei noastre biblioteci "prime" include din greseala sau din nestiinta de doua ori fisierul "prime.h", aceasta garda va avea ca efect sarirea peste intregul continut al fisierului, si astfel nu vom vedea erori de la compilator de genul "is_prime already defined".

    acum, putem sterge declaratia functiei is_prime din main.c si folosi fisierul prime.h in schimb:

    #include <stdio.h>
    #include <stdlib.h>
    #include "prime.h"
    
    int main(void) {
    unsigned int n;
    printf("numar: ");
    scanf("%u", &n);
    if(is_prime(n)) {
    printf("%u este prim", n);
    }
    else {
    printf("%u nu este prim", n);
    }
    return EXIT_SUCCESS;
    }
    
  • crearea si folosirea unei biblioteci

    Hai sa transformam prime.c intr-o biblioteca reutilizabila numita "prime" (fisierul libprime.a)
    Nimic mai simplu:

    ar rcs libprime.a prime.o
    
    Si gata biblioteca :-)

    Acum putem scapa de prime.o:
    rm prime.o
    
    vom folosi in schimb biblioteca:

    gcc main.o -L. -lprime -o prime.exe
    

    Atentie: parametrii -L si -l ii cunosti dintr-un punct anterior! "." inseamna "directorul curent", deci linkerul se va uita si in "." dupa bibliotecile specificate cu parametrii -l<nume> urmatori.

    Distribuirea bibliotecii

    Acum poti distribui biblioteca ta libprime.a (care nu este nimic altceva decat o arhiva cu codul obiect al lui prime.o) prietenilor. Nu uita ca trebuie sa le trimiti si fisierele .h, care in mod normal contin si documentatia functiilor puse la dispozitie de libprime.

    Noua ta biblioteca libprime este la fel de biblioteca ca si "biblioteca standard C" stdlibc (care in cazul de fata este o colectie de fisiere .a, asa cum le-ai pasat lui "ld.exe" la un punct anterior).
  • Tutorial despre pointeri al lui Ted Jensen de la http://home.netcom.c...tr/pointers.htm
  • va continua
  • Întrebări și răspunsuri
    • Cum pot vedea cata memorie foloseste programul meu?

      1. in primul rand, un program nu foloseste memorie (caci presupun ca te referi la memoria de lucru). un program doar ocupa spatiu pe hdd. memoria ocupata de un program o afli ca pentru orice alt fisier: click dreapta si proprietati (sau programatic cu functiile filesystem-ului folosit)
      2. depinde ce intelegi prin memoria folosita de un proces. Poate fi marimea unui segment al procesului precum heap-ul, sau marimea intregului proces, cum poți vedea mai jos

      lectia: program != proces. Sub windows, componenta sistemului de operare numita PE Loader ia fisierul binar (programul) de pe hdd, il incarca in memorie, si face multe alte lucruri ciudate pe care nici eu nu mi le mai amintesc exact (hint: IAT - import address table, e unul din lucrurile importante), si il transforma intr-un proces complet izolat in RAM. Il "virtualizeaza".

      Cod pentru windows (deoarece astfel de informații depind de sistemul de operare), autor @neagu_laurentiu:
      #include <windows.h>
      #include <stdio.h>
      #include <psapi.h>
      
      void PrintMemoryInfo(DWORD processID)
      {
      HANDLE hProcess;
      PROCESS_MEMORY_COUNTERS pmc;
      
      // Print the process identifier.
      
      printf("\nProcess ID: %u\n", processID);
      
      // Print information about the memory usage of the process.
      
      hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
      PROCESS_VM_READ,
      FALSE, processID);
      if (NULL == hProcess)
      return;
      
      if (GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)))
      {
      printf( "\tPageFaultCount: 0x%08X\n", pmc.PageFaultCount );
      printf( "\tPeakWorkingSetSize: 0x%08X\n",
      pmc.PeakWorkingSetSize);
      printf( "\tWorkingSetSize: 0x%08X\n", pmc.WorkingSetSize);
      printf( "\tQuotaPeakPagedPoolUsage: 0x%08X\n",
      pmc.QuotaPeakPagedPoolUsage);
      printf( "\tQuotaPagedPoolUsage: 0x%08X\n",
      pmc.QuotaPagedPoolUsage);
      printf( "\tQuotaPeakNonPagedPoolUsage: 0x%08X\n",
      pmc.QuotaPeakNonPagedPoolUsage);
      printf( "\tQuotaNonPagedPoolUsage: 0x%08X\n",
      pmc.QuotaNonPagedPoolUsage);
      printf( "\tPagefileUsage: 0x%08X\n", pmc.PagefileUsage);
      printf( "\tPeakPagefileUsage: 0x%08X\n",
      pmc.PeakPagefileUsage);
      }
      
      CloseHandle( hProcess);
      }
      
      int main(void)
      {
      // Get the list of process identifiers.
      
      DWORD aProcesses[1024], cbNeeded, cProcesses;
      unsigned int i;
      
      if (!EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded))
      return 1;
      
      // Calculate how many process identifiers were returned.
      
      cProcesses = cbNeeded / sizeof(DWORD);
      
      // Print the memory usage for each process
      
      for ( i = 0; i < cProcesses; i++ )
      PrintMemoryInfo( aProcesses[i]);
      
      return 0;
      }
      
      ( http://msdn.microsof.....v=VS.85).aspx )
    • Este bine să faci type casting?

      Nu. In C nu e nevoie sa faceti cast in urma unui malloc // calloc // realloc, este chiar descurajat. http://en.wikipedia....and_type_safety (contribuit de: @gnomemory)
    • Cum pot compila si fisiere scrise in C++ (.cpp)?

      g++ -Wall fisier.cpp
      
Cu timpul ar trebui sa strang aici mai toate notiunile semiteoretice cu o alura de pragmatism care nu prea sunt predate prin invatamantul romanesc, astfel incat cine le cunoaste si intelege sa poata comunica pe meritate ca un homo sapiens cu alti homo sapiensi (read: sa ne intelegem unii cu altii, ca intre programatori).

Daca iti place acest articol il poti imbunatati:
- ca incepator poti pune intrebari legate de cele citite
- ca avansat, poti semnala eventuale greseli, ambiguitati, sau lucruri pe care iti doresti sa le fi stiut la inceput

Completari, corecturi, întrebari, critica constructiva sau multumiri sunt ca de obicei binevenite.
Dupa ce s-a ajuns la o concluzie finala, discutia va fi stearsa si concluzia mutata în postul initial. Asa se poate pastra totul într-un loc si este usor de urmarit si de citit de oricine viziteaza aceasta arie aproape saptamânal, cât si de cei care citesc acest topic pentru prima oara.

#2
msmihai

msmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 5,243
  • Înscris: 02.09.2006

Quote

aceasta garda va avea ca efect sarirea peste intregul continut al fisierului, si astfel nu vom vedea erori de la compilator de genul "is_prime already defined".

Aici ar merge explicat pe scurt ce face compilatorul cu continutul fisierelor header:)

#3
pax0xFF

pax0xFF

    Member

  • Grup: Members
  • Posts: 869
  • Înscris: 21.10.2012
Dupa ce am citit articolul , chiar mi s-au clarificat unele lucruri.
In schimb nu am inteles cum pot link-a dinamic. Poate pune cineva un exemplu cu legarea de un dll?

Mersi.

#4
neagu_laurentiu

neagu_laurentiu

    Guru Member

  • Grup: Senior Members
  • Posts: 35,061
  • Înscris: 30.07.2003
Citeste capitolul urmator: http://msdn.microsof...9(v=vs.85).aspx
apoi intreaba ce nu intelegi.

#5
pax0xFF

pax0xFF

    Member

  • Grup: Members
  • Posts: 869
  • Înscris: 21.10.2012
Am facut un dll care afiseaza un mesaj pe ecran, si l-am apelat din program. De ce programul are aceeasi marime (27 kb) si atunci cand nu am dll si atunci cand am dll?
Folosind dll'uri consumul de resurse scade, sau ramane acelasi?

#6
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 25,058
  • Înscris: 10.08.2006

View Postpax0xFF, on 01 ianuarie 2013 - 22:57, said:

Am facut un dll care afiseaza un mesaj pe ecran, si l-am apelat din program. De ce programul are aceeasi marime (27 kb) si atunci cand nu am dll si atunci cand am dll?
Deoarece codul binar se află  în .dll, nu în .exe. Sistemul de operare face "magicul" atunci când PE loader (vezi şi în postul #1) încarcă/virtualizează programul făcându-l proces.

Şi probabil nu ai nici mii de linii de cod, ca să observi o diferenţă, şi nici alocări de date statice mari. Uită-te la diferenţele de mărime în bytes, nu kb.

View Postpax0xFF, on 01 ianuarie 2013 - 22:57, said:

Folosind dll'uri consumul de resurse scade, sau ramane acelasi?
Per total, faţă de sistemul tău, consumul de RAM este mai mic faţă de linkarea statică doar dacă acel .dll este refolosit de mai multe procese ce rulează în paralel.

Încearcă să deschizi mai multe instanţe ale aceluiaşi program. (va trebui probabil să deschizi foarte multe ca să observi o diferenţă).

Edited by OriginalCopy, 01 January 2013 - 23:18.


#7
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 25,058
  • Înscris: 10.08.2006
Adăugat

Quote

Apoi deschide fisierul .bat amintit mai sus cu Notepad si adauga-i la sfarsit instructiunea pentru a schimba directorul curent (comanda cd vine de la change directory) in "proiecte":
Motiv: http://forum.softped...8#entry12518744

Edited by OriginalCopy, 12 January 2013 - 14:54.


#8
andrei_kick

andrei_kick

    Junior

  • Grup: Members
  • Posts: 192
  • Înscris: 06.01.2013
înainte sa citesc articolul asta trebui sa stiu c, nu? eu nu prea inteleg mare lucru din articol. ce tutorial/carte imi recomanzi? multumesc

Edited by andrei_kick, 15 May 2013 - 21:29.


#9
MarianG

MarianG

    be that as it may

  • Grup: Moderators
  • Posts: 20,182
  • Înscris: 10.08.2005
tu vrei sa inveti peste noapte?

#10
andrei_kick

andrei_kick

    Junior

  • Grup: Members
  • Posts: 192
  • Înscris: 06.01.2013
am intrebat daca inainte de a citi articolul trebuie sa stiu c sau sunt eu idiot si nu inteleg articolul. daca imi trebuie c, va rog sa imi ziceti o carte sau un tutorial bun. multumesc

#11
neagu_laurentiu

neagu_laurentiu

    Guru Member

  • Grup: Senior Members
  • Posts: 35,061
  • Înscris: 30.07.2003

View Postandrei_kick, on 16 mai 2013 - 09:00, said:

o carte sau un tutorial bun
Acest "bun" e relativ si tine de fiecare persoana in parte. Eu nu pot garanta care va prinde la tine (nici nu sunt profesor) de aceea iti recomand sa intri pe amazon.com, alegi cateva editii (le gasesti si pe net in lumea larga) si astfel tu decizi in cunostinta de cauza.

Edited by neagu_laurentiu, 16 May 2013 - 09:19.


#12
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 25,058
  • Înscris: 10.08.2006
Adăugat "Sfat pentru liceeni:" la punctul 1.

#13
EnachescuAlin

EnachescuAlin

    Active Member

  • Grup: Members
  • Posts: 1,008
  • Înscris: 08.07.2013
Foarte bun tutorialul.
Am ramas uimit sa vad ce se afla in spatele codului C pe care noi il scriem.
Nu m-am multumit sa vad doar ce e in spatele unui program care afiseaza "hello world" si m-am uitat si la alte probleme mai mari si am ramas uimit.

EDIT: Totusi am cateva nelamuriri. Fisierul main.o ar trebui sa contina codul binar? Ca as vrea sa vad din curiozitate si codul binar al unui program C.

Edited by EnachescuAlin, 30 December 2013 - 20:10.


#14
adrian93

adrian93

    Active Member

  • Grup: Members
  • Posts: 1,738
  • Înscris: 29.10.2009
Păi conține cod mașină relocabil, obținut după asamblare. Dacă vrei să vezi ceea ce e în el (și dacă ești pe Linux), îi poți da
objdump -s fisier.o

Însă nu prea e... ”distractiv”, pentru că o să vezi cam ce e relevant în hexa (în partea stânga apar octeții afișați în ASCII, dar nici așa nu prea te ajută foarte mult).
Încearcă să cauți partea de ”.rodata”, acolo o să fie _chestiuni_ mai clare Posted Image, mai ales dacă ai un macro prin cod.
Cel mai interesant e să îl dezasamblezi, (parametrul -d sau --disassemble la objdump), pentru a vedea codul în limbaj de asamblare.

Edited by adrian93, 30 December 2013 - 21:01.


#15
EnachescuAlin

EnachescuAlin

    Active Member

  • Grup: Members
  • Posts: 1,008
  • Înscris: 08.07.2013
Merge. Interesant ce se afla acolo...dar totusi nu se poate vedea codul binar al unui fisier C?

#16
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 25,058
  • Înscris: 10.08.2006
Codul e deja binar, dacă îl deschizi (în windows cu Notepad++ şi te joci prin meniu cu nişte pluginuri parcă) vei vedea fix codul binar.

Ceea ce vrei este să vezi reprezentarea în ASCII (adică să vezi caracterele '0' şi '1') a codului binar.

În linux:

xxd -b file.o



#17
EnachescuAlin

EnachescuAlin

    Active Member

  • Grup: Members
  • Posts: 1,008
  • Înscris: 08.07.2013
Pai eu asta vreau sa vad, fisierul cu caractere '0' si '1' dar am Windows, iar comanda pe care mi-a dat-o Adrian e buna, afiseaza ceva pe acolo dar nu binar.

#18
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 25,058
  • Înscris: 10.08.2006

View PostEnachescuAlin, on 30 decembrie 2013 - 21:26, said:

Pai eu asta vreau sa vad, fisierul cu caractere '0' si '1' dar am Windows, iar comanda pe care mi-a dat-o Adrian e buna, afiseaza ceva pe acolo dar nu binar.

Stimate utilizator.

Ţi-am dat şi o soluţie pentru windows. Nu trebuie decât să citeşti ce am scris.

Şi ţi-am spus şi că nu vrei să-ţi afişeze binar (binar e ce vezi când îl deschizi cu Notepad), ci vrei să-ţi afişeze reprezentarea în ASCII a conţinutului binar.

"binar" înseamnă că un bit ocupă 1 bit. Ori un bit nu poate fi vizualizat, e prea mic.

Atunci când vrei să vezi text, cel mai simplu mod e să-l vezi folosind standardul ASCII.

Însă un simbol în ASCII ocupă 7 biţi (hai 8). Deci ca să vezi reprezentarea în ASCII a acelui un bit, trebuie să-l umfli la 8 biţi, ca să poţi vedea în fapt, ceva!

Bitul 0 va fi reprezentat ca byte-ul 0011 0000 iar bitul 1 va fi reprezentat ca byte-ul 0011 0001 (vezi aici tabelul http://en.wikipedia....able_characters ).

Asta dacă ai un editor text pentru ASCII. Dacă n-ai (glumă, râzi), va trebui să "umfli" acel bit 0 respectiv 1 la numărul de biţi pentru ceea ce poate interpreta editorul tău text.

Anunturi


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

Forumul Softpedia foloseste "cookies" pentru a imbunatati experienta utilizatorilor Accept
Pentru detalii si optiuni legate de cookies si datele personale, consultati Politica de utilizare cookies si Politica de confidentialitate