Read documentation

Bitdefender este un lider recunoscut în domeniul securității IT, care oferă soluții superioare de prevenție, detecție și răspuns la incidente de securitate cibernetică. Milioane de sisteme folosite de oameni, companii și instituții guvernamentale sunt protejate de soluțiile companiei, ceea ce face Bitdefender cel mai de încredere expert în combaterea amenințărilor informatice, în protejarea intimității și datelor și în consolidarea rezilienței la atacuri. Ca urmare a investițiilor susținute în cercetare și dezvoltare, laboratoarele Bitdefender descoperă 400 de noi amenințări informatice în fiecare minut și validează zilnic 30 de miliarde de interogări privind amenințările. Compania a inovat constant în domenii precum antimalware, Internetul Lucrurilor, analiză comportamentală și inteligență artificială, iar tehnologiile Bitdefender sunt licențiate către peste 150 dintre cele mai cunoscute branduri de securitate din lume. Fondată în 2001, compania Bitdefender are clienți în 170 de țări și birouri pe toate continentele. Mai multe detalii sunt disponibile pe www.bitdefender.ro.

Resposabili:

  • Cristi Olaru
  • Cristi Pătrașcu
  • Cristi Popa
  • Darius Neațu
  • Liza Babu
  • Radu Nichita

Cuprins

Scopul tutorialului

Limbajele de programare ne permit să-i transmitem unui calculator instrucțiuni pe care acesta să le înțeleagă și să le execute. Deși fiecare limbaj de programare are particularitățile lui, ele au în comun multe concepte similare, care, odată înțelese într-o manieră generică, pot fi aplicate în aproape orice limbaj de programare.

Exemple de astfel de concepte includ:

  • variabile și structuri de control (de exemplu, if, while, for, return);
  • structuri de date (de exemplu, vectori, liste, dicționare, arbori);
  • algoritmi (de exemplu, sortare, găsirea unui element cu o anumită proprietate, dar și alți algoritmi mai avansați);
  • interacțiunea cu sistemul de fișiere (de exemplu, deschiderea/închiderea de fișiere, citiri, scrieri, etc.);
  • interacțiunea cu alte componente alte sistemului de operare (de exemplu, placă de rețea, alte procese, dispozitive I/O).

Pentru a înțelege cum se aplică astfel de concepte într-un limbaj de programare, trebuie să învățăm să căutăm și să aplicăm corect noțiunile prezentate în documentația acelui limbaj.

Ce reprezintă documentația unui limbaj

În programare, documentația este o colecție de texte, imagini, videoclipuri care au ca scop descrierea funcționalității unui software.

În particular, documentația unui limbaj de programare descrie funcționalitățile oferite de acel limbaj, sub forma unor reguli de sintaxă, dar și a unor biblioteci (standard sau non-stardard), framework-uri etc.

Documentațiile pot fi scrise atât de dezvoltatorii limbajelor, cât și de alte organizații, de aceea de multe ori găsim mai multe surse din care ne putem informa corect.

Ce este o bibliotecă? Dar un framework? Dar un API?

Notă: Acești termeni au multiple semnificații dependente de limbajul de programare, context etc. Nu vom da definiții exacte, ci vom oferi explicații sumare ca să vă faceți o idee despre diferența dintre aceste noțiuni.

  • bibliotecă (EN: library): este o colecție de cod reutilizabil, ce implementează diferite funcționalități și care poate fi folosit de multiple aplicații.
  • framework: este un software folosit ca schelet de bază pentru dezvoltarea aplicațiilor sau a diverselor funcționalități. Un framework expune, în general, doar acele bucăți de cod (funcții, clase) de care este nevoie pentru a îl folosi. Restul funcționalității este, în general, ținută intern de framework.
  • API (Application programming interface): Este interfața expusă de o bibliotecă/un framework/o aplicație. Un API nu este cod propriu-zis, ci este interfața prin care putem folosi un cod (de exemplu, interfața unei biblioteci în C ar putea fi reprezentată de semnăturile funcțiilor conținute, strânse într-un header - aceasta explică ce funcții există în bibliotecă, ce valoare returnează, ce parametri primesc, ce face funcția).

Exemple de documentații

  • C: Există multiple standarde de C, fiecare având diferite particularități. Limbajul C este dependent atât de standardul folosit, cât și de sistemul de operare pe care rulează. Printre documentațiile pentru diferite standarde C se numără:
    • Linux man pages - inclusă in distribuții Linux, oferă detalii despre multe funcționalități expuse de C pentru Linux.
      • Pentru C, avem 2 moduri să verificăm documentația / să căutăm în ea:
        • Google it!: de exemplu, dacă căutăm pe Google man scanf sau man stdio / man stdio.h între primele (2-3) rezultate vom găsi linkuri de man7.org sau linux.die.net/man care ne duc către documentația pentru funcția scanf / biblioteca stdio.
        • terminal: de exemplu, dacă nu avem acces la internet sau la un browser, putem rula în terminal man scanf sau man stdio / man stdio.h pentru a deschide în terminal documentația pentru funcția scanf / biblioteca stdio.
      • Observație: cele 2 metode se aplică și comenzilor Linux - de exemplu, încercați să cautați cu man ls sau man grep.
    • Windows - documentație oficială Microsoft pentru Windows;
    • ANSI C - o variantă free de documentație pentru ANSI C. Există și variantă non-free.
  • C++: La fel ca la C, nu există o documentație oficială pentru tot limbajul. Exemple de documentații:
  • Java: Oracle
  • Python: Python 3

Cum integrăm o funcționalitate în codul nostru?

Vom parcurge niște pași orientativi și vom exemplifica fiecare pas pe o problemă dată.

Problemă propusă: Avem o listă de studenți, iar pentru fiecare student știm numele și grupa. Se cere o listă sortată a studenților după următoarele criterii: lexicografic după grupă, iar în caz de egalitate sortăm lexicografic după nume. Limbajul care trebuie folosit este limbajul C.

  • Pasul 1: Identificăm problema de rezolvat

    • În acest pas este important să formulăm problema cât mai generic

    • În această problemă, un mod natural de rezolvare ar fi să stocăm studenții într-un vector de structuri. Problema de rezolvat este, în mod generic, sortarea unui vector.

  • Pasul 2: Căutăm în documentație ce ne oferă limbajul pentru a rezolva problema dată

    • Vom folosi paginile man. Observăm, la o căutare pe internet sau în documentație, că există funcția qsort documentată în man7 și linux.die. Consultăm amândouă sursele dacă este cazul (de exemplu, prima are explicații mai bune, dar doar a doua are exemplu de utilizare).

    • Să citim descrierea acestei funcții:

        The qsort() function sorts an array with _nmemb_ elements of size _size_. The _base_ argument points to the start of the array. The contents of the array are sorted in ascending order according to a comparison function pointed to by _compar_, which is called with two arguments that point to the objects being compared.
    • Semnătura funcției este:

    void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));
    • Prin urmare, această funcție va primi un vector a cărui adresă de start este în variabila base, va primi numărul de elemente (nmemb) și dimensiunea unui element (size), precum și o funcție comparator (compare).

  • Pasul 3: Testăm această funcționalitate pe un exemplu simplu

    • Anumite documentații oferă deja exemple pentru funcționalitățile oferite, așa cum se întâmplă și pentru man qsort

    • Aceste exemple pot fi uneori rulate direct pe platforma lor. Este util să faceți asta și să schimbați datele pentru a vedea exact cum se comportă acea funcționalitate.

    • În acest caz, exemplul de pe linux.die.net/man/3/qsort nu poate fi rulat pe platformă, așa că vom face un program în care vom rula exemplul oferit de documentație. Facem acest lucru pentru a ne asigurăm că funcția face ce ne dorim și pentru a înțelege exact cum să o folosim pentru problema noastră. Luăm exact codul de pe acea pagină, care sortează string-uri primite de la linia de comandă:

    // example.c: code sample from https://linux.die.net/man/3/qsort
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    static int cmpstringp(const void *p1, const void *p2)
    {
        /* The actual arguments to this function are "pointers to
        pointers to char", but strcmp(3) arguments are "pointers
        to char", hence the following cast plus dereference */
     
    return strcmp(* (char * const *) p1, * (char * const *) p2);
    }
     
    int main(int argc, char *argv[])
    {
        int j;
     
        if (argc < 2) {
            fprintf(stderr, "Usage: %s <string>...\n", argv[0]);
            exit(EXIT_FAILURE);
        }
        qsort(&argv[1], argc - 1, sizeof(char *), cmpstringp);
     
        for (j = 1; j < argc; j++)
            puts(argv[j]);
        return 0;
    }
    • Compilăm și rulăm codul:

    $ gcc -Wall example.c -o example
    $ ./example mama are mere
    are
    mama
    mere
    • Observăm că a sortat cele 3 string-uri primite ca argumente în linia de comandă, exact cum ne așteptam. Trecem la pasul următor.

  • Pasul 4: Integrăm în proiectul nostru

    • În acest pas vom scrie mai întâi o funcție comparator:

    static int cmp_students(const void* first_ptr, const void* second_ptr)
    {
        student_t*  first = (student_t *) first_ptr;
        student_t* second = (student_t *) second_ptr;
        int cmp_groups = strcmp(first->group, second->group);
        if (cmp_groups != 0)
            return cmp_groups;
     
        return strcmp(first->name, second->name);
    }
    • Acum vom scrie codul complet și il vom testa:

    // sort_students.c
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    typedef struct {
        char *group;
        char *name;
    } student_t;
     
    static int cmp_students(const void* first_ptr, const void* second_ptr)
    {
        student_t*  first = (student_t *) first_ptr;
        student_t* second = (student_t *) second_ptr;
        int cmp_groups = strcmp(first->group, second->group);
        if (cmp_groups != 0)
            return cmp_groups;
     
        return strcmp(first->name, second->name);
    }
     
    int main(void)
    {
        student_t students[] = {
            { "312CA", "Popescu Ion" },
            { "311CA", "Dumitrescu Mihai" },
            { "315CA", "Almasan Maria" },
            { "313CA", "Grigorescu Alex" },
            { "311CA", "Barbu Gigel" },
            { "312CA", "Vadim Tudor" },
            { "312CA", "Florescu Teodora" },
            { "314CA", "Fodor Maria" },
            { "314CA", "Alexandrescu Matei" },
            { "313CA", "Radu Ioana" },
            { "311CA", "Mugurel Alexandra" },
            { "315CA", "Ungureanu Andreea" }
        };
        int no_students = sizeof(students) / sizeof(students[0]);
        qsort(students, no_students, sizeof(students[0]), cmp_students);
     
        for (int i = 0; i < no_students; i++) {
            printf("%s, %s\n", students[i].group, students[i].name);
        }
     
        return 0;
    }
    • Foarte important: Nu uitați să testați să vedeți dacă problema voastră este rezolvată corect!

    • Compilăm și rulăm codul:

    $ gcc  -Wall -Wextra sort_students.c -o sort_students
    $ ./sort_students
    311CA, Barbu Gigel
    311CA, Dumitrescu Mihai
    311CA, Mugurel Alexandra
    312CA, Florescu Teodora
    312CA, Popescu Ion
    312CA, Vadim Tudor
    313CA, Grigorescu Alex
    313CA, Radu Ioana
    314CA, Alexandrescu Matei
    314CA, Fodor Maria
    315CA, Almasan Maria
    315CA, Ungureanu Andreea

Comparație între o documentație și platformele de tip forum

Prin platforme de tip forum ne referim la: Stackoverflow, Quora, Ask Ubuntu, și alele.

Avantaje

  • Răspund în general la probleme specifice pe care un programator le poate întâmpina și care nu sunt direct adresate în documentații, legate de cum poate fi folosit limbajul pentru a obține o funcționalitate dorită, nu despre limbajul în sine. (în special probleme de debugging);
  • Oricine poate posta întrebări;
  • Răspunsurile sunt votate și ordonate descrescător în funcție de voturi.

Dezavantaje

  • Nu întotdeauna răspunsurile oferite sunt corecte!!! Mare atenție, chiar dacă răspunsurile sunt votate de alții, nu sunt întotdeauna corecte, așa că ele trebuie și verificate. Testați orice cod luat de pe aceste platforme!
  • Nu întotdeauna găsiți întrebarea pe care o doriți. Scopul unei astfel de platforme este să vă lămurească cu probleme / exemple specifice, atunci când documentația oficială nu este de ajuns.

În continuare vă vom oferi un exemplu de postare de pe Stackoverflow în care primul răspuns este unul greșit, deși este votat de mulți utilizatori: link.

În primul răspuns, este specificat că următoarele linii de cod sunt aproximativ același lucru din punct de vedere practic:

// varianta 1
artist = (char *) malloc(0);
 
// varianta 2
artist = NULL;

Cu toate acestea, dacă rulați următorul cod pe diverse platforme:

int *i = malloc(0);
*i = 100;
printf("%d\n", *i);

… surprinzător poate, dar pe anumite platforme veți obține rezultatul 100.

În schimb, dacă scrieți:

int *i = NULL;
*i = 100;
printf("%d\n", *i);

cel mai probabil veți primi Segmentation fault.

Așadar, practic vorbind, cele 2 coduri se comportă diferit. Deși primul cod pare că merge în anumite cazuri, în realitate el va ascunde un acces invalid la memorie pentru că se încearcă scrierea la o adresă de memorie nealocată. Să rulam cu valgrind acest program:

// bad_example.c
#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
    int *i = malloc(0);
    *i = 100;
    printf("%d", *i);
 
    return 0;
}
valgrind --leak-check=full ./bad_example
==51943== Memcheck, a memory error detector
==51943== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==51943== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==51943== Command: ./bad_example
==51943==
==51943== Invalid write of size 4
==51943==    at 0x109187: main (in /path/to/bad_example)
==51943==  Address 0x4a5a040 is 0 bytes after a block of size 0 alloc'd
==51943==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==51943==    by 0x10917E: main (in /path/to/bad_example)
==51943==
==51943== Invalid read of size 4
==51943==    at 0x109191: main (in /path/to/bad_example)
==51943==  Address 0x4a5a040 is 0 bytes after a block of size 0 alloc'd
==51943==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==51943==    by 0x10917E: main (in /path/to/bad_example)
==51943==
100==51943==
==51943== HEAP SUMMARY:
==51943==     in use at exit: 0 bytes in 1 blocks
==51943==   total heap usage: 2 allocs, 1 frees, 1,024 bytes allocated
==51943==
==51943== 0 bytes in 1 blocks are definitely lost in loss record 1 of 1
==51943==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==51943==    by 0x10917E: main (in /path/to/bad_example)
==51943==
==51943== LEAK SUMMARY:
==51943==    definitely lost: 0 bytes in 1 blocks
==51943==    indirectly lost: 0 bytes in 0 blocks
==51943==      possibly lost: 0 bytes in 0 blocks
==51943==    still reachable: 0 bytes in 0 blocks
==51943==         suppressed: 0 bytes in 0 blocks
==51943==
==51943== For lists of detected and suppressed errors, rerun with: -s
==51943== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

Observăm, așa cum ne-am aștepta, că avem un leak de memorie. Mai mult, accesăm locații de memorie pe care nu ar trebui, odată la atribuire, odată la afișare.

De ce uneori se afișează 100?

Răspunsul pe scurt este că malloc, în anumite situații, în spate alocă extra spațiu față de cât îi spunem noi. Dar acel spațiu nu este un spațiu pe care noi să-l putem folosi (nu ne garantează nimeni nici faptul că este alocat acel extra spațiu), iar accesul lui va conduce la comportament nedefinit, deoarece acolo pot fi stocate alte date. În anumite cazuri, se alocă acel spațiu extra unde se va stoca valoarea 100, iar mai apoi se va citi valoarea și se va afișa. Acesta este și motivul pentru care, în aparență, programul pare că merge. Mai multe detalii veți afla la cursul de SO.

Din nou, acest acces la memorie este incorect, nu faceți așa ceva deoarece aveți comportament nedefinit. Pentru a evita aceste situații vă recomandăm să utilizați tool-uri de memorie, cum ar fi valgrind.

Vă invităm să citiți toată conversația What’s the point of malloc(0)?, oferă niște puncte de vedere interesante despre acest subiect.

Concluzie: mare atenție când folosiți aceste platforme. Recomandarea noastră este să căutați în mai multe locuri răspunsuri și să testați orice cod folosiți din surse externe înainte de a-l integra în aplicațiile voastre.

programare/tutoriale/read_docs.txt · Last modified: 2022/01/24 16:38 by stefan.popa99
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0