Questo è un vecchio thread e penso che le altre risposte siano fantastiche, ma trascurano qualcosa, quindi ecco i miei (in ritardo) 2 centesimi.
Il rivestimento sintetico di zucchero nasconde la complessità
Il problema con le stringhe è che sono cittadini di seconda classe nella maggior parte delle lingue, e in realtà la maggior parte delle volte non fanno realmente parte delle specifiche della lingua stessa: sono un costrutto implementato in biblioteca con qualche occasionale rivestimento sintattico di zucchero nella parte superiore per renderli meno dolorosi da usare.
La conseguenza diretta di ciò è che il linguaggio nasconde gran parte della loro complessità lontano dalla tua vista e paghi per gli effetti collaterali subdoli perché prendi l'abitudine di considerarli come un'entità atomica di basso livello, proprio come altri tipi primitivi (come spiegato dalla risposta più votata e altri).
Dettagli di implementazione
Good Ol 'Array
Uno degli elementi di questa "complessità" sottostante è che la maggior parte delle implementazioni di stringhe ricorrerebbe all'uso di una semplice struttura di dati con uno spazio di memoria contiguo per rappresentare la stringa: il tuo buon vecchio array.
Questo ha senso, intendiamoci, poiché vuoi che l'accesso alla stringa nel suo insieme sia veloce. Ciò implica costi potenzialmente terribili quando si desidera manipolare questa stringa. Accedere a un elemento nel mezzo potrebbe essere veloce se sai quale indice stai cercando , ma cercare un elemento basato su una condizione non lo è.
Anche restituire la dimensione della stringa potrebbe essere costoso, se la tua lingua non memorizza nella cache la lunghezza della stringa e deve attraversarla per contare i caratteri.
Per motivi analoghi, l' aggiunta di elementi alla stringa risulterà costosa poiché molto probabilmente dovrai riassegnare un po 'di memoria perché l'operazione si verifichi.
Quindi, lingue diverse adottano approcci diversi a questi problemi. Java, ad esempio, si è preso la libertà di rendere immutabili le sue stringhe per alcuni motivi validi (lunghezza della cache, sicurezza dei thread) e per le sue controparti mutabili (StringBuffer e StringBuilder) sceglierà di allocare le dimensioni usando blocchi di dimensioni maggiori per non dover allocare ogni volta, ma piuttosto spero per gli scenari migliori. In genere funziona bene, ma il lato negativo è a volte pagare per gli impatti della memoria.
Supporto Unicode
Inoltre, e questo è dovuto al fatto che il rivestimento sintattico di zucchero della tua lingua ti nasconde questo per giocare bene, spesso non pensi che termini di supporto Unicode (specialmente finché non ne hai davvero bisogno e colpire quel muro). E alcune lingue, essendo lungimiranti, non implementano stringhe con matrici sottostanti di semplici primitivi char a 8 bit. Hanno funzionato in UTF-8 o UTF-16 o supporto what-have-you per te e la conseguenza è un consumo di memoria tremendamente maggiore, che spesso non è necessario, e un tempo di elaborazione maggiore per allocare memoria, elaborare le stringhe, e implementare tutta la logica che va di pari passo con la manipolazione dei punti di codice.
Il risultato di tutto ciò è che quando fai qualcosa di equivalente in pseudo-codice a:
hello = "hello,"
world = " world!"
str = hello + world
Potrebbe non essere - nonostante tutti i migliori sforzi che gli sviluppatori del linguaggio hanno fatto per farli comportare come avresti fatto - semplice come:
a = 1;
b = 2;
shouldBeThree = a + b
Come follow-up, potresti voler leggere: