Sari la conținut
ELFORUM - Forumul electronistilor

Invatat programare de la zero


Postări Recomandate

bine indentat. Folosind tab-uri. Indentarea cu ajutorul unui spatiu (tasta SPACE)

O alta parere intalnita de mine e ca, din cauza ca fiecare editor are alte setari pentru tab-uri (si codul arata altfel pe fiecare masina in parte), pentru indentare e preferabila folosirea spatiilor (mai multe, nu unul singur).

Link spre comentariu

Liviu, metoda sugerata de tine este foolproof. Adica odata folosita lasa mai putin loc pentru eventuale probleme. De ex in Python este o problema masiva pentru ca acolo indentarile nu reprezinta doar o chestie de "cosmetica" a programului ci fac parte din sintaxa.

 

Dar ... sincer ... cine pana mea sta sa apese pe tasta SPACE de 3 ... 4 ori pentru o indentare? Lasi loc de greseli si intr-un final programul (fara corectura) arata ca un cocostarc :)

Personal cred ca este mai confortabil sa se foloseasca TAB-ul pentru a cosmetiza programul si a-l face "sexy" :)

 

Exemplu pentru postul meu anterior, cu intentia de a arata diferenta dintre un cod "scris cu picioarele" si un cod "sexy".

 

Program-ul scris "cu picioarele", greu de citit:

void main() {unsigned int timp = 60000;const unsigned char ON = 1;const unsigned char OFF = 0;CMCON = 0b00000111; //dezactivam comparatorADCON0 = 0; //dezactivam ADCADCON1 = 0x6; //totuna cu ADCON1 = 0x00000110; facem toate intrarile analogice, digitale.CVRCON = 0; //dezactivam referinta variabila de tensiuneTRISA = 0b00000001; //facem INTRARE din pinul2 adica RA0; aici vom avea switch-ul conectatTRISB = 0b00000000; //facem IESIRE din pinul 33 adica RB0; aici vom avea LED-ul conectatPORTB = 0b00000000; //pornim cu LED-ul atasat pe pinul33 stins. Adica RB0 = LOW;/*urmeaza bucla infinitaCat timp 1 este egal cu 1(si 1 este egal cu 1 tot timpul) executa ce este intre acolade*/while(1 == 1) { if (RA0_bit == 0) { //daca intrarea este LOWdelay_ms(10); //asteptam 10 milisecundeif (RA0_bit == 0) { //daca intrarea inca mai este LOW atunci chiar ca avem o apasare reala de switch     RB0_bit = ON; //aprindem LED-ulwhile (timp != 0) { //cat timp timpul este diferit de zero/*Urmatoarele doua conditii if(expresie) se folosesc pentru ca daca cumva in timpul de asteptare mai apasamcumva pe buton, timpul trecut se reseteaza si temporizarea o ia de la inceput*/if (RA0_bit == 0) { //daca intrarea este LOW       delay_ms(10); //asteptam 10 milisecunde if (RA0_bit == 0) { //daca intrarea inca mai este LOW atunci chiar ca avem o apasare reala de switchtimp = 60000; //resetam timpul la valoarea initiala, 60000 * 10ms = 600000ms = 600sec = 10minute} }//variabila timp va scadea cu o unitate pentru fiecare 100ms care trec    timp = timp - 1; // sau se poate scrie timp--;delay_ms(10); //aceasta este intarzierea esentiala pentru cronometrarea timpului cat LED-ul sta aprins}//timpul cat LED-ul trebuie sa stea ON a trecut si acum facem LED-ul OFF              RB0_bit = OFF;timp = 60000;}   }} }

si programul scris folosind indentari si separari prin linii libere:

void main() {  /*declaram o variabila numita timp, de tip unsigned int - deci poate lua valori intre 0 si 65535  Aceasta variabila va tine in ea numarul de zeci de milisecunde care va constitui perioada de temporizare.  Initializam aceasta variabila cu val 60000 care corespunde la 60000 cicluri de 10ms adica 10minute */  unsigned int timp = 60000;    /*Declaram doua constante ON si OFF ca sa ne fie mai usor de inteles programul*/  const unsigned char ON = 1;  const unsigned char OFF = 0;      /************** INITIALIZARE***************************/  CMCON = 0b00000111;  //dezactivam comparator  ADCON0 = 0;          //dezactivam ADC  ADCON1 = 0x6;        //totuna cu ADCON1 = 0x00000110; facem toate intrarile analogice, digitale.  CVRCON = 0;          //dezactivam referinta variabila de tensiune   TRISA = 0b00000001;  //facem INTRARE din pinul2 adica RA0; aici vom avea switch-ul conectat   TRISB = 0b00000000;  //facem IESIRE din pinul 33 adica RB0; aici vom avea LED-ul conectat  PORTB = 0b00000000;  //pornim cu LED-ul atasat pe pinul33 stins. Adica RB0 = LOW;  /**************SFARSIT INITIALIZARE********************/   /*urmeaza bucla infinita Cat timp 1 este egal cu 1(si 1 este egal cu 1 tot timpul) executa ce este intre acolade  */  while(1 == 1) {        if (RA0_bit == 0) {   //daca intrarea este LOW      delay_ms(10);       //asteptam 10 milisecunde              if (RA0_bit == 0) { //daca intrarea inca mai este LOW atunci chiar ca avem o apasare reala de switch        RB0_bit = ON;    //aprindem LED-ul                while (timp != 0) {     //cat timp timpul este diferit de zero          /*Urmatoarele doua conditii if(expresie) se folosesc pentru ca daca cumva in timpul de asteptare mai apasam          cumva pe buton, timpul trecut se reseteaza si temporizarea o ia de la inceput*/          if (RA0_bit == 0) {   //daca intrarea  este LOW            delay_ms(10);       //asteptam 10 milisecunde            if (RA0_bit == 0) { //daca intrarea inca mai este LOW atunci chiar ca avem o apasare reala de switch              timp =  60000;     //resetam timpul la valoarea initiala, 60000 * 10ms = 600000ms = 600sec = 10minute            }          }          //variabila timp va scadea cu o unitate pentru fiecare 100ms care trec          timp = timp - 1;      // sau se poate scrie timp--;          delay_ms(10);         //aceasta este intarzierea esentiala pentru cronometrarea timpului cat LED-ul sta aprins                  }        //timpul cat LED-ul trebuie sa stea ON a trecut si acum facem LED-ul OFF        RB0_bit = OFF;        timp = 60000;      }    }   }}

Ambele sunt functionale dar ... daca le cititi si incercati sa intelegeti prima varianta, rezultatul o sa fie doar enervare. Intr-un final veti ajunge sa puneti codul intr-un editor si sa il modificati adaugand indentari pentru a il putea intelege.

 

LE: O idee buna este ca daca se folosesc comentariile pe linie, ele sa se gaseasca la acelasi numar de TAB-uri, indentari. Nu cum am facut eu mai sus, este dificil de pastrat aceasta uniformitate folosind instrumentele forumului.

Editat de mars01
Link spre comentariu

Dar ... sincer ... cine pana mea sta sa apese pe tasta SPACE de 3 ... 4 ori pentru o indentare?

Eu. :rade: Cel putin daca nu indenteaza editorul pentru mine.La unul din clienti (pentru care scriem si mici programele), cel putin, face parte din "caietul de sarcini". Editat de Liviu M
Link spre comentariu

@Liviu, mda, ce sa faci ... te adaptezi ...

 

In continuare, despre FUNCTII.

 

Am spus despre functii urmatoarele:

- reprezinta grupari de linii cod care au rolul de a "face" ceva. Toate liniile de cod dintr-o functie au un scop bine definit, sunt scrise avand in minte un rezultat.

 

- utilizarea functiilor presupune 3 pasi:

     a) declaratia functiei (aici se declara tipurile de variabile pentru parametri si pentru rezultat (rezultatul "intors" de functie).

     b) definirea functiei. Adica scrii efectiv functia, liniile de cod care vor compune corpul functiei

     c) apelul functiei in programul principal. Aici folosim acea functie. Daca nu folosim functia in programul principal atunci chiar daca o declaram si o definim, compilatorul (la momentul compilarii - adica atunci cand apesi butonul BUILD din interfata IDE) va elimina codul functiei din program si fisierul .hex rezultat nu va contine operatiile respective. Daca nu o folosim, de ce am programa-o in controller ca sa ocupe spatiu degeaba, nu?

 

- functiile sunt cam cum sunt niste organisme: intra date in ele, sunt prelucrate, iese ceva din ele.

Ca o vaca de exemplu: mananca iarba, graunte etc, le prelucreaza in burta ei si apoi da un rezultat: lapte si balega. Balega nu se pune :) , la functii avem un singur rezultat, in conditii obisnuite.

 

Datele de intrare se numesc parametrii functiei, informatia care iese se numeste rezultat "intors" (vine de la cuvantul cheie 'return' care semnaleaza ca variabila de dupa 'return' contine informatia prelucrata de functie. O functie poate avea cuvantul cheie 'return' sau poate sa nu il aiba. Daca il are inseamna ca la declaratie am scris un tip de variabila altul decat 'void' exact la inceput, adica ma refer la cuvantul cheie 'int' de mai jos - observati pozitia:

int functie ();

Cand punem un tip de variabila in pozitia cum este mai sus, altul decat 'void', inseamna ca ne asteptam ca functia sa intoarca un rezultat. Daca noi declaram functia ca mai sus, dar cand o definim (adica atunci cand scriem efectiv codul ei) nu se intalneste cuvantul cheie 'return', atunci compilatorul o sa dea eroare.

"Ce pana ei mi-ai declarat un tip variabila la rezultat daca nu ai de gand sa ai un rezultat? - intreaba retoric o personificare a compilatorului !!!"

 

Exemplu gresit:

//declaratie functieint functie (int, int);//definire functieint functie (int a, int b) {  int res;  res = a + b;}

De ce? Pentru ca desi am declarat un tip pentru rezultat (int in fata numelui functiei), nu am folosit cuvantul cheie 'return' in corpul functiei.

 

Exemplu corect:

//declaratie functieint functie (int, int);//definire functieint functie (int a, int b) {  int res; //variabila res este variabila locala - o sa vedem mai tarziu ce este o varibila locala  res = a + b;  return res;}

sau aceasi functie se mai poate scrie:

//declaratie functieint functie (int, int);//definire functieint functie (int a, int b) {  return (a + b);}

Daca functia se declara cu tipul special 'void' pentru rezultat (in fata numelui functiei) atunci se traduce ca nu ne asteptam ca functia sa intoarca un rezultat, cel putin nu 'oficial'. O sa scriu mai jos la ce m-am referit cu 'oficial'. 

 

Ex: 

/* Exemplu declaratie a unei functii numite 'new_generation' si care are ca parametri   doua variabile, una tip int si alta tip char,   si care nu intoarce nici-un rezultat fiindca la tipul rezultatului avem tipul 'void'*/void new_generation (int, char);

Daca functia doar aprinde un LED, se cheama ca rezultatul functiei nu este necesar pentru ca este executat cu efect in lumea exterioara. Ma rog, este un caz particular, pentru ca desi functia poate doar aprinde un LED, cine stie .... poate avem nevoie de o variabila care sa ne spuna care este starea LED-ului si atunci punem si un rezultat din functie, o variabila care va fi "returned, intoarsa".

Combinatii sunt destule, imaginatie sa avem !!!

 

In cazul parametrilor, putem avea parametri pentru functie sau putem sa nu avem. Doar ca in cazul lipsei parametrilor nu este nevoie sa ii punem in declaratie ca fiind cu tipul 'void'. Pur si simplu numai punem nimic intre parantezele rotunde ale functiei. Aceasta inseamna ca functia nu are parametri.

 

Ex:

/*declaratie functie. Observati ca desi functia intoarce un rezultat de tip int,  functia nu are nici-un parametru, intre paranteze nu e nimic.*/int functie();

Acest caz cand functia nu are parametri poate sa apara atunci cand informatia care este folosita ca date de intrare este preluata direct in functie, de ex se citeste starea unui pin al controlerului prin evaluarea unui registru asociat.

Interesant cum se leaga toate acum, nu? Registrii, care sunt si ei un gen de variabile speciale, contin informatie care se poate accesa de oriunde. Foarte important conceptul de 'oriunde' ...

 

Pentru ca o functie fara parametri poate sa isi ia informatia de input, de intrare nu numai din registri dar si din alte variabile care se pot accesa de 'oriunde', asa numitele variabile globale. Si registrii pot fi priviti ca variabile globale ...

 

Dar ce este o VARIABILA GLOBALA? Si ce este prin contrast o variabila locala si ce legatura au toate acestea cu functiile ?

Data viitoare.

Va urma.

Editat de mars01
Link spre comentariu

Legat de return in functie care intoarce void, exista cazuri cand folosim return fara sa intoarcem nici un rezultat din functie, doar intrerupem functia si iesim din ea.

Se foloseste in multe cazuri de validari pre-procesare, ne asiguram ca parametri sunt corecti sa continue functia, de exemplu.

Editat de core
Link spre comentariu

@core, foarte buna interventia. Imi scapase din vedere acest aspect. Am folosit si eu de cateva ori acest procedeu, dar atat de rar incat era mai mult ceva intuitiv.

 

Cuvantul  cheie 'return' la modul solitar fara sa fie insotit de numele unei variabile, adica ca si solutie de iesire instantanee din functie, functioneaza similar cu cuvantul cheie 'break' pentru ciclurile for, while, instuctiunea switch-case.

Link spre comentariu

Nu am mai scris de ceva timp aici,  invat un limbaj nou si incerc sa fac cate ceva (dupa posibilitati) intr-un proiect open source (flatcam.org) asa ca nu prea a mai ramas timp liber.

 

 

Ramasesem la variabile globale, locale.

 

Nu pretind ca am sa epuizez subiectul dar ce trebuie sa stiti este ca in programare exista un concept numit "scope". 

Acest concept se refera la unde anume se aplica ceva. 

Spre exemplu, avem o surubelnita pe care am cumparat-o special ca sa o folosim in atelier si numai in atelier. Surubelnita poate fi folosita doar in atelier pentru ca asa am intentionat cand am cumparat-o. 

 

Tota asa si variabilele. In functie de locul unde sunt declarate ele pot fi folosite in tot programul (var globala) sau doar in anumite sectiuni ale programului (var locala). Si nu se incurca unele cu altele chiar daca au aceasi nume :)

 

Atunci cand o variabila se poate folosi peste tot se numeste ca este o variabila globala.

Atunci cand o alta variabila se poate folosi doar intr-o functie, se cheama ca este o variabila locala.

 

De ce am folosi variabile locale cand variabilele globale se pot folosi peste tot? De ce sa ne incurcam cu variabile care le folosesti doar intr-o functie?

Unul din motive este ca incercam sa folosim denumiri de variabile cat mai sugestive. Este posibil ca in anumite situatii sa avem nevoie de o variabila care face ceva intr-un loc in program si o denumim sa zicem 'afiseaza' si este posibil ca in alt loc in program sa folosim acelasi nume de variabila 'afiseaza'.

 

Dar nu vrem ca modificand intr-un loc variabila 'afiseaza' sa ne pomenim ca in celalalt loc in program, variabila a2a care are acelasi nume dar alta functionalitate sa fie modificata.

 

Apare nevoia de a le separa cumva.

 

Astfel, putem avea mai multe variabile locale cu numele 'afiseaza' fara ca una sa influenteze pe cealalta. Putem avea o variabila globala cu numele 'afiseaza' si altele locale (fiecare intr-o functie distincta) tot cu numele 'afiseaza'.

 

In C nu avem un cuvant cheie care sa spuna ca variabila este globala cum ar fi 'global' si nici un cuvant cheie care sa declare variabila ca fiind locala.

O variabila este globala sau locala in functie de locul exact in program unde este declarata, adica unde ii spui tipul. De ex:

int i;

O variabila declarata (definita) in afara unei functii (adica o variabila care nu este locala, deci este globala) se poate folosi intre punctul unde se declara si finalul programului.

 

[Nu imi propun sa discut aici (poate alta data, sau poate un coleg care are timp) despre cuvantul cheie extern. Folosirea acestui cuvant cheie in fata unei variabile presupune ca programul nostru se intinde in mai multe fisiere deci face parte dintr-un proiect complex.]

 

Ex de variabila globala:

int maca_maca; // variabila globalavoid functia_noastra (int, int); // declaratia unei functii oarecare care nu intoarce un rezultat// definim functia, ii scriem corpulvoid functia_noastra(int a, int b) {  maca_maca = a + b;}void main() {  //apel functia 'functia_noastra'  functia_noastra(3,5);    //functie standard C, care tipareste pe ecran PC  printf("Variabila maca_maca este: %d n", maca_maca);}// Rezultatul programului este:// Variabila maca_maca este: 8

Putem observa ca functia 'functia_noastra' nu intoarce nici-un rezultat.

Si totusi, deoarece variabila maca_maca este globala, atunci cand chemam (apelam) functia, va aduna valorile parametri (in cazul nostru 3 si 5), pune rezultatul in variabila maca_maca (o var globala) si aceasta variabila se poate folosi in functia printf din main() pentru a ii tipari valoarea pe ecran. Chiar daca valoarea nu este intoarsa de functie. Deci o variabila globala care este modificata chair si intr-o functie, pastreaza modificarea chiar si dupa ce functia si-a terminat treaba. 

 

De ce este important ce am scris mai sus?

Pentru ca in mod normal o functie isi pune rezultatul doar in variabila intoarsa (cea care in corpul functiei urmeaza cuvantului cheie 'return').

Orice altceva se intampla intr-o functie nu influenteaza in mod normal programul (nu luam in considerare variabilele tip pointer, subiect avansat).

Dar vedem ca avem cel putin o exceptie de la regula, atunci cand folosim variabile globale intr-o functie, orice le facem acolo, modificarile raman, sunt persistente.

 

Multi nu se obosesc sa declare variabile locale in functii (mai ales incepatorii). Declara la inceputul programului toate variabilele adica le fac globale si apoi le folosesc pe unde au nevoie in program, chiar si in functii.

 

Cand se folosesc functiile asa (adica numai cu variabile globale), ele pot sa nu aiba nici parametri, nici rezultat si sunt folosite doar pentru partitionare cod, incat codul sa devina usor de citit.

Multi fac acest lucru usual de ex in programe gen FlowCode si nu numai.

 

O variabila declarata (definita) in interiorul unei functii este o variabila locala.

Diferenta este ca o variabila pur locala, odata ce functia si-a incheiat activitatea si se trece mai departe in program, este distrusa. Adica zona de memorie care ii era alocata la declarare este marcata de compilator ca libera si disponibila pentru orice altceva.

 

Cu alte cuvinte, durata de viata a unei variabile locale coincide cu functionarea functiei.

Practic functia cand este apelata, daca are o variabila locala declarata in interior, se aloca memorie pentru acea variabila, face ce face cu ea pe acolo si la finalul functiei, dupa cuvantul cheie 'return', dispare.

 

Avantajul folosirii variabilelor locale este:

- nu ocupa memorie decat atunci cand sunt folosite spre deosebire de variabila globala care odata declarata, zona de memorie este ocupata pe tot timpul functionarii programului

- este specifica strict functiei unde este declarata. Ce inseamna? Inseamna ca poate fi folosita doar in functia unde este declarata. Orice incercare sa fie utilizata in afara functiei, genereaza o eroare de compilare. (LE: Aceasta daca nu este definita vreo variabila globala cu acelasi nume, pentru ca daca exista o var globala cu acelasi nume, sa vezi bug-uri pe urma. Pentru ca una intentionezi sa faci si alta obtii. Si pe urma oftat-uri cu "de ce mama masii nu merge .. ").

In acest fel acea variabila locala se poate folosi fara grija ca intr-un program complex, cine stie, poate o modificam pe undeva fara sa ne dam seama.

 

Ar fi si un dezvantaj. Din cauza ca zonele de memorie se aloca si apoi se elibereaza functie de cat de des functia C este apelata, apare o zona de memorie ocupata dinamic in cazul variabilelor locale.

Cu alte cuvinte, daca ai numai variabile globale stii exact cata memorie RAM o sa fie folosita si nu iti faci probleme ca ar putea apare probleme de RAM insuficient (bug-urile generate de asemenea probleme RAM sunt foarte nasoale si cam greu de depistat). Aceasta pentru ca variabilele globale sunt declarate pentru toata functionarea programului. Vor tine memoria ocupata cat timp programul functioneaza.

 

Cu variabilele locale nu este asa. Uneori nu stii daca nu cumva, functie de ce face user-ul, o functie este apelata exact cand un o variabila tip vector (vom vorbi si despre aceasta) este plina cu date si daca aduni necesarul functiei de memorie RAM cu necesarul vectorului, suma este peste dimensiunea memoriei RAM a controller-ului. Normal ca ceva crapa pe acolo dar nu tot timpul, doar in anumite situatii specifice. Acesta ar fi un exemplu de bug, in care variabiele locale prin alocarea lor temporara pot genera probleme.

 

Pe de alta parte, un programator care stie ce face pe acolo, are o viziune de ansamblu si sta cu pixul si hartia si calculeaza "worst case scenario" pentru consumul de RAM. 

Nu inseamna ca trebuie sa ne fie frica de variabilele locale. Au utilitatea lor dar trebuie sa fim atenti cu ele.

 

Ex de utilizare variabila tip local in conjunctie cu utilizarea unei variabile de tip global.

#include <stdio.h>// variabile globaleint gigel = 3; // declaram variabila globala gigel si ii dam valoarea 3int print_pe_ecran;// declaram o functie care accepta doi parametri tip INT si intoarce un rezultat tot tip INTint my_func(int param_a, int max);// scriem corpul functiei, o definimint my_func(int param_a, int max){  // declaram variabila locala gigel  int gigel;  int rezultat = 0; // declaram si initializam o varibila locala unde punem rezultatul de intors din functie  for (gigel = 0; gigel < max; gigel = gigel + 1){    rezultat = rezultat + param_a;  }  return rezultat;}void main(){  printf("Variabila globala gigel este: %d n", gigel);    // rezultatul functiei care contine varibila locala gigel  print_pe_ecran = my_func(3,5);  printf("Rezultat functie este: %d n ", print_pe_ecran);}// Rezultatul pe ecran va fi:// Varibila globala gigel este: 3// Rezultat functie este: 15 

Observam ca desi in functie, variabila locala gigel isi tot schimba valoarea fiindca este folosita ca iterator in ciclul for, totusi nu schimba valoarea variabilei globale gigel, care ramane tot 3, exact cu cat am initializat-o (nu am facut nimic altceva cu ea in program ca sa ii schimbam valoarea).

 

Urmeaza sa vorbesc putin despre cuvantul cheie static, pe urma un rezumat pentru functii si pe urma discutam pe un exemplu. Facem din programul licurici un program care foloseste functii.

 

LE: Codul se poate rula online aici.

Editat de mars01
Link spre comentariu

Salut,

Optservatii: 

- poate ca ar fi trebuit sa insisti asupra faptului ca atunci cand ai o variabila globala si una locala cu acelasi nume, in blocul in care e valabila cea locala se foloseste aceasta

- "spargerea" functiilor in declaratie si definitie in conditiile in care sunt una dupa alta nu-si prea are rostul. De obicei se sparg ca sa ai declaratiile inainte de main si definitiile dupa, ca sa nu te plictisesti scroll-uind pana ajungi la main. Sau ca sa pui definitiile in alte fisiere, dar atunci pui si declaratiile in fisiere header. 

Link spre comentariu

Sorry, am inceput de vreo doo ori (ceva despre operatii pe bit) si am renuntat. Degeaba, n-am ștofa. :(

Oricum eu apreciez ce faci pe topicul asta si, daca fac cate o observatie, nu trebuie vazuta ca o critica sau ca vreo parere "negativa".

 

PS Interesant flatcam-ul. Pacat ca am demontat cnc-ul... :(

Link spre comentariu

Sincer nu am nimic de aparat aici. Asa ca orice critica, daca e facuta in spirit constructiv este mai mult decat acceptata, este bine venita. So keep it going !!!

 

Nu scriu pentru ca am ceva de demonstrat.

Scriu pentru ca nimeni altcineva nu a facut-o inca aici (dupa cate stiu, poate ma insel?) si cineva trebuia sa o inceapa ca sa "intram si noi in randul lumii" :)

 

Chiar aseara vorbeam cu un coleg care este foarte bun in tot ce inseamna programare/electronica (de la limbaje de nivel inalt la asamblare la Verilog etc, nimic nu este sa nu se priceapa). Si el spunea ca este foarte important sa impartim ce stim si cu ceilalti pentru ca impreuna crestem mai mult decat de unii singuri.

Si eu invat impreuna cu ceilalti, sunt chestii la care nu ma gandeam si le-am vazut expuse pe acest topic (chestia cu #define semicolon ; sau gen PORTB1_bit = (x <= 1000) etc :)

 

Cu CNC-ul, ce s-a demontat se poate monta la loc :) Eu vreau sa ma folosesc de FlatCAM si sa fac un solder paste dispenser, practic un mic CNC cu curele dar aceasta este o discutie pentru alt topic.

Editat de mars01
Link spre comentariu

Cu variabilele locale nu este asa. Uneori nu stii daca nu cumva, functie de ce face user-ul, o functie este apelata exact cand un o variabila tip vector (vom vorbi si despre aceasta) este plina cu date si daca aduni necesarul functiei de memorie RAM cu necesarul vectorului, suma este peste dimensiunea memoriei RAM a controller-ului. Normal ca ceva crapa pe acolo dar nu tot timpul, doar in anumite situatii specifice. Acesta ar fi un exemplu de bug, in care variabiele locale prin alocarea lor temporara pot genera probleme.

 

Pe de alta parte, un programator care stie ce face pe acolo, are o viziune de ansamblu si sta cu pixul si hartia si calculeaza "worst case scenario" pentru consumul de RAM. 

Nu inseamna ca trebuie sa ne fie frica de variabilele locale. Au utilitatea lor dar trebuie sa fim atenti cu ele.

 

Poti da un exemplu mai clar pentru cazul de mai sus?

 

 Cred ca ar trebui vorbit si de tipurile de alocari de "memorie": segment de date (variabile externe si statice), registri (variabil locale precedate de cuvantul cheie register, in general ignorate de compilator si folosite in optimizarile compilatorului), din heap (alocate cu Malloc, dezalocate cu FreeMem), si stiva (variabile locate si parametri de functie). Primul si ultimul tip sunt cele mai utilizate.

Ar trebui si adus aminte de limitele de imbricare (nesting) a functiilor, din cate stiu 8 la pic 12/16, 31 la pic18. Daca se folosesc intreruperile, trebuie luat in calcul si aceasta "functie", care intrerupe o alta functie prin definitie.

Stiva de memorie este de asemenea limitata, si trebuie avut grija la pasarea parametrilor prin valoare, mai ales a structurilor mai mari. Aceeasi atentie trebuie si la variabile locate de tip structura, mai ales in combinatie cu functiile imbricate. Limitata de stiva tine de compilator, unii aloca jumatate din RAM, altii mai putin.

Link spre comentariu

Cum spuneam, subiectul este vast si timpul este limitat. Cum vezi, scriu aici la o ora aproape de 3:00 noaptea.

Nu am de gand sa ating toate "fineturile" pentru ca nu mi-am propus sa fac un curs aici (nici macar nu stiu daca pot face asa ceva; cunoasterea limbajului C pe care o am este limitata, la nivel hobby).

Sunt atat de multe lucruri despre care se poate vorbi dar este ca si in inginerie. Trebuie sa stii sa te opresti la un moment dat, gata cu imbunatatirile la un design, e timpul sa il si faci practic.

 

Cat despre nivelele hardware de stiva, sa fim noi fericiti si sa le atingem :) daca ajungem sa facem 8 functii apelabile una in alta, apoi eu zic ca stim in mod sigur si despre stivele hardware si deja am citit cam era de citit din datasheet si applications notes oferite de Microchip.

 

Exemplul de mai sus este cum este. Poate nu cel mai fericit si faza ca buffer-ul este plin de date sau gol este irelevant. Important, ca o regula de bun simt, este sa nu te apuci sa faci planuri sa ocupi toata memoria RAM. 

Daca vezi ca te aproprii semnificativ de limita superioara mai bine treci la un controller cu mai multa memorie sau se poate atasa o memorie RAM externa (ceva cu acces SPI de ex ca sa nu se consume prea multi pini controler) in care sa stochezi buffere de date. 

 

Dar se pare ca stii foarte bine despre ce vorbesti asa ca te invit sa scrii aici pe topic despre aceste aspecte enumerate mai sus. Rugamintea mea (in numele colegilor) este ca explicatiile sa fie adaptate ca pentru incepatori si nu ca pentru cei care deja stiu despre acestea.
Multumesc pentru interventie dar ar fi bine ca informatiile sa fie oferite intr-o forma mai "comestibila".
Link spre comentariu

Probabil nu sunt necesare discutii detaliate despre aceste limite, e bine de stiut. Te apuci vre-odata sa faci o functie recursiva (functie care se apeleaza pe sine, folosita in anumite cazuri la injumatatirea domeniului de calcul), si nu-ti dai seama ce se-ntampla cand depasesti limita. 31 este destul de ok, dar 8 e relativ mic.

Zona de memorie de stiva a compilatorului, e destul de putin discutata, mai ales la microcontrolere. Aceasta nu exista pentru limbaj de asamblare, unde exista doar RAM si registru. La compilatoarele pentru PC, stiva de date si cea de adrese de retur a functiilor sunt in aceasi zona de memorie, la microcontroler, stiva de adrese e hardware, separata de cea de date, care e "mapata" in RAM de compilator.

Am mai incercat o data sa explic termenul de mapare, insa se pare ca nu am success.

Problema resurselor limitate, apare la microcontrolere, in special, si de aceea e bine daca se intelege raportul resurselor folosite, dupa etapa de compilare, aftel incat programatorul sa poata estima daca are suficiente resurse ramase sa termine proiectul. Poate fi util si sa inteleaga erorile de compilare.

Link spre comentariu

Despre cuvantul cheie STATIC care se pune in fata unei variabile.

 

Atributul STATIC poate fi atasat atat unei variabile globale (care se poate folosi in tot programul si care se declara undeva la inceputul programului) sau unei variabile locale (o variabila care se declara intr-o functie si care se poate folosi doar in acea functie).

 

Daca programul nostru se gaseste doar intr-un singur fisier atunci nu conteza daca o variabila globala o facem STATIC.

Daca programul nostru se intinde in mai multe fisiere, in acest caz declararea unei variabile globale ca si STATIC face in asa fel incat acea variabila se poate folosi doar in fisierul unde se declara. Incercarea de a folosi acea variabila intr-un alt fisier care face parte din program va genera o eroare. 

 

Sintaxa este cam asa:

static int x; 

Personal am gasit utilitate pentru folosirea acestui cuvant cheie STATIC, in corelatie cu functiile, deci asociat variabilelor locale.

Spuneam ca atunci cand declaram o variabila locala, acea variabila are o durata de viata limitata pe durata executiei functiei.

 

Sa zicem ca ajungem in momentul in care intr-un program se apeleaza o functie. In acel moment programul salveaza o suma de parametri de functionare si sare la zona de memorie unde este stocata functia, si incepe sa execute codul functiei.

Printre primele lucruri care se intampla intr-o functie este evident, declararea variabilelor locale. Se aloca memorie RAM pentru acele variabile, in cursul executiei functiei valorile care se gasesc la acele adrese de variabile locale se schimba in acord  cu ce se face in functie, dar important, la finalul functiei, variabilele locale standard sunt distruse.

Adica mai exact, zona care este alocata in memoria RAM pentru acele variabile locale este eliberata si devine libera.

 

De aceea se spune ca variabilele locale sunt temporare. Ele "traiesc" doar cat se executa functia. Dupa ce functia inceteaza acele variabile dispar si continutul acestora numai este disponibil.

Daca vrem ca sa avem intr-o functie o variabila care dorim sa "supravietuiasca" dupa ce functia s-a excutat avem doua, trei variante:

 

1. Folosim o variabila globala care are durata de viata pe intreaga durata a programului. Doar ca in acest moment, daca nu avem grija, putem modifica variabila din greseala in alta sectiune de program si sa generam bug-uri.

2. Folosim variabile de tip pointer care scriu direct la o anumita adresa in memorie (subiect avansat).

3. Folosim o variabila locala declarata incluzand cuvantul cheie STATIC.

 

Asadar, cand declaram o variabila locala ca fiind STATIC, ii conferim longevitatea unei variabile globale dar diferenta este ca acea variabila locala statica va fi accesibila doar in cadrul functiei. Aceasta o face pe deoparte imuna la modificari din afara functiei, si in acelasi timp face ca valoarea sa sa fie persistenta intre apelurile functiei.

 

Pentru exemplificare sa luam urmatoarea situatie: 

int func() {  int x = 0;  int i;  for (i = 0; i < 10; i++) {    x = x + 1;  }  return x;}

Daca intr-un program principal facem asa:

void main () {  int res;  res = func();  printf("Rezultatul este: %d n", res);  res = func();  printf("Rezultatul este: %d n", res);  res = func();  printf("Rezultatul este: %d n", res);}

Rezultatul pe ecran va fi:

Rezultatul este: 10Rezultatul este: 10Rezultatul este: 10

Dupa fiecare apel de functie:

res = func();

Totul se reseteaza, varaibila locala x din functia func() este initializata cu 0 la fiecare apel de functie. Totul este mereu la fel, nimic nu se scchimba (asa e facuta aceasta functie).

 

 

Dar daca .... facem variabila locala x sa fie STATIC ce se intampla? Hai sa vedem.

int func() {  static int x = 0;  int i;  for (i = 0; i < 10; i++) {    x = x + 1;  }  return x;}

Acelasi program principal ca mai sus:

void main () {  int res;  res = func();  printf("Rezultatul este: %d n", res);  res = func();  printf("Rezultatul este: %d n", res);  res = func();  printf("Rezultatul este: %d n", res);}

Va avea rezultatul tiparit pe ecran:

Rezultatul este: 10Rezultatul este: 20Rezultatul este: 30

[Programul se poate accesa si executa online aici. Dupa ce se incarca pagina, click 'Compile' si apoi click 'Execute'. Rezultatul este in fereastra de jos.]

 

De ce se intampla asa?

 

Pentru ca dupa prima executie a functiei, variabila x va avea valoarea 10. Si este tiparita pe ecran. Dar la terminarea primei executii:

res = func();

Variabila x nu mai este distrusa, ea supravietuieste pentru ca acum este tip STATIC.

 

Si chiar daca la inceputul functiei, la declararea variabilei locale x ea este initializata cu zero, acest lucru se intampla DOAR LA PRIMA EXECUTIE a functiei.

La urmatoarele executii ale functiei, desi scrie negru pe alb ca x = 0, de fapt nu este asa. Pentru ca este o variabila tip STATIC, la urmatoarea executie variabila va avea valoarea de data trecuta. Se tine cont undeva acolo in maruntaiele compilatorului :) cand s-a executat prima data o functie.

 

Deci dupa prima executie a functiei, la a 2a executie a functiei, valoarea lui x nu mai porneste de la 0 ci porneste de unde a ramas data trecuta adica de la 10. Se adauga de 10 ori cate 1 in ciclul for() si apoi rezultatul va fi 20.

La urmatoarea executie a functiei func(), x pleaca de la 20 si rezultatul va fi 30.

 

Asadar variabila noastra locala tip STATIC, numita x (si care este de tip INT adica ia valori pe 16bit, cu valori cu semn, deci de la - 32768 pana la 32767) se comporta oarecum ca o variabila globala.

 

Ia sa incercam sa o folosim ca o variabila globala, adica sa o folosim in afara functiei unde este declarata.

void main () {  int res;    x = 100; // aici apelez o variabila locala declarata in functia func() - voi primi eroare aici  res = func();  printf("Rezultatul este: %d n", res);  res = func();  printf("Rezultatul este: %d n", res);  res = func();  printf("Rezultatul este: %d n", res);}

Rezultatul dupa compilare este ... 

main.c: In function 'main':                                                                          main.c:15:3: error: 'x' undeclared (first use in this function)

Zbang, eroare. Compilatorul ne avertizeaza ca avem o variabila x nedeclarata in functia main(). Exact ce ne asteptam, pentru ca variabila x este una locala, este definita in corpul functiei func() si nu poate fi folosita altundeva decat in functia func() unde a fost declarata.

 

O sa urmeze si licuriciu convertit.

Editat de mars01
Link spre comentariu

Creează un cont sau autentifică-te pentru a adăuga comentariu

Trebuie să fi un membru pentru a putea lăsa un comentariu.

Creează un cont

Înregistrează-te pentru un nou cont în comunitatea nostră. Este simplu!

Înregistrează un nou cont

Autentificare

Ai deja un cont? Autentifică-te aici.

Autentifică-te acum
×
×
  • Creează nouă...

Informații Importante

Am plasat cookie-uri pe dispozitivul tău pentru a îmbunătății navigarea pe acest site. Poți modifica setările cookie, altfel considerăm că ești de acord să continui.Termeni de Utilizare si Ghidări