Șiruri de caractere (char[]) în C

În C, un șir de caractere este un tablou de char terminat cu caracterul nul '\0', care marchează sfârșitul textului.

Sintaxă
char s[] = "text";          // tablou modificabil, cu '\0' la final
char s[20];                  // loc pentru 19 caractere + '\0'
const char *p = "text";      // pointer la un literal (doar citire)

Ce face Șiruri de caractere (char[])

C nu are un tip special pentru text. Un șir este un tablou de char în care, după ultimul caracter, stă octetul '\0' (valoarea 0). Toate funcțiile din string.h și printf("%s") citesc până la acest terminator.

char s[] = "Ana"; creează un tablou de 4 octeți: 'A', 'n', 'a', '\0'. Tabloul poate fi modificat. În schimb, const char *p = "Ana"; doar arată spre un literal din memoria programului, care nu are voie să fie modificat.

Un tablou de char trebuie să aibă loc și pentru '\0'. Literele cu diacritice (ș, ț, ă) ocupă câte 2 octeți în UTF-8, așa că „Iași” are 5 octeți plus terminatorul.

Exemple

Exemplul 1: Ce conține de fapt un șir

sizeof numără și terminatorul, strlen nu. Bucla se oprește când întâlnește '\0'.

#include <stdio.h>
#include <string.h>

int main(void) {
    char nume[] = "Ana";
    printf("sizeof = %zu, strlen = %zu\n", sizeof nume, strlen(nume));
    for (size_t i = 0; nume[i] != '\0'; i++) {
        printf("nume[%zu] = '%c' (%d)\n", i, nume[i], nume[i]);
    }
    printf("terminator: %d\n", nume[3]);
    return 0;
}

Rezultat

sizeof = 4, strlen = 3
nume[0] = 'A' (65)
nume[1] = 'n' (110)
nume[2] = 'a' (97)
terminator: 0

Exemplul 2: Un tablou se poate modifica

Schimbarea unui caracter merge pe tablou. Pe un literal accesat prin const char * compilatorul nu te lasă să scrii.

#include <stdio.h>

int main(void) {
    char oras[] = "Iasi";
    oras[0] = 'i';
    printf("%s\n", oras);

    const char *capitala = "Bucuresti";
    // capitala[0] = 'b';  // eroare: capitala arată spre date const
    printf("%s are prima litera %c\n", capitala, capitala[0]);
    return 0;
}

Rezultat

iasi
Bucuresti are prima litera B

Exemplul 3: Diacriticele ocupă mai mulți octeți

În UTF-8, „ș” ocupă 2 octeți, deci „Iași” are 5 octeți, nu 4. strlen numără octeți, nu litere.

#include <stdio.h>
#include <string.h>

int main(void) {
    const char *oras = "Iași";
    printf("%s: %zu octeți\n", oras, strlen(oras));
    char buffer[5];
    printf("încape în buffer[5]? %s\n", strlen(oras) < sizeof buffer ? "da" : "nu");
    return 0;
}

Rezultat

Iași: 5 octeți
încape în buffer[5]? nu

Greșeli frecvente

  • Declari char s[3] = "Ana";. În C este permis, dar tabloul nu mai are loc pentru '\0', iar printf("%s") citește mai departe în memorie. Lasă dimensiunea goală sau pune cu unu mai mult.
  • Compari șiruri cu ==. Așa compari adresele, nu textul; folosește strcmp.
  • Atribui un șir cu s = "Ion"; unui tablou deja declarat. Tablourile nu se atribuie; folosește strcpy sau snprintf.
  • Modifici un literal prin char *p = "Ana"; p[0] = 'a';. Este comportament nedefinit (de obicei programul se oprește). Declară pointerul const char *.

Comenzi înrudite

  • Tablouri (array): Un șir de elemente de același tip, așezate unul după altul în memorie și accesate prin index, de la 0.
  • strlen: Numără caracterele unui șir C până la terminatorul ‘\0’, fără să-l includă.
  • strcpy, strncpy și strdup: Copiază un șir C într-un alt tablou (strcpy, strncpy) sau într-o zonă nouă alocată cu malloc (strdup).
  • strcmp și strncmp: Compară două șiruri caracter cu caracter și spune dacă sunt egale sau care vine primul.
  • printf: Afișează text formatat pe ecran: un șablon cu specificatori (%d, %s, %.2f) completați cu valorile primite după el.
  • fgets: Citește un rând întreg (cu tot cu spații) într-un tablou de caractere, fără să depășească dimensiunea dată.