>> Inapoi <<

=========
Capitolul 10
=========
======================
Siruri de caractere si pointeri
======================
Un caracter dintr-un sir de caractere "a" poate fi accesat folosind indexul 
sirului (a[i], de exemplu) sau folosind pointeri la caracter.


------------------------------------------------------
Marcatorul "sfarsit de sir de caractere" \0
------------------------------------------------------
Prin conventie, un sir de caractere se termina prin marcatorul (santinela, 
delimitator) \0, sau caracterul nul. De exemplu, sirul "abc" este memorat pe 
4 caractere, ultimul fiind \0. Deci numarul de elemente al sirului este 3, iar
dimensiunea 4. 

-----------
Exemplu:
-----------
                #define  MAXWORD 100
                void main()
                 {
                  char w[MAXWORD];
                  . . . . .
                 }
Initializarea (citirea) unui sir se poate face in mai multe moduri:

        1. Initializarea fiecarui element cu cate un caracter:
        
                   w[0] = 'A';
                   w[1] = 'B';
                   w[2] = 'C';
                   w[3] = '\0';
           
        2. Folosind functia "scanf()":
        
                   scanf("%s", w);
           
           Formatul "%s" este folosit pentru citirea unui sir de caractere. 
            Distingem trei pasi:
             - pozitionare pe primul caracter al sirului;
             - se citesc toate caracterele diferite de  si se 
               introduc in "w";
             - citirea se face pana cand intalnim EOF; acum se plaseaza la 
               sfarsitul sirului '\0'.
               
           Din moment ce numele unui sir este un pointer la adresa de baza a 
           sirului, expresia "w" este echivalenta cu "&w[0]".
           Daca sirul citit are mai multe caractere decat cele rezervate, 
           atunci se va obtine o eroare.
           
           Atentie ! 'a' si "a" sunt diferite. Prima este o constanta  
           caracter, iar a doua este o constanta sir de caractere. 

                                ----- ------ 
                         "a" = | 'a' | '\0' |           
                                ----- ------

        3. Sirurile se pot initializa la fel ca si caracterele
        
                        char s[] = "abc";
                        
           sau echivalent
           
                        char s[] = {'a', 'b', 'c', '\0'};
                        
        4. Putem folosi si un pointer catre un sir constant, dar interpretarea
            este diferita:
           
                        char *p = "abc";
                      
           Va reamintim ca numele unui sir poate fi tratat ca un pointer catre
            adresa de baza a sirului din memorie. 
           Constanta "abc" este memorata de catre compilator. In  acelasi timp,
            aceasta este "un nume de sir". 
           
Asadar, diferenta dintre un sir initializat cu o constanta sir si un pointer 
 initializat tot cu o constanta sir este ca sirul contine 
caractere individuale urmate de caracterul "\0", in timp ce pointerul este 
 asignat cu adresa sirului constant din memorie. 
           
-----------
Exemplu: Utilizarea sirurilor de caractere (ca vectori). 
-----------  Citim o linie de caractere dintr-un sir, le tiparim in ordine 
   inversa si adunam literele din sir.
         
#include 
#include 

#define MAXSTRING  100

main()
 {
  char c, name[MAXSTRING];
  int i, sum = 0;
  
  printf("\nSalut! Care este numele tau? ");
  for (i = 0; (c = getchar()) != '\n'; ++i) 
   {
    name[i] = c;
    if (isalpha(c))
      sum += c;
   }
  name[i] = '\0';
  printf("\n%s%s%s\n%s",
         "Ma bucur ca te-am intalnit ",name,".",
         "Numele tau scris invers este ");
  for (--i; i >= 0; --i)
    putchar(name[i]);
  printf("\n%s%d%s\n\n%s\n",
         "si numele tau are ", sum," litere .",
         "La revedere. ");
 }
    

------------------------------------------------------------
Folosirea pointerilor pentru procesarea unui sir
------------------------------------------------------------
Vom discuta despre folosirea pointerilor pentru procesarea unui sir si cum se 
pot folosi acestea pentru a fi transmise ca parametri unei functii. Vom scrie 
un exemplu de program interactiv care citeste intr-un sir o linie de caractere
introdusa de utilizator. 
 Programul va crea un nou sir si-l va tipari.

------------
Exemplu:
------------
#include 
#define MAXLINE 100

void main()
 {
  char linie[MAXLINE], *schimba(char *);
  void citeste_in(char *);
  
  printf("\nDati un sir:");
  citeste_in(linie);
  printf("\n%s\n\n%s\n\n",
         "Asa arata sirul dupa schimbare:", schimba(linie));
 }
 
void citeste_in(char s[])
 {
  int c, i = 0;
  
  while ((c = getchar()) != EOF && c != '\n')
    s[i++] = c;
  s[i] = '\0';
 } 
 
char *schimba(char *s)
 {
  static char sir_nou[MAXLINE];
  char        *p = sir_nou;
  
  *p++ = '\t';
  for ( ; *s != '\0'; ++s)
    if (*s == 'e')
      *p++ = 'E';
    else 
      if (*s == ' ')
       {
        *p++ = '\n';
        *p++ = '\t';
       }
      else 
        *p++ = *s;
  *p = '\0';
  return sir_nou;
 }

----------
Intrebare: De ce vectorul "sir_nou" a fost declarat static ?
----------

Deoarece numele "sir_nou" este tratat ca un pointer catre adresa de baza a 
sirului. Fiind declarat "static", acesta se pastreaza in memorie si dupa ce se
iese din functia "schimba()". Acest lucru nu s-ar fi intamplat si daca, de 
exemplu, sirul ar fi fost declarat "auto". 
 
----------
Exemplu: Functie C pentru numararea cuvintelor unui sir de caractere
----------
#include 

int numarare_cuvinte(char *s)
 {
  int contor = 0;
  
  while (*s != '\0')
   {
    while (isspace(*s))                    /* sarim spatiile goale */
      ++s;
    if (*s != '\0')                        /* gasim un cuvant */
     {
      ++contor;
      while (!isspace(*s) && *s != '\0')   /* sarim peste cuvant */
        ++s;
     }
   }
  return contor; 
 }
 

----------------------------------------------------
Trimiterea argumentelor catre "main()"
----------------------------------------------------
C pune la dispozitie siruri de orice tip, inclusiv siruri de pointeri. Pentru 
scrierea de programe care folosesc argumente in linia de comanda, trebuie sa 
folosim siruri de pointeri catre caractere. Pentru aceasta, functia "main()" 
foloseste doua argumente, numite 
generic "argc" si "argv". 

-----------
Exemplu:
-----------
#include 

void main(int argc, char *argv[])
 {
  int i;
  
  printf("argc = %d\n", argc);
  for (i = 0; i < argc; ++i)
    printf("argv[%d] = %s\n", i, argv[i]);
 }
 
Variabila "argc" precizeaza numarul de argumente din linia de comanda. Sirul 
"argv" este un sir de pointeri catre caracter si poate fi gandit ca vector de 
siruri de caractere. Deoarece elementul "argv[0]" contine intotdeauna numele 
comenzii, rezulta ca valoarea lui "argc" va fi mai mare sau egala cu 1. 

Compilam programul de mai sus si obtinem executabilul "prog1.exe". Daca dam 
comanda

                prog1
                
atunci pe ecran se va afisa 

                argc = 1
                argv[0] = prog1
                
Daca dam comanda

                prog1 fisier1 fisier2
                
atunci pe ecran se va afisa 

                argc = 3
                argv[0] = prog1
                argv[1] = fisier1
                argv[2] = fisier2
                
Parametrul "argv" s-ar fi putut declara si astfel

                char **argv;
                
Acesta este un pointer catre pointer catre "char" si acesta poate fi gandit ca 
un sir de pointeri catre "char", care la randul lor pot fi ganditi ca vector de
siruri de caractere. Observati ca nu alocam spatiu in memorie pentru sirurile 
din linia de comanda. Acest lucru este facut de insusi sistemul C cand atribuie
valori pentru argumentele "argc" si "argv".


-----------------------------------------------------
Lucrul cu sirurile din biblioteca standard
-----------------------------------------------------
Biblioteca standard  contine multe functii utile pentru lucrul cu 
siruri de caractere. Sirurile ce sunt argumente trebuie terminate cu '\0' si 
toate returneaza un intreg sau o valoare a unui pointer catre "char".  

      Cateva functii utile pentru lucrul cu siruri de caractere
      ---------------------------------------------------------
      - char *strcat(char *s1, const char *s2);
      
        Functia primeste doua argumente, le concateneaza si pune rezultatul in "
         s1". Programatorul trebuie sa verifice daca "s1" are suficient spatiu
         pentru pastrarea rezultatului. Se returneaza sirul "s1".
        
      - int strcmp(const char *s1, const char *s2);
      
        Sunt trimise doua siruri de caractere si se returneaza un intreg care 
         este mai mic strict, egal sau mai mare strict decat 0 dupa cum "s1" 
         este mai mic, egal sau mai mare lexicografic decat "s2".
        
      - char *strcpy(char *s1, const char *s2);
      
        Sirul "s2" este copiat in "s1" pana cand se intalneste '\0'. Ceea ce se
         gaseste in "s1" se suprascrie. Se presupune ca "s1" are suficient 
         spatiu pentru pastrarea rezultatului. Se returneaza valoarea lui "s1".
        
      - unsigned strlen(const char *s);
      
        Pastreaza numarul de caractere inaintea lui '\0'.
      ----------------------------------------------------------------  

Aceste functii sunt scrise in C si sunt foarte scurte. Variabilele din ele sunt
de obicei declarate "register" pentru a face executia mai rapida. 

----------
Exemplu: Functia "strlen()" (o varianta).
----------

        unsigned strlen(const char *s)
         {
          register int n = 0;
          
          for ( ; *s != '\0'; ++s)
            ++n;
          return n;
         }
         
     -----------------------------------------------------------
     | Declaratii          si          initializari            |
     -----------------------------------------------------------
     | char s1[] = "tara noastra frumoasa si bogata",          |
     |      s2[] = "facultatea de informatica";                |
     -----------------------------------------------------------
     |         Expresie         |       Valoare                |
     -----------------------------------------------------------
     |    strlen(s1)            |           31                 |
     |    strlen(s2 + 8)        |           17                 |
     |    strcmp(s1, s2)        |     numar pozitiv            |
     -----------------------------------------------------------
     |      Instructiune        |   Ce se va tipari  ?         |
     -----------------------------------------------------------
     | printf("%s", s1 + 13);   |   frumoasa si bogata         |
     | strcpy(s2 + 11, s1 + 25);|                              |   
     | strcat(s2, "\n");        |                              |
     | printf("%s", s2);        |    facultatea bogata         |
     -----------------------------------------------------------

-------------------------
Unde este eroarea ?
-------------------------
1.              char s[14];
                strcpy(s, "Ce mai faci ?\n");
                
2.              char s[14];
                scanf("%s", &s);
                

-----------------------------------------------
Exercitii propuse spre implementare
-----------------------------------------------
1. Folosind "argc" si "argv" (si eventual optiunea -c) tipariti cu litere 
 majuscule argumentele din "argv".

2. Scrieti o functie proprie "strncmp()" (extrageti din Help definitia si 
 prototipul). 

3. Presupunem ca avem declaratia si initializarea:

        char *p[2][3] = {
          "abc", "defg", "hi", "jklmno", "pqrstuvw", "xyz"
          };
   Completati urmatorul tabel (incercati intai sa nu rulati programul C).
   
    ------------------------------------------------------------------
    |    Expresie             | Expresie echivalenta |  Valoare      |
    ------------------------------------------------------------------
    |      ***p               |         p[0][0][0]   |     'a'       |
    |     **p[1]              |                      |               |
    |  **(p[1] + 2)           |                      |               |
    |*(*(p + 1) + 1)[7]       |                      |  eroare       |
    |(*(*(p + 1) + 1))[7]|    |                      |               |
    |   *(p[1][2] + 2)        |                      |               |
    ------------------------------------------------------------------
    
4. Folosind "scanf()" cititi 7 siruri de caractere, dupa care folosind 
  "strcmp()" sortati-le alfabetic (eventual cu "bubble sort").

5. (*) Scrieti un program similar cu exercitiul 4 care sorteaza si afiseaza 
 argumentele din linia de comanda.