LABORATORUL 02

Constante. Variabile.

Durată: 2 ore Limbaj: C / C++ Precedent: Laboratorul 1 PDF îndrumar EN English version

O constantă este o valoare fixă scrisă direct în cod; o variabilă este un nume asociat unei zone de memorie al cărei conținut se poate schimba. Tipul ales pentru fiecare determină cât spațiu ocupă, ce valori poate lua și ce operații se pot aplica.

1Obiectivele lucrării

  • Scrierea constantelor întregi în bazele 10, 8 și 16 și folosirea sufixelor U și L
  • Utilizarea constantelor reale, caracter și șir de caractere, inclusiv a secvențelor escape
  • Declararea variabilelor cu tipul potrivit domeniului de valori
  • Determinarea experimentală a dimensiunilor cu operatorul sizeof
  • Distingerea domeniului de vizibilitate de durata de viață a unei variabile

2Constante

Constantele literale sunt valori fixe care nu pot fi modificate în timpul execuției. Tipul lor este determinat automat de compilator, pe baza valorii și a sintaxei folosite.

Constante întregi

BazăConvenție de scriereExemple
Zecimală (10)prima cifră diferită de 023, -555L, 3276L
Octală (8)prima cifră este 0; cifrele 8 și 9 sunt ilegale067, -067, 067u
Hexazecimală (16)prefix 0x sau 0X0x1F, 0xFFu, 0xAB3L
Capcana zeroului din față int x = 010; nu înseamnă zece, ci opt - prefixul 0 declanșează interpretarea în baza 8. Este o greșeală frecventă la scrierea datelor calendaristice sau a codurilor cu zerouri nesemnificative.

Determinarea implicită a tipului

SituațieTip atribuitExemplu
valoarea încape în domeniul intint15, 23
depășește int, încape în longlong99999
depășește longdouble98765432100

Tipul implicit poate fi forțat cu sufixe: U/u pentru unsigned, L/l pentru long, combinate în UL.

Constante caracter și secvențe escape

SecvențăSemnificațieCod ASCII
'\n'linie nouă10
'\t'tabulator orizontal9
'\r'retur de car13
'\0'caracter nul - marchează sfârșitul unui șir0
'\\'backslash92
'\''apostrof39
Caracter vs. șir'A' este o constantă caracter și ocupă 1 octet. "A" este un șir și ocupă 2 octeți: litera plus terminatorul '\0'. Confuzia dintre ele produce erori greu de depistat.

3Tipuri fundamentale de date

unsigned char: 8 biti, domeniul 0...255254255012+1+1dupa 255 urmeaza 0, nu 256depasirea nu produce eroare la compilare si nici la executievaloarea se reia de la celalalt capat al domeniului
Fig. - Depasirea domeniului la un tip intreg fara semn. Aritmetica se face modulo 2^n, deci incrementarea valorii maxime readuce variabila la zero, tacut.
TipDimensiune tipicăDomeniu de valoriSpecificator printf
char1 octet−128 … 127%c / %d
unsigned char1 octet0 … 255%u
short2 octeți−32 768 … 32 767%hd
int4 octeți−2 147 483 648 … 2 147 483 647%d
unsigned int4 octeți0 … 4 294 967 295%u
long long8 octeți±9,2 · 10¹⁸%lld
float4 octeți±3,4 · 10³⁸, ~7 cifre semnificative%f
double8 octeți±1,7 · 10³⁰⁸, ~15 cifre semnificative%lf
bool (C++)1 octettrue / false-
Dimensiunile nu sunt garantateStandardul impune doar relații de ordine (char ≤ short ≤ int ≤ long). Pe un microcontroler pe 8 biți, int are 2 octeți; pe un PC obișnuit, 4. De aceea se verifică întotdeauna cu sizeof.

4Variabile

Declarația unei variabile rezervă spațiu în memorie și îi asociază un nume și un tip:

sintaxă
tip nume;                  // declarație simplă, valoare nedefinită
tip nume = valoare;        // declarație cu inițializare
tip n1, n2, n3;            // mai multe variabile de același tip
const tip nume = valoare;  // valoare care nu mai poate fi modificată
Variabile neinițializateO variabilă locală declarată fără inițializare conține „gunoi" - orice se afla anterior la acea adresă în memorie. Citirea ei înainte de atribuire produce rezultate imprevizibile, care diferă de la o rulare la alta.

5Vizibilitate și durată de viață

ClasăUnde este vizibilăCât trăieșteValoare inițială implicită
auto (locală)în blocul în care e declaratăpână la ieșirea din blocnedefinită
static localăîn blocul în care e declaratătoată execuția programului0
globalăîn tot fișierul, după declarațietoată execuția programului0
externîn alte fișiere sursătoată execuția programului0
registerîn blocul în care e declaratăpână la ieșirea din blocnedefinită
Diferența cheieO variabilă static locală este vizibilă doar în funcția ei, dar nu este distrusă la ieșire: la următorul apel își regăsește valoarea. Este mecanismul standard pentru contoare care trebuie să rețină starea între apeluri.

6Simulator de execuție

Urmăriți pas cu pas cum se comportă o variabilă locală obișnuită față de una declarată static, la trei apeluri succesive ale aceleiași funcții.

Variabilă locală vs. variabilă statică

7Cod sursă

dimensiuni.c - verificarea tipurilor
#include <stdio.h>
#include <limits.h>

int main(void)
{
    printf("Tip            Octeti   Minim              Maxim\n");
    printf("-------------------------------------------------------\n");
    printf("char           %2zu     %-18d %d\n", sizeof(char),  CHAR_MIN,  CHAR_MAX);
    printf("short          %2zu     %-18d %d\n", sizeof(short), SHRT_MIN,  SHRT_MAX);
    printf("int            %2zu     %-18d %d\n", sizeof(int),   INT_MIN,   INT_MAX);
    printf("long           %2zu     %-18ld %ld\n", sizeof(long), LONG_MIN, LONG_MAX);
    printf("float          %2zu\n", sizeof(float));
    printf("double         %2zu\n", sizeof(double));

    return 0;
}
constante.c - baze de numerație
#include <stdio.h>

int main(void)
{
    int zecimal     = 100;      // baza 10
    int octal       = 0144;     // baza 8  -> tot 100
    int hexazecimal = 0x64;     // baza 16 -> tot 100

    printf("%d %d %d\n", zecimal, octal, hexazecimal);   // 100 100 100

    // aceeasi valoare afisata in baze diferite
    printf("zecimal: %d   octal: %o   hexa: %X\n", 255, 255, 255);

    // capcana zeroului din fata
    int gresit = 010;
    printf("010 inseamna %d, nu 10!\n", gresit);         // 8

    // caracter vs sir
    char c = 'A';
    char s[] = "A";
    printf("sizeof('A') in C = %zu, sizeof(\"A\") = %zu\n", sizeof(c), sizeof(s));

    return 0;
}
depasire.c - ce se întâmplă la depășire
#include <stdio.h>

int main(void)
{
    short mic = 32767;          // valoarea maxima pentru short
    printf("inainte: %d\n", mic);
    mic = mic + 1;              // depasire!
    printf("dupa +1: %d\n", mic);   // -32768, se trece la valoarea minima

    unsigned char u = 255;
    u = u + 1;
    printf("unsigned char 255+1 = %u\n", u);   // 0, revine la inceput

    return 0;
}
De ce se întâmplă astaDepășirea nu produce eroare: valoarea „se rotește" la celălalt capăt al domeniului, pentru că biții disponibili se epuizează. Este responsabilitatea programatorului să aleagă un tip suficient de încăpător.

8Atelier de cod

Dimensiunile tipurilor și comportamentul la depășire nu se învață din tabele, ci văzându-le. Rulați programele de mai jos și modificați valorile ca să vedeți exact unde se rupe fiecare tip.

Cât ocupă fiecare tip și ce valori încap în el
#include <stdio.h>

int main(void)
{
    printf("char        %d octet(i)\n", (int)sizeof(char));
    printf("short       %d octet(i)\n", (int)sizeof(short));
    printf("int         %d octet(i)\n", (int)sizeof(int));
    printf("long long   %d octet(i)\n", (int)sizeof(long long));
    printf("float       %d octet(i)\n", (int)sizeof(float));
    printf("double      %d octet(i)\n", (int)sizeof(double));

    printf("\nConstante scrise in baze diferite:\n");
    printf("  67 zecimal = %d\n", 67);
    printf("  0103 octal = %d\n", 0103);
    printf("  0x43 hexa  = %d\n", 0x43);
    printf("  'C' este   = %d\n", 'C');
    return 0;
}
De încercatToate cele patru constante din final sunt aceeași valoare, scrisă în patru feluri. Compilatorul nu reține baza - reține doar numărul.
Depășirea de domeniu - vedeți cum „se învârte” valoarea
#include <stdio.h>

int main(void)
{
    short s = 32767;          /* valoarea maxima pentru short */
    printf("s        = %d\n", s);
    s = s + 1;
    printf("s + 1    = %d   <- a devenit negativ!\n", s);

    unsigned char u = 255;
    printf("\nu        = %d\n", u);
    u = u + 1;
    printf("u + 1    = %d   <- a revenit la zero\n", u);

    char c = 200;             /* 200 nu incape in char cu semn */
    printf("\nchar c   = %d\n", c);
    return 0;
}
De încercatDepășirea nu produce nicio eroare la execuție: valoarea se reia de la celălalt capăt al domeniului. De aceea alegerea tipului potrivit este o decizie, nu o formalitate.

9Sarcini de lucru

  • Rulați programul de verificare a dimensiunilor și notați valorile obținute pe calculatorul din laborator.
  • Scrieți valoarea 200 în bazele 10, 8 și 16 și verificați că afișarea este identică.
  • Declarați o variabilă short cu valoarea maximă și adunați 1; explicați rezultatul.
  • Comparați sizeof('A') în C cu sizeof('A') în C++ - rezultatele diferă, explicați de ce.
  • Scrieți o funcție cu un contor static și apelați-o de cinci ori; afișați valoarea la fiecare apel.
  • Declarați o variabilă locală fără inițializare, afișați-o și rulați programul de trei ori - observați dacă valoarea se schimbă.
  • Folosiți toate secvențele escape din tabel într-un singur printf și observați efectul.

10Aplicație de aprofundare

ExtindereScrieți un program care determină experimental, fără a folosi <limits.h>, valoarea maximă a tipului int de pe calculatorul vostru: porniți de la 1 și dublați valoarea până când aceasta devine negativă, apoi ajustați. Comparați rezultatul cu INT_MAX. Repetați pentru unsigned int.

11Întrebări de verificare

12Resurse