2.4 Le stringhe di caratteri (C e C++) — Versione approfondita

Obiettivi di apprendimento

  • Capire la differenza tra char[] (C) e std::string (C++).
  • Gestire input con spazi in modo sicuro (cin vs getline, ignore(), ws).
  • Applicare i metodi chiave: size(), substr(), find(), replace(), erase(), at().
  • Convertire tra C-string e std::string, usare stoi/to_string, evitare errori tipici.

Stringhe in C (array di char)

In C una stringa è un array di caratteri terminato dal carattere speciale '\0' (terminatore). Senza di esso le funzioni C non sanno dove la stringa termina.

char ok[6]  = {'r','o','s','s','o','\0'};  // CORRETTA
char err[5] = {'r','o','s','s','o'};       // NO: manca '\0'
char s[]    = "rosso";                     // il compilatore aggiunge '\0' (dimensione 6)
Dimostrazione con sizeof e stampa caratteri
#include <iostream>
using namespace std;

int main(){
  char colore[] = "rosso";
  cout << "byte: " << sizeof(colore) << '\n';
  for(size_t i=0;i<sizeof(colore);++i)
    cout << "colore[" << i << "] = " << colore[i] << '\n';
}
Overflow & input C-style. Funzioni come gets/strcpy/strcat non controllano la capienza del buffer. Evita gets; se ti servono versioni C usa fgets/strncpy/strncat con dimensioni corrette.

Input sicuro in C++: cin vs getline

cin >> s si ferma al primo spazio. Per leggere frasi utilizza getline(cin, s). Se li combini, “pulisci” lo stream dal newline residuo con cin.ignore() (o cin >> ws).

#include <iostream>
#include <string>
#include <limits>              // per numeric_limits
using namespace std;

string nome, indirizzo;
cin >> nome; // legge solo la prima parola
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // pulisci newline
getline(cin, indirizzo); // ora legge l'intera riga con spazi
Esempio completo con getline
#include <iostream>
#include <string>
using namespace std;

int main(){
  string intestazione = "Riga di intestazione";
  string nome, indirizzo;

  cout << intestazione << '\n';
  cout << "Nome e cognome: "; getline(cin, nome);
  cout << "Indirizzo: ";       getline(cin, indirizzo);

  cout << nome + ", " + indirizzo << '\n';
}

std::string (C++) — costruttori, accesso, capacità, modifiche

Perché std::string? Gestisce memoria e terminatore automaticamente, offre operatori e metodi per confrontare, cercare, inserire, sostituire e cancellare testo in modo sicuro.
Costruttori utili
string a;                        // vuota
string b = "testo";              // da literal
string c("testo");               // equivalente
string d(5, '*');                // "*****"
string e(b, 0, 3);               // "tes"
string f = b.substr(2, 2);       // "st"
Accesso ai caratteri
string s = "Lambda";
char x = s[0];     // 'L' (nessun controllo)
char y = s.at(1);  // 'a' (controllo limiti: può lanciare out_of_range)
s.front() = 'l';   // "lambda"
s.back()  = '!';   // "lambda!"
Capacità & gestione memoria
string s = "ciao";
s.size();           // 4
s.empty();          // false
s.reserve(100);     // pre-alloca (utile con molte append)
s.shrink_to_fit();  // prova a ridurre la capacità
Modificatori principali
string s = "Buona giornata!";
s.append(" :)");
s.push_back('!');
s.insert(6, "bellissima ");
s.replace(0, 5, "Ottima");
s.erase(6, 3);
s.clear();

Sottostringhe e ricerca

string s = "Buona giornata!";
auto p1 = s.find("na");              // 2
auto p2 = s.rfind("na");             // ultima occorrenza
auto p3 = s.find_first_of("aeiou");  // prima vocale
auto p4 = s.find_first_not_of(" ");  // primo non-spazio
if (s.find("xyz") == string::npos) { /* non trovato */ }
Tutte le occorrenze di una sottostringa
string s = "banana bandana";
string sub = "ana";
for (size_t pos = s.find(sub); pos != string::npos; pos = s.find(sub, pos + 1)){
  cout << "Trovata in pos " << pos << '\n';
}

Confronto e conversioni

// confronto
string a="alfa", b="beta";
bool uguali = (a == b);    // false
int cmp = a.compare(b);    // <0 se a<b (lessicografico)

// conversioni numeriche
int n = stoi("123");       // 123
double x = stod("3.14");   // 3.14
string t = to_string(42);  // "42"

// interfaccia C
const char* cstr = a.c_str(); // per funzioni C
Eccezioni comuni: at() può lanciare out_of_range; alcune operazioni possono lanciare length_error se superi la capacità massima.
Unicode/UTF-8: la lunghezza in caratteri può differire dai byte; size() conta i byte. Per elaborazioni Unicode avanzate valuta librerie dedicate.

C (char[]) vs C++ (std::string)

Aspetto char[] (C) std::string (C++)
Terminatore Richiesto '\0' Gestito internamente
Lunghezza Fissa alla dichiarazione Dinamica
Sicurezza Rischio overflow at() controlla i limiti
Operazioni strcpy/strcat/strcmp append/replace/find/compare
Interop C Nativa c_str()const char*

Ricette veloci

Trim spazi iniziali/finali
auto ltrim = [](string &s){ s.erase(0, s.find_first_not_of(' ')); };
auto rtrim = [](string &s){ s.erase(s.find_last_not_of(' ')+1); };
auto trim  = [&](string &s){ ltrim(s); rtrim(s); };
Split su un delimitatore
vector<string> split(const string &s, char sep){
  vector<string> out; string cur;
  for(char ch: s){
    if(ch==sep){ out.push_back(cur); cur.clear(); }
    else cur.push_back(ch);
  }
  out.push_back(cur);
  return out;
}

Esercizi (con soluzioni a scomparsa)

  1. Dato string s="Programmare in C++"; stampa gli indici di tutte le occorrenze di "ra".
    Soluzione
    for(size_t p = s.find("ra"); p != string::npos; p = s.find("ra", p+1))
      cout << p << ' ';
  2. Con string a="alfa"; string b="beta"; usa compare() per dire quale viene prima.
    Soluzione
    int cmp = a.compare(b);
    cout << (cmp<0 ? "alfa prima di beta" : (cmp>0 ? "beta prima di alfa" : "uguali"));
  3. Dalla stringa "Arrivederci" ottieni "veder" senza cicli.
    Soluzione
    string s="Arrivederci";
    string out = s.substr(3,5);
  4. Trasforma "Buona giornata!" in "Ottima giorna?" con due operazioni.
    Soluzione
    string s="Buona giornata!";
    s.replace(0,5,"Ottima");
    s.erase(6,3);
    s.back()='?';
  5. Converte "-120" in intero e poi di nuovo in stringa con suffisso "px".
    Soluzione
    int n = stoi("-120");
    string t = to_string(n) + "px";
Errori comuni (checklist)

  • Dimenticare '\0' nelle char[].
  • Mescolare cin >> e getline senza pulire lo stream (ignore o >> ws).
  • Usare [] fuori range: preferisci at() quando l’indice non è garantito valido.
  • Confondere byte e caratteri con UTF-8.



Mini-quiz • Stringhe (C e C++)

1) In C, che cosa rende un char[] una vera stringa?




Le funzioni C si fermano al primo '\0'.

2) Con string s="Buona giornata!"; il valore di s.find("na") è:




Gli indici partono da 0: “Buona…”.

3) Metodo più sicuro per accedere a un carattere di std::string?




at() lancia out_of_range su indici invalidi.

4) Con string saluto="Arrivederci"; ottieni «ArrivederLa» con:




Sostituisci gli ultimi 2 caratteri (“ci”) con “La”.

5) Con string s=" testo "; quale sequenza rimuove gli spazi iniziali e finali?




Usiamo find_first_not_of/find_last_not_of e erase.


Premi “Verifica” per evidenziare verde/rosso e mostrare le spiegazioni. “Reset” pulisce tutto.