Breviar 07 - Funcții cu pointeri; Șiruri de caractere

În acest laborator veţi învăţa să:

  • Trimiteţi parametri prin adresă.
  • Utilizaţi şiruri de caractere şi funcţii pentru şiruri de caractere.
  • Definiţi funcţii care prelucrează şiruri de caractere.

Pointeri

Atunci când definiţi o variablilă, compilatorul va fi informat despre 2 lucruri:

  • numele variabilei
  • tipul variabilei

De exemplu, în declaraţia:

int n;

compilatorul va rezerva o zonă de memorie pentru această variabilă, de dimensiune sizeof(int) (4 Bytes, pe arhitecturi de 32bit), şi va asocia respectivei zone de memorie numele n.

Atunci când se face o atribuire,

n = 7;

în respectiva zonă de memorie va fi înscrisă noua valoare, 7.

Numele variabilelor în C au sens doar în domeniul în care au fost definite acele variabile. Acest domeniu de definiţie al unei variabile poartă numele de scope. De exemplu, în programul de mai jos, există 4 variabile distincte, toate purtând numele n!

#include <stdio.h>
 
int n; /* Variabila 1 */
 
int f(int n /* Variabila 2 */) {
   n = 42; /* Variabila 2 */
}
 
int g() {
   int n; /* Variabila 3 */
   n = 42; /* Variabila 3 */
}
 
int h() {
   n = 42; /* Variabila 1 */
}
 
int main() {
   int n; /* Variabila 4 */
   n = 42; /* Variabila 4 */
   return 0;
}

Observăm astfel, că nu putem accesa direct prin nume variabile definite într-o altă funcţie, deoarece numele nu au sens decât în interiorul funcţiei în care au fost declarate. Parametrii formali ai unei funcţii nu sunt altceva decât nişte variabile locale, care în momentul apelului primesc o copie a valorilor cu care a fost apelată funcţia. O funcţie de incrementare scrisă astfel:

void incorrect_increment(int n) {
   /* Se va incrementa doar o copie a valorii cu care a fost apelată funcţia! */
   n++;
}

Nu face decât să incrementeze o variaiblă locală funcţiei, efect ce nu poate fi observat din exterior!

Pentru a accesa un obiect definit într-o altă funcţie, îl putem referi, totuşi, folosindu-ne de adresa acestuia în memorie. Adresele în C poartă numele de pointeri.

Pentru a obţine adresa unei variabile, vom folosi operatorul & pus în faţa numelui variabilei, iar pentru a ne referi la valoarea memorată la o anumită adresă, vom folosi operatorul *.

Să ne amintim că pentru a citi valori cu ajutorul funcţiei scanf, trebuia să precedăm numele variabilelor citite cu operatorul &. Acest lucru se întâmplă deoarece din interiorul funcţiei scanf, unde se produce citirea, nu sunt accesibile prin nume variabilele definite în funcţia apelantă.

O funcţie care incrementează în mod corect o valoare se va scrie astfel:

void correct_increment(int* pn) {
   /* Vom incrementa aici variabila care este memorată la adresa pn */
   (*pn)++;
}

iar pentru a incrementa o variabilă numită n, va trebui să comunicăm funcţiei de incrementare locaţia acestei variabile astfel:

int main() {
   int n;
   n = 0;
   correct_increment(&n); /* Incrementăm valoarea memorată la adresa variabilei n. */
   return 0;
}

Putem să ne imaginăm memoria virtuală a calculatorului ca pe un vector foarte lung de Bytes, iar un pointer ca pe un indice în acel vector. Dimensiunea unui pointer (deci a unei adrese de memorie) depinde de tipul sistemului de operare.

Pe sistemele de operare de 32bit, fie ele Windows, Linux, Mac OS, sau altele, dimensiunea oricărui pointer va fi de 32 biţi, adică 4 Bytes, cu valori acceptate între 0 şi 2^32-1, adică un total de 4GB.

Şiruri de caractere

Un şir de caractere în limbajul C este o succesiune de octeţi care începe de la o adresă dată şi se termină în mod convenţional cu primul Byte întâlnit care are valoarea numerică 0 (care se poate scrie şi ca '\0', caracterul ce are codul ASCII egal cu 0).

Şirurile de caractere se memorează fie în vectori de tipul char, fie în zone de memorie alocate dinamic (despre care vom discuta la laboratorul de alocare dinamică).

De exemplu, şirul de caractere “abc” va fi reprezentat prin vectorul:

{'a', 'b', 'c', '\0'}

şi deci va ocupa 4 Bytes.

Biblioteca string.h conţine o serie de funcţii predefinite pentru prelucrarea şirurilor de caractere. Cele mai importante dintre ele sunt:

int strcmp(const char* a, const char* b);

Funcţie care compară două şiruri de caractere, şi întoarce ca rezultat un număr întreg care are valoarea:

  • -1, dacă primul şir este mai mic lexicografic decât al doilea;
  • 0, dacă cele două şiruri sunt egale;
  • +1, dacă primul şir este mai mare lexicografic decât al doilea;
char* strcpy(char* destination, const char* source);
char* strncpy(char* destination, const char* source, unsigned int n);

Funcţii care copiază şirul de caractere care începe de la adresa source într-o zonă continuă de memorie care începe de la adresa destination. Copierea se opreşte după întâlnirea terminatorului de şir în source (sau în cazul strncpy, după copierea a cel mult n caractere, indiferent dacă s-a atins sau nu sfârşitul şirului). Valoarea returnată de funcţie este egală cu parametrul destination.

int strlen(source);

Funcţie care caculează lungimea unui şir care începe de la adresa source. Atenţie! Numărarea lungimii se face până la întâlnirea terminatorului de şir, dar acesta nu este inclus în lungimea totală.

char* strcat(char* destination, const char* source);
char* strncat(char* destination, const char* source, unsigned int n);

Funcţie care copiază în continuarea şirului care începe de la adresa destination, conţinutul şirului care începe de la adresa source. Copierea se opreşte după întâlnirea terminatorului de şir în source (sau în cazul strncat, după copierea a cel mult n caractere, indiferent dacă s-a atins sau nu sfârşitul şirului). Valoarea returnată de funcţie este egală cu parametrul destination.

char* strchr(const char* source, const char key); // Prima apariţie a caracterului key.
char* strrchr(const char* source, const char key); // Ultima apariţie a caracterului key.
char* strstr(const char* source, const char* key); // Prima apariţie a şirului key.

Acest funcţii sunt folosite pentru a căuta o un caracter sau un şir de caractere în interiorul şirului care începe de la adresa source.

Valoarea returnată este fie adresa apariţiei găsite, daca valoarea există în şir, fie NULL, în cazul în care valoarea nu a fost găsită.

Citirea şi afişarea şirurilor de caractere

Pentru a citi sau scrie şiruri de caractere se pot folosi funcţiile scanf si printf, cu variantele lor de utilizare:

int scanf("%s", destination); // Observaţi că s este deja o adresă de memorie!
int printf("%s", source);

Alternativ, se pot folosi şi funcţiile:

char* gets(char* destination);
char* fgets(char* destination, int n, FILE* input_file);
int puts(char* source);

Funcţia gets este cauza multor erori grave de programare deoarece nu există nici o protecţie în cazul în care şirul introdus este mai lung decât spaţiul de memorie alocat în program, ceea ce poate duce la suprascrierea accidentală a altor date din program. Pentru a limita citirea la maxim n caractere, folosiţi funcţia fgets. Fişierul asociat consolei (cel de-al treilea parametru) este în C constanta stdin.

programare-cc/breviar-7.txt · Last modified: 2020/11/22 16:21 by bogdan.nutu
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