Qualcuno può spiegare perché abbiamo bisogno +1 byte per il valore sentinella? Come sappiamo che 1 char = 1 byte quindi se dichiariamo un array come char a [50] perché non posso memorizzare 50 caratteri invece di 49?
In genere gli utenti C ++ preferiscono utilizzare std::string
laddove possibile. La tua domanda deriva dalla programmazione C di livello inferiore e dai tipi nativi (che ovviamente sono ancora rilevanti in C ++, ma comunque)
Bottom-line, quando passi le stringhe di C in giro stai semplicemente passando l'indirizzo del primo byte della stringa. Molte funzioni di manipolazione delle stringhe comunemente usate hanno qualcosa di simile a questo:
char* c;
for (c = str; *c != 0; ++c) {
// Do something
}
Quando inizi a programmare in C / C ++ potresti essere tentato di dire "So che questa stringa è lunga 50 byte". Ma davvero?
char myStr[50] = "My Name Is Assimilater";
È piuttosto comune allocare più del numero esatto di byte di cui hai bisogno. Non vorrai che printf
copi i caratteri illeggibili dopo "Assimilater" nella console, ora vuoi?
Anche i buffer C-String tendono a essere riutilizzati per memorizzare molte stringhe nel corso della loro vita; questo non è un problema fintanto che il buffer è abbastanza grande da gestire qualsiasi stringa che intendi lanciare.
Questo, unito al fatto che un algoritmo può andare in myStr[51]
e oltre se non viene data una sentinella a cui fermarsi, rende importante la presenza di un valore sentinella.
Hai bisogno di un byte per un valore sentinella, perché quel valore sentinella è codificato come carattere di terminazione
- che richiede semplicemente un byte da memorizzare. abz
Il fatto che venga utilizzato un valore sentinella è solo una decisione progettuale. Non c'è alcuna necessità intrinseca, ci sono altre opzioni .
In C, è semplicemente "convenzione" terminare stringhe come array di caratteri null-terminati . In effetti, c'è una differenza:
Un array di caratteri è solo un array di valori di caratteri. ['a', 'b', 'z'] è un array di caratteri con tre caratteri. Se sai che l'array ha tre caratteri, non è necessario chiuderlo:
char cs[3] = {'a', 'b', 'z'};
for (int i = 0; i < 3; i++)
std::cout << cs[i];
stamperà la stringa char cs[3] = {'a', 'b', 'z'}
.
Puoi pensare a una stringa come una singola entità rappresentata da un array di caratteri. Cioè, puoi anche avere array di caratteri che non sono stringhe, come se tu potessi avere array di interi che non vuoi stampare. Tuttavia, si desidera sempre che una stringa sia rappresentata come matrice di caratteri. (In breve: le stringhe sono sempre array di caratteri, bot non ogni array di caratteri è una stringa in C.)
Non è possibile conoscere in anticipo la lunghezza di una stringa. Pertanto, tutte le funzioni della libreria (?) Possono gestire stringhe di lunghezza arbitraria. Ora, come rappresentare una tale stringa? Sappiamo che possiamo rappresentare una stringa a lunghezza fissa (come "abz") come una matrice a lunghezza fissa (come %code% ).
Quindi è stato deciso che una stringa con n è una matrice di caratteri che contiene almeno ( n +1) caratteri, dove il primo carattere n è il contenuto effettivo e il carattere ( n + 1) è un carattere di terminazione che segnala "fine della stringa ".
Ci sono diversi motivi per questa decisione - in quanto vi sono anche degli svantaggi. Un vantaggio è che puoi riutilizzare la memoria (l'allocazione di nuova memoria può essere costosa, quindi è possibile allocare una parte più grande della memoria), come sottolineato da Assimilater.
Quando lavori con strutture dati di varia lunghezza, ci sono un paio di soluzioni semplici comuni: tieni traccia della lunghezza o mantieni un valore sentinella.
I valori Sentinel hanno il vantaggio di essere della stessa dimensione dei dati. Pertanto la sentinella può essere facilmente utilizzata nel flusso di dati. Tuttavia, è necessario essere costantemente al corrente del valore sentinella mentre elabori i dati.
Mantenere una lunghezza rende molte operazioni più veloci e più facili (non è necessario esaminare ogni valore), tuttavia, la lunghezza deve vivere al di fuori dei dati. Ci sono un paio di aspetti negativi nel tracciare la lunghezza. Per prima cosa devi avere un posto dove mettere la lunghezza che viaggia con i tuoi dati. (Vedi std::string
). Il tipo di lunghezza determina anche la lunghezza massima dei tuoi dati; è un int
abbastanza grande? a long
? Le sentinelle non hanno questa limitazione.
Anche se si dispone di una struttura di dati a dimensione fissa, potrebbe essere necessario interagire con funzioni che presuppongono una struttura di dati di dimensioni variabili. La maggior parte delle funzioni standard di manipolazione delle stringhe di libreria sono disponibili in due versioni, basata sentinella e basata sulla lunghezza. (La lunghezza basata è spesso preferita per evitare buffer overflow.)
Naturalmente, il fatto che il compilatore C / C ++ abbia le stringhe sentinel incorporate convince la maggior parte degli sviluppatori a usare solo quella capacità. Ma questo funziona solo per matrici di personaggi. Devi tenere conto di sentinelle, lunghezza, ecc. Per altri tipi di dati.
Naturalmente, questa è tutta una conoscenza utile, ma la risposta corretta è usare contenitori di libreria standard come std::string
, std::vector
e amici. Questi contenitori sono efficienti ed efficaci nella gestione dei dati e della lunghezza con meno preoccupazioni.