Le stringhe C sono sempre nulle, o dipendono dalla piattaforma?

11

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

La ragione per cui lo chiedo è che stavo pensando di utilizzare un sistema operativo in tempo reale a un certo punto e mi piacerebbe riutilizzare il più possibile il mio codice attuale. Quindi, per le varie opzioni disponibili, posso aspettarmi che le stringhe funzionino allo stesso modo?

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

Aggiorna

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

    
posta Snoop 21.03.2017 - 14:18
fonte

8 risposte

44

Le cose che sono chiamate "stringhe C" saranno terminate con null 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 matrice di caratteri che non termina in null. Comunque dovrai usare qualche altro metodo per evitare di finire alla fine di una stringa.

    
risposta data 21.03.2017 - 14:39
fonte
20

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

La convenzione della terminazione NUL risale al C pre-standard, e in 30+ anni, non posso dire di essere incappato 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 lo stage per NUL -terminated stringhe richiedendo che un NUL venga aggiunto a stringhe letterali.
  • La Sezione 7.1.1 porta ciò alle funzioni nella libreria standard definendo una stringa come "una sequenza contigua di caratteri terminata da e includendo il primo carattere null."

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

    
risposta data 21.03.2017 - 16:42
fonte
2

Come altri hanno già detto, la terminazione null delle stringhe è una convenzione della libreria standard C. Puoi gestire le stringhe come preferisci se non utilizzi la libreria standard.

Questo è vero per qualsiasi sistema operativo con un compilatore "C" e, inoltre, puoi scrivere programmi "C" che non vengono eseguiti con un vero sistema operativo come menzionato nella tua domanda. Un esempio potrebbe essere il controller per una stampante a getto d'inchiostro che ho progettato una volta. Nei sistemi embedded, il sovraccarico della memoria di un sistema operativo potrebbe non essere necessario.

In situazioni di memoria, guarderei le caratteristiche del mio compilatore di fronte al set di istruzioni del processore, per esempio. In un'applicazione in cui le stringhe vengono elaborate molto, potrebbe essere opportuno 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 registri di indirizzi.

Quindi, che è più importante nella tua applicazione: dimensioni del codice ed efficienza, 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 da mantenere.

    
risposta data 21.03.2017 - 20:52
fonte
2

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

Le stringhe C sono terminate da null. Cioè, sono terminati dal carattere nullo, NUL . Non vengono terminati dal puntatore nullo 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.

NULL non è garantito che abbia un intero tipo. NULL è inteso per l'uso in un contesto di puntatore e generalmente si prevede che abbia un tipo di puntatore, che non dovrebbe convertirsi in un carattere o in un numero intero se il compilatore è valido. Mentre la definizione di NULL riguarda il glifo 0 , non è garantito che abbia effettivamente quel valore [1], e a meno che il compilatore non implementi la costante come% co_de di un carattere% (molti non lo fanno, perché #define veramente non dovrebbe essere significativo in un contesto non puntatore), quindi non è garantito che il codice espanso implichi effettivamente un valore zero (anche se confonde implicitamente un glifo zero).

Se si digita NULL , è improbabile che abbia una dimensione pari a 1 (o un'altra dimensione di carattere). Ciò potrebbe in teoria causare ulteriori problemi, anche se le costanti di carattere reali non hanno la dimensione del carattere per la maggior parte.

Ora la maggior parte delle persone vedrà questo e pensa, "puntatore nullo come qualcosa di diverso da tutto-zero-bit? che assurdità" - ma ipotesi del genere sono sicure solo su piattaforme comuni come x86. Poiché hai esplicitamente menzionato un interesse a indirizzare altre piattaforme, devi tenere conto di questo problema, poiché hai esplicitamente separato il tuo codice dalle ipotesi sulla natura della relazione tra i puntatori e gli interi.

Pertanto, mentre le stringhe C sono terminate da null, non vengono terminate da NULL , ma da NULL (di solito scritto NUL ). Il codice che utilizza in modo esplicito 'NULL' come un terminatore di stringhe funzionerà su piattaforme con una struttura di indirizzi semplice e verrà anche compilato con molti compilatori, ma non è assolutamente corretto C.

[1] il vero valore del puntatore nullo viene inserito dal compilatore quando legge un 0 token in un contesto in cui verrebbe convertito in un tipo di puntatore. Questa non è una conversione dal valore intero valore 0, e non è garantita se si utilizza qualcosa di diverso dal token 0 stesso, come un valore dinamico da una variabile; anche la conversione non è reversibile e un puntatore nullo non deve fornire il valore 0 quando viene convertito in un numero intero.

    
risposta data 21.03.2017 - 21:27
fonte
2

I am working with embedded systems ... with no operating system...I am...using the idea of having NULL terminated character pointers and treating them as strings where the NULL signifies the end. I know that this is fairly common, but can you always count on this to be the case?

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

Se inserisci una stringa letterale nel tuo programma, di solito sarà NUL terminato (ma vedi il caso speciale, discusso nei commenti sotto). Vale a dire, Se metti "foobar" in un posto dove% co_de Il valore% è previsto, il compilatore emetterà const char * al segmento / codice const / codice del programma e il valore dell'espressione sarà un puntatore all'indirizzo in cui è stato memorizzato il carattere foobar⊘ . (Nota: sto usando f 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 NUL. Quelle routine di libreria non esisteranno in un ambiente bare metal a meno che non le porti da solo.

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

    
risposta data 21.03.2017 - 21:30
fonte
1

Ho usato la stringa in C, significa che i caratteri con terminazione null sono chiamati stringhe.

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

Nel mondo embedded la terminazione nulla in realtà aiuta di più a simbolizzare il carattere come stringa.

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

Forse ti starai chiedendo, cos'è effettivamente la stringa in C?

Stringhe in stile C, che sono matrici, esistono anche stringhe letterali, come "questo". In realtà, entrambi questi tipi di stringhe sono semplicemente raccolte di caratteri seduti l'uno accanto all'altro in memoria.

Whenever you write a string, enclosed in double quotes, C automatically creates an array of characters for us, containing that string, terminated by the %bl0ck_qu0te% character.

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

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

Risposta semplice: non hai davvero bisogno di preoccuparti per l'utilizzo di caratteri con terminazione nulla, questo funziona indipendentemente da qualsiasi piattaforma.

    
risposta data 21.03.2017 - 22:08
fonte
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 usava tipicamente quello che chiamava "descrittore di stringhe" link accessibile in C da #include < descrip.h >

Gli elementi a livello di applicazione possono utilizzare la terminazione nullo o no, tuttavia lo sviluppatore ritiene opportuno. Ma roba VMS di basso livello richiede assolutamente descrittori, che non utilizzano affatto la terminazione nulla (vedi il collegamento sopra per i dettagli). Questo è in gran parte il modo in cui tutte le lingue (C, assembly, ecc.) Che utilizzano direttamente gli interni di VMS possono avere un'interfaccia comune con loro.

Quindi, se stai anticipando qualsiasi tipo di situazione simile, potresti voler essere un po 'più cauto di quanto "la terminazione nulla universale" possa suggerire sia necessaria. Sarei più attento se facessi quello che stai facendo, ma per il mio materiale a livello di applicazione è sicuro assumere la terminazione nulla. Non ti suggerirei lo stesso livello di sicurezza per te. Il tuo codice potrebbe essere necessario interfacciarsi con assembly e / o altro codice della lingua in futuro, il che potrebbe non essere sempre conforme allo standard C delle stringhe con terminazione nulla.

    
risposta data 22.03.2017 - 04:38
fonte
0

Nella mia esperienza di sistemi embedded, safety critical e real time non è raro usare entrambe le convenzioni di stringa C e PASCAL, cioè fornire la lunghezza delle stringhe come primo carattere (che limita la lunghezza a 255), e per terminare la stringa con almeno un 0x00, ( NUL ), che riduce la dimensione utilizzabile a 254.

Una ragione per questo è sapere quanti dati ci si aspetta dopo che il primo byte è stato ricevuto e un altro è che, in tali sistemi, le dimensioni del buffer dinamico sono evitate laddove possibile - allocare una dimensione del buffer 256 fissa è più veloce e più sicuro , (non è necessario controllare se malloc non è riuscito). Un altro è che gli altri sistemi con cui stai comunicando potrebbero non essere scritti in ANSI-C.

In qualsiasi lavoro integrato è importante stabilire e mantenere un documento di controllo dell'interfaccia (IDC) che definisca tutte le strutture di comunicazione, inclusi formati di stringhe, endianness, dimensioni dei numeri interi, ecc., il prima possibile, ( idealmente prima di iniziare ), e dovrebbe essere il tuo e tutti i team, libro sacro quando scrivi il sistema - se qualcuno desidera introdurre una nuova struttura o formattarlo deve essere documentato lì prima e tutti quelli che potrebbero essere interessati, possibilmente con un'opzione per porre il veto alla modifica

    
risposta data 22.03.2017 - 08:15
fonte

Leggi altre domande sui tag