Le stringhe C sono sempre nulle o dipendono dalla piattaforma?


13

In questo momento sto lavorando con sistemi embedded e sto cercando di capire come implementare le stringhe su un microprocessore senza sistema operativo. Finora quello che sto facendo è semplicemente usare l'idea di avere dei puntatori a caratteri terminati NULL e trattarli come stringhe in cui il NULL indica la fine. So che questo è abbastanza comune, ma puoi sempre contare su questo per essere il caso?

Il motivo per cui mi chiedo è che stavo pensando di usare un sistema operativo in tempo reale ad un certo punto, e mi piacerebbe riutilizzare il più possibile il mio codice attuale. Quindi, per le varie scelte che ci sono, posso praticamente aspettarmi che le stringhe funzionino allo stesso modo?

Vorrei essere più specifico anche se per il mio caso. Sto implementando un sistema che accetta ed elabora i comandi su una porta seriale. Posso mantenere lo stesso codice di elaborazione dei comandi e quindi aspettarmi che tutti gli oggetti stringa creati su RTOS (che contiene i comandi) vengano tutti terminati NULL? O sarebbe diverso in base al sistema operativo?

Aggiornare

Dopo essere stato consigliato di dare un'occhiata a questa domanda, ho deciso che non risponde esattamente a ciò che sto chiedendo. La stessa domanda è se la lunghezza di una stringa debba sempre essere passata, che è completamente diversa da quella che sto chiedendo, e sebbene alcune delle risposte contenessero informazioni utili in esse, non sono esattamente ciò che sto cercando. Le risposte lì sembravano spiegare perché o perché non terminare una stringa con un carattere nullo. La differenza con quello che sto chiedendo è se posso più o meno aspettarmi che le stringhe innate di diverse piattaforme terminino le proprie stringhe con null, senza dover uscire e provare ogni singola piattaforma là fuori se ciò ha senso.


3
Non uso C da molto tempo, ma non riesco a pensare a un momento in cui mi sono imbattuto in un'implementazione che non utilizzava stringhe con terminazione NULL. Fa parte dello standard C, se ricordo bene (come ho detto, è passato un po 'di tempo ...)
MetalMikester

1
Non sono uno specialista in C, ma per quanto ne so tutte le stringhe in C sono matrici di caratteri, con terminazione nulla. Tuttavia, puoi creare il tuo tipo di stringa, ma dovresti implementare tutte le funzioni di manipolazione delle stringhe da solo.
Machado,


1
@MetalMikester Pensi che queste informazioni possano essere trovate nelle specifiche C standard?
Snoop,

3
@Snoopy Molto probabilmente, sì. Ma in realtà, quando si parla di stringhe in C, sono solo una serie di caratteri che terminano con NULL e basta, a meno che non si usi una sorta di libreria di stringhe non standard, ma non è di questo che stiamo parlando qui. Dubito che troverai una piattaforma che non lo rispetti, specialmente con uno dei punti di forza di C è la portabilità.
MetalMikester

Risposte:


42

Le cose che si chiamano "stringhe C" verranno annullate su qualsiasi piattaforma. Ecco come le funzioni standard della libreria C determinano la fine di una stringa.

All'interno del linguaggio C, non c'è nulla che ti impedisca di avere una serie di caratteri che non termina in un null. Tuttavia dovrai usare qualche altro metodo per evitare di scappare dalla fine di una stringa.


4
solo per aggiungere; di solito hai un numero intero da qualche parte per tenere traccia della lunghezza della stringa e poi finisci
Rudolf Olah

8
Caso in questione: lavoro con un programma C che utilizza almeno cinque diversi formati di stringa: array con terminazione nulla char, chararray con la lunghezza codificata nel primo byte (comunemente noto come "stringhe di Pascal"), wchar_tversioni basate su entrambi sopra e charmatrici che combinano entrambi i metodi: lunghezza codificata nel primo byte e un carattere null che termina la stringa.
Segna il

4
@Mark Interfaccia con molti componenti / applicazioni di terze parti o un pasticcio di codice legacy?
Dan Fiddling By Firelight,

2
@DanNeely, tutto quanto sopra. Stringhe Pascal per l'interfacciamento con MacOS classico, stringhe C per uso interno e Windows, stringhe ampie per l'aggiunta del supporto Unicode e stringhe bastard perché qualcuno ha cercato di essere intelligente e creare una stringa che potesse interfacciarsi con MacOS e Windows contemporaneamente.
Segna il

1
@Mark ... e ovviamente nessuno è disposto a spendere soldi per pagare il debito tecnico perché il classico MacOS è morto da tempo, e le stringhe bastarde sono un doppio clusterfrak ogni volta che devono essere toccate. Le mie simpatie.
Dan Fiddling By Firelight,

22

La determinazione del carattere finale spetta al compilatore per valori letterali e all'implementazione della libreria standard per le stringhe in generale. Non è determinato dal sistema operativo.

La convenzione di NULdisdetta risale alla pre-standard C e in oltre 30 anni non posso dire di essermi imbattuta in un ambiente che fa qualsiasi altra cosa. Questo comportamento è stato codificato in C89 e continua a far parte dello standard del linguaggio C (il collegamento è a una bozza di C99):

  • La Sezione 6.4.5 imposta il palcoscenico per NULle stringhe terminate richiedendo che NULvenga aggiunto a valori letterali di stringa.
  • La Sezione 7.1.1 riporta ciò alle funzioni nella libreria standard definendo una stringa come "una sequenza contigua di caratteri terminata dal e includendo il primo carattere null".

Non c'è motivo per cui qualcuno non possa scrivere funzioni che gestiscono stringhe terminate da qualche altro carattere, ma non c'è nemmeno motivo di invertire lo standard stabilito nella maggior parte dei casi a meno che il tuo obiettivo non sia adatto ai programmatori. :-)


2
Una ragione sarebbe quella di evitare di dover trovare la fine della stessa stringa più e più volte.
Paŭlo Ebermann,

@ PaŭloEbermann Right. A scapito di dover passare due valori anziché uno. Il che è un po 'fastidioso se passi semplicemente una stringa come in printf("string: \"%s\"\n", "my cool string"). L'unico modo per aggirare quattro parametri in questo caso (oltre a qualche tipo di byte di terminazione) sarebbe definire una stringa che assomigli std::stringa quella del C ++, che ha i suoi problemi e limiti.
cmaster - ripristina monica il

1
La Sezione 6.4.5 non richiede che una stringa letterale sia terminata con un carattere null. Nota esplicitamente " Una stringa di caratteri letterale non deve necessariamente essere una stringa (vedi 7.1.1), perché un carattere nullo può essere incorporato in esso da una sequenza di escape \ 0 " .
bzeaman,

1
@bzeaman La nota a piè di pagina dice che puoi costruire una stringa letterale che non soddisfa la definizione di stringa di 7.1.1, ma la frase che fa riferimento a essa dice compilatori conformi NUL- li termina a prescindere da cosa: "Nella fase di traduzione 7, un byte o un codice di valore zero viene aggiunto a ciascuna sequenza di caratteri multibyte risultante da una stringa letterale o letterale. " Le funzioni di libreria che utilizzano la definizione di 7.1.1 si fermano al primo NULrilevamento e non sanno né si preoccupano dell'esistenza di caratteri aggiuntivi al di là di esso.
Blrfl,

Sono corretto. Ho cercato vari termini come "null" ma ho perso 6.4.5.5 menzionando il "valore zero".
bzeaman,

3

Sto lavorando con sistemi embedded ... senza sistema operativo ... Sto ... usando l'idea di avere dei puntatori a caratteri terminati NULL e trattandoli come stringhe in cui NULL indica la fine. So che questo è abbastanza comune, ma puoi sempre contare su questo per essere il caso?

Non esiste un tipo di dati stringa nel linguaggio C, ma esistono valori letterali stringa .

Se metti una stringa letterale nel tuo programma, di solito sarà NUL terminata (ma vedi il caso speciale, discusso nei commenti qui sotto). Vale a dire, Se metti "foobar"in un posto dove const char *ci si aspetta un valore, il compilatore emetterà foobar⊘nella sezione const / code / sezione del programma e il valore dell'espressione sarà un puntatore all'indirizzo in cui è stato memorizzato il fcarattere. (Nota: sto usando per indicare il byte NUL.)

L'unico altro senso in cui il linguaggio C ha stringhe è che ha alcune routine di libreria standard che operano su sequenze di caratteri terminate con NUL. Quelle routine di libreria non esisteranno in un ambiente di metallo nudo a meno che tu non le porti da solo.

Sono solo codici --- non diversi dal codice che tu stesso scrivi. Se non li rompi quando li porti, allora faranno quello che fanno sempre (ad esempio, fermati su un NUL.)


2
Ri: "Se metti una stringa letterale nel tuo programma, sarà sempre terminata NUL": Ne sei sicuro? Sono abbastanza sicuro che (ad esempio) char foo[4] = "abcd";sia un modo valido per creare un array di quattro caratteri senza terminazione null.
Ruakh,

2
@ruakh, Oops! è un caso che non ho preso in considerazione. Stavo pensando a una stringa letterale che appare in un posto dove ci si aspetta char const * un'espressione . Ho dimenticato che gli inizializzatori C a volte possono obbedire a regole diverse.
Solomon Slow

@ruakh Il valore letterale della stringa è terminato con NUL. L'array non lo è.
Jamesdlin,

2
@ruakh hai un char[4]. Questa non è una stringa, ma è stata inizializzata da una
Caleth,

2
@Caleth, "inizializzato da uno" non è qualcosa che deve accadere in fase di esecuzione. Se aggiungiamo la parola chiave staticall'esempio di Ruakh, il compilatore potrebbe emettere un "abcd" non terminato con NUL su un segmento di dati inizializzato in modo che la variabile venga inizializzata dal programma di caricamento del programma. Quindi, Ruakh aveva ragione: c'è almeno un caso in cui l'aspetto di una stringa letterale in un programma non richiede che il compilatore emetta una stringa terminata con NUL. (ps, in realtà ho compilato l'esempio con gcc 5.4.0, e il compilatore non ha emesso il NUL.)
Solomon Slow

2

Come altri hanno già detto, la terminazione nulla delle stringhe è una convenzione della libreria standard C. È possibile gestire le stringhe nel modo desiderato se non si intende utilizzare la libreria standard.

Questo vale per qualsiasi sistema operativo con un compilatore 'C' e, inoltre, è possibile scrivere programmi 'C' che non sono eseguiti con un vero sistema operativo come menzionato nella domanda. Un esempio potrebbe essere il controller per una stampante a getto d'inchiostro che ho progettato una volta. Nei sistemi integrati, l'overhead di memoria di un sistema operativo potrebbe non essere necessario.

In situazioni a corto di memoria, osserverei le caratteristiche del mio compilatore rispetto al set di istruzioni del processore, ad esempio. In un'applicazione in cui le stringhe vengono elaborate molto, potrebbe essere preferibile utilizzare descrittori come la lunghezza della stringa. Sto pensando a un caso in cui la CPU è particolarmente efficiente nel lavorare con offset brevi e / o offset relativi con i registri degli indirizzi.

Quindi, qual è più importante nella tua applicazione: dimensioni ed efficienza del codice o compatibilità con un sistema operativo o una libreria? Un'altra considerazione potrebbe essere la manutenibilità. Più ti allontani dalla convenzione, più difficile sarà per qualcun altro mantenere.


1

Altri hanno affrontato il problema che in C le stringhe sono in gran parte ciò che ne fai. Ma sembra esserci un po 'di confusione nella tua domanda contro lo stesso terminatore, e da una prospettiva, questo potrebbe essere ciò di cui qualcuno nella tua posizione è preoccupato.

Le stringhe C hanno una terminazione nulla. Cioè, vengono terminati dal carattere nullo, NUL. Non vengono terminati dal puntatore null NULL, che è un tipo di valore completamente diverso con uno scopo completamente diverso.

NULè garantito per avere il valore intero zero. All'interno della stringa, avrà anche la dimensione del tipo di carattere sottostante, che di solito sarà 1.

NULLnon è garantito che abbia un tipo intero. NULLè inteso per l'uso in un contesto di puntatore e generalmente dovrebbe avere un tipo di puntatore, che non dovrebbe essere convertito in un carattere o intero se il compilatore è utile. Mentre la definizione di NULLcoinvolge il glifo 0, non è garantito che abbia effettivamente quel valore [1], e a meno che il tuo compilatore non implementi la costante come un carattere singolo #define(molti non lo fanno, perché in NULL realtà non dovrebbe essere significativo in un non- contesto del puntatore), pertanto non è garantito che il codice espanso coinvolga effettivamente un valore zero (anche se coinvolge in modo confuso un glifo zero).

Se NULLviene digitato, sarà anche improbabile che abbia una dimensione di 1 (o un'altra dimensione del carattere). Ciò può presumibilmente causare ulteriori problemi, sebbene le costanti di caratteri effettive non abbiano dimensioni dei caratteri per la maggior parte.

Ora la maggior parte delle persone vedrà questo e penserà "puntatore nullo come qualcosa di diverso da tutti i bit zero? Che assurdità" - ma ipotesi del genere sono sicure solo su piattaforme comuni come x86. Dato che hai esplicitamente menzionato un interesse nel prendere di mira altre piattaforme, devi prendere in considerazione questo problema, poiché hai esplicitamente separato il tuo codice dalle ipotesi sulla natura della relazione tra puntatori e numeri interi.

Pertanto, mentre le stringhe C hanno terminazione nulla, non sono terminate da NULL, ma da NUL(di solito scritte '\0'). Il codice che utilizza esplicitamente NULLcome terminatore di stringa funzionerà su piattaforme con una struttura di indirizzi semplice e verrà persino compilato con molti compilatori, ma non è assolutamente corretto C.


[1] il valore attuale del puntatore nullo viene inserito dal compilatore quando legge un 0 token in un contesto in cui verrebbe convertito in un tipo di puntatore. Non si tratta di una conversione dal valore intero 0 e non è garantito il mantenimento se 0viene utilizzato qualcosa di diverso dal token stesso, ad esempio un valore dinamico da una variabile; anche la conversione non è reversibile e un puntatore null non deve restituire il valore 0 quando viene convertito in un numero intero.


Ottimo punto Ho inviato una modifica per chiarire.
Monty Harder,

" NULè garantito per avere il valore intero zero." -> C non definisce NUL. Invece C definisce che le stringhe hanno un carattere null finale , un byte con tutti i bit impostati su 0.
chux - Ripristina Monica il

1

Ho usato la stringa in C, significa che i caratteri con terminazione nulla si chiamano Stringhe.

Non avrà alcun problema quando si utilizza in baremetal o in qualsiasi sistema operativo come Windows, Linux, RTOS: (FreeRTO, OSE).

Nel mondo incorporato, la terminazione nulla in realtà aiuta di più a contrassegnare il carattere come stringa.

Ho usato stringhe in C come quella in molti sistemi critici per la sicurezza.

Ti starai chiedendo, che cos'è effettivamente la stringa in C?

Stringhe di tipo C, che sono array, esistono anche valori letterali di stringa, come "questo". In realtà, entrambi questi tipi di stringhe sono semplicemente raccolte di personaggi seduti uno accanto all'altro in memoria.

Ogni volta che scrivi una stringa, racchiusa tra virgolette, C crea automaticamente una matrice di caratteri per noi, contenente quella stringa, terminata dal carattere \ 0.

Ad esempio, è possibile dichiarare e definire un array di caratteri e inizializzarlo con una costante di stringa:

char string[] = "Hello cruel world!";

Risposta semplice: non devi preoccuparti dell'uso di caratteri con terminazione nulla, questo lavoro indipendente da qualsiasi piattaforma.


Grazie, non sapevo che quando dichiarato con virgolette doppie, a NULviene automaticamente aggiunto.
Snoop,

1

Come altri hanno già detto, la terminazione nulla è praticamente universale per lo standard C. Ma (come altri hanno anche sottolineato) non al 100%. Per (un altro) esempio, il sistema operativo VMS in genere utilizzava ciò che chiamava "descrittori di stringhe" http://h41379.www4.hpe.com/commercial/c/docs/5492p012.html a cui accedeva in C da #include <descrip.h >

Le cose a livello di applicazione possono usare la terminazione nulla o meno, comunque lo sviluppatore lo ritenga opportuno. Ma le cose VMS di basso livello richiedono assolutamente descrittori, che non usano affatto la terminazione nulla (vedi link sopra per i dettagli). Questo è in gran parte in modo che tutte le lingue (C, assembly, ecc.) Che utilizzano direttamente gli interni VMS possano avere un'interfaccia comune con loro.

Quindi, se stai anticipando qualsiasi tipo di situazione simile, potresti voler essere un po 'più attento di quanto potrebbe suggerire la "terminazione nulla universale". Starei più attento se stavo facendo quello che stai facendo, ma per la mia roba a livello di applicazione è sicuro presumere la nullità. Non ti consiglierei lo stesso livello di sicurezza. Il tuo codice potrebbe dover interfacciarsi con assembly e / o altro codice di lingua in un momento futuro, che potrebbe non essere sempre conforme allo standard C delle stringhe con terminazione null.


Oggi, la terminazione 0 è in realtà abbastanza insolita. C ++ std :: string no, Java String no, Objective-C NSString no, Swift String no - di conseguenza, ogni libreria di lingue supporta stringhe con codici NUL all'interno della stringa (cosa impossibile con C stringhe per ovvi motivi).
gnasher729,

@ gnasher729 Ho cambiato "... praticamente universale" in "praticamente universale per lo standard C", che spero rimuova ogni ambiguità e rimanga corretto oggi (e che è ciò che intendevo dire, per argomento e domanda del PO).
John Forkosh,

0

Nella mia esperienza con sistemi embedded, critici per la sicurezza e in tempo reale non è raro usare le convenzioni di stringa C e PASCAL, ovvero fornire la lunghezza delle stringhe come primo carattere (che limita la lunghezza a 255) e terminare la stringa con almeno uno 0x00, ( NUL), che riduce la dimensione utilizzabile a 254.

Uno dei motivi è sapere quanti dati ci si aspetta dopo la ricezione del primo byte e un altro è che, in tali sistemi, le dimensioni del buffer dinamico sono evitate ove possibile - l'allocazione di una dimensione fissa del buffer 256 è più veloce e più sicura (no è necessario verificare se mallocnon è riuscito). Un altro è che gli altri sistemi con cui stai comunicando potrebbero non essere scritti in ANSI-C.

In qualsiasi lavoro incorporato è importante stabilire e mantenere un documento di controllo dell'interfaccia (IDC), che definisce tutte le strutture di comunicazione tra cui formati di stringa, endianness, dimensioni di interi, ecc., Il più presto possibile ( idealmente prima di iniziare ), e dovrebbe essere la vostra, e tutte le squadre, libro sacro quando si scrive il sistema - se i desideri di qualcuno che introducono un nuovo formato o struttura che deve essere documentato lì prima e tutti quelli che potrebbero essere influenzati informati, possibilmente con la possibilità di veto il cambiamento .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.