Domanda Risolto Bug/Incomprensione sulle stringhe

Stato
Discussione chiusa ad ulteriori risposte.

DanyDollaro

Utente Electrum
14 Luglio 2018
148
41
58
138
Salve a tutti, mi sono accorto di questo problema da poco e non ho ben capito se è un bug o una mia incompresione delle stringhe, ho scritto questo piccolo sketch di codice che lo mostra:
C++:
#include <iostream>
#include <string>

int main()
{
    std::string a;

    std::cout << "Cin String: ";
    getline(std::cin, a);

    for (int i = 0, ii = 0; i <= a.length(); i++)
    {
        if (a[i] != ' ')
        {
            a[ii] = a[i];
            ii++;
        }
    }

    std::cout << "Output: " << a;

    std::cout << "\nString length: " << a.length();
    getchar();
}
Quello che dovrebbe fare è: ricevendo una stringa in input manda come output la stessa stringa però con tutti i caratteri spazio rimossi.

Quindi mandando come input la stringa "a b c" dovrebbe mandare in output la stringa "abc" ma non è così, quello che ricevo è "abc c" eppure esaminando la regione di memoria su cui è allocata la stringa (Dopo essere uscito dal ciclo for) è 61 62 63 00 63 - 'a' 'b' 'c' '\0' 'c' (di consequenza l`operatore cout dovrebbe fermarsi al quarto carattere '\0') e non capisco il motivo per cui la funzione length() dia come risultato 5 non ostante il carattere '\0' si trovi dopo 3 caratteri
 
Stai sovrascrivendo la stringa iniziale ad ogni iterazione volendo modificare la lunghezza senza però andare ad intervenire su tutti i caratteri.

"a b c" è di lunghezza 5
"abc" è di lunghezza 3.

Nel tuo ciclo ignori gli spazi e quindi sostituirà soltanto 3 occorrenze lasciando inalterate le posizioni 3 e 4.
 
Le stringe in C terminano per '\0'. Le stringhe in C++ invece no, ma hanno un metodo (std::string::c_str) per convertirle in cstring. In particolare, la non-necessità del terminatore '\0' è un ottimizzazione che ti permette di fare come come manipolare le sotto-stringhe senza fare riallocazioni e avere maggior controllo sugli attacchi di buffer overflow.

Quello che vuoi fare tu si fa in questo modo:
C++:
#include <iostream>
#include <algorithm>
#include <string>

std::string a = "a b c";
a.erase(std::remove(std::begin(a), std::end(a), ' '), std::end(a));
std::cout << a << std::endl;

Un'alternativa più didattica potrebbe essere questa:
C++:
for (int i = 0; i < a.length();) {
  if (a[i] == ' ')
    a.erase(std::begin(a) + i);  // do not increment
  else
    i++;
}

Con il primo codice che ho postato, sbatti gli elelemnti che non vuoi in fondo all'array (remove) e poi ridimensioni la stringa (erase). Nella seconda versione invece ci sono tanti ridimensionamenti quanti gli elementi da eliminare, quindi potenzialmente (implementation defined) ci sono tante riallocazioni di memoria. Ovviamente il primo codice è molto più efficiente (oltre che molto più corto, visto che sta su una riga).

Se invece vuoi semplicemente far funzionare il tuo codice ti basta scrivere:
C++:
std::cout << "Output: " << a.c_str();
...ma non è il modo corretto di procedere.
 
  • Mi piace
Reazioni: DanyDollaro
Stato
Discussione chiusa ad ulteriori risposte.