Sta inizializzando un char [] con una cattiva pratica letterale stringa?

42

Stavo leggendo un thread intitolato "strlen vs sizeof" su CodeGuru , e una delle risposte afferma che "è comunque [sic] una cattiva pratica per initialie [sic] una matrice char con una stringa letterale. "

È vero, o è solo la sua opinione (anche se un "membro elitario")?

Ecco la domanda originale:

#include <stdio.h>
#include<string.h>
main()
{
    char string[] = "october";
    strcpy(string, "september");

    printf("the size of %s is %d and the length is %d\n\n", string, sizeof(string), strlen(string));
    return 0;
}

right. the size should be the length plus 1 yes?

this is the output

the size of september is 8 and the length is 9

size should be 10 surely. its like its calculating the sizeof string before it is changed by strcpy but the length after.

Is there something wrong with my syntax or what?

Ecco la risposta :

It's anyways bad practice to initialie a char array with a string literal. So always do one of the following:

const char string1[] = "october";
char string2[20]; strcpy(string2, "september");
    
posta Cole Johnson 16.01.2013 - 17:25
fonte

6 risposte

57

It's anyways bad practice to initialie a char array with a string literal.

L'autore di quel commento non lo giustifica davvero, e trovo la dichiarazione sconcertante.

In C (e lo hai etichettato come C), questo è praticamente l'unico modo per inizializzare un array di char con un valore stringa (l'inizializzazione è diversa dall'assegnazione). Puoi scrivere

char string[] = "october";

o

char string[8] = "october";

o

char string[MAX_MONTH_LENGTH] = "october";

Nel primo caso, la dimensione dell'array è presa dalla dimensione dell'inizializzatore. I valori letterali stringa sono memorizzati come matrici di char con un byte 0 di fine, quindi la dimensione dell'array è 8 ('o', 'c', 't', 'o', 'b', 'e', ' r ', 0). Nei secondi due casi, la dimensione dell'array viene specificata come parte della dichiarazione (8 e MAX_MONTH_LENGTH , qualunque cosa accada).

Ciò che non puoi fare è scrivere qualcosa come

char string[];
string = "october";

o

char string[8];
string = "october";

ecc. Nel primo caso, la dichiarazione di string è incompleta perché non è stata specificata alcuna dimensione di matrice e non c'è alcun inizializzatore da cui prendere la dimensione. In entrambi i casi, = non funzionerà perché a) un'espressione di matrice come string potrebbe non essere la destinazione di un'assegnazione eb) l'operatore = non è definito per copiare il contenuto di una matrice ad un altro comunque.

Con lo stesso token, non puoi scrivere

char string[] = foo;

dove foo è un'altra matrice di char . Questa forma di inizializzazione funzionerà solo con stringhe letterali.

Modifica

Dovrei modificare questo per dire che puoi anche inizializzare gli array per contenere una stringa con un inizializzatore stile array, come

char string[] = {'o', 'c', 't', 'o', 'b', 'e', 'r', 0};

o

char string[] = {111, 99, 116, 111, 98, 101, 114, 0}; // assumes ASCII

ma è più facile per gli occhi usare stringhe letterali.

Modifica 2

Per assegnare i contenuti di un array all'esterno di una dichiarazione, devi utilizzare strcpy/strncpy (per stringhe con terminazione 0) o memcpy (per qualsiasi altro tipo di array):

if (sizeof string > strlen("october"))
  strcpy(string, "october");

o

strncpy(string, "october", sizeof string); // only copies as many characters as will
                                           // fit in the target buffer; 0 terminator
                                           // may not be copied, but the buffer is
                                           // uselessly completely zeroed if the
                                           // string is shorter!
    
risposta data 16.01.2013 - 18:28
fonte
10

L'unico problema che ricordo è assegnare letterale stringa a char * :

char var1[] = "september";
var1[0] = 'S'; // Ok - 10 element char array allocated on stack
char const *var2 = "september";
var2[0] = 'S'; // Compile time error - pointer to constant string
char *var3 = "september";
var3[0] = 'S'; // Modifying some memory - which may result in modifying... something or crash

Ad esempio, prendi questo programma:

#include <stdio.h>

int main() {
  char *var1 = "september";
  char *var2 = "september";
  var1[0] = 'S';
  printf("%s\n", var2);
}

Questo sulla mia piattaforma (Linux) si blocca mentre tenta di scrivere sulla pagina contrassegnata come di sola lettura. Su altre piattaforme potrebbe stampare "settembre" ecc.

Detto questo - l'inizializzazione letterale rende l'importo specifico della prenotazione in modo che non funzioni:

char buf[] = "May";
strncpy(buf, "September", sizeof(buf)); // Result "Sep"

Ma questo sarà

char buf[32] = "May";
strncpy(buf, "September", sizeof(buf));

Come ultima osservazione - Non userei affatto strcpy :

char buf[8];
strcpy(buf, "very long string very long string"); // Oops. We overwrite some random memory

Mentre alcuni compilatori possono cambiarlo in chiamate sicure, strncpy è molto più sicuro:

char buf[1024];
strncpy(buf, something_else, sizeof(buf)); // Copies at most sizeof(buf) chars so there is no possibility of buffer overrun. Please note that sizeof(buf) works for arrays but NOT pointers.
buf[sizeof(buf) - 1] = '
char var1[] = "september";
var1[0] = 'S'; // Ok - 10 element char array allocated on stack
char const *var2 = "september";
var2[0] = 'S'; // Compile time error - pointer to constant string
char *var3 = "september";
var3[0] = 'S'; // Modifying some memory - which may result in modifying... something or crash
';
    
risposta data 18.01.2013 - 13:38
fonte
6

Una cosa che nessuno dei thread mostra è questa:

char whopping_great[8192] = "foo";

vs.

char whopping_great[8192];
memcpy(whopping_great, "foo", sizeof("foo"));

Il primo farà qualcosa del tipo:

memcpy(whopping_great, "foo", sizeof("foo"));
memset(&whopping_great[sizeof("foo")], 0, sizeof(whopping_great)-sizeof("foo"));

Quest'ultimo fa solo la memcpy. Lo standard C insiste sul fatto che se una qualsiasi parte di un array viene inizializzata, è tutto. Quindi in questo caso, è meglio farlo da soli. Penso che potrebbe essere stato quello che stava arrivando a treuss.

Di sicuro

char whopping_big[8192];
whopping_big[0] = 0;

è migliore di entrambi:

char whopping_big[8192] = {0};

o

char whopping_big[8192] = "";

P.S. Per i punti bonus, puoi fare:

memcpy(whopping_great, "foo", (1/(sizeof("foo") <= sizeof(whopping_great)))*sizeof("foo"));

per generare un errore di compilazione per errore di zero se stai per scavalcare l'array.

    
risposta data 20.02.2015 - 21:56
fonte
5

Principalmente perché non avrai le dimensioni del char[] in una variabile / costrutto che puoi usare facilmente all'interno del programma.

Esempio di codice dal link:

 char string[] = "october";
 strcpy(string, "september");

string è allocato nello stack di 7 o 8 caratteri. Non riesco a ricordare se è terminato con null in questo modo o meno - il thread che hai collegato ha dichiarato che è.

Copiare "settembre" su quella stringa è un ovvio sovraccarico di memoria.

Un'altra sfida si verifica se si passa string a un'altra funzione in modo che l'altra funzione possa scrivere nell'array. Devi dire all'altra funzione per quanto tempo l'array è così esso non crea un sovraccarico. Puoi passare string insieme al risultato di strlen() ma il thread spiega come questo può esplodere se string non è terminato con null.

È meglio allocare una stringa con una dimensione fissa (preferibilmente definita come una costante) e quindi passare la matrice e la dimensione fissa all'altra funzione. I commenti di @John Bode sono corretti e ci sono modi per mitigare questi rischi. Richiedono anche un maggiore sforzo da parte dell'utente per utilizzarli.

Secondo la mia esperienza, il valore che ho inizializzato con char[] su è in genere troppo piccolo per gli altri valori che devo inserire lì. L'utilizzo di una costante definita consente di evitare tale problema.

sizeof string ti darà la dimensione del buffer (8 byte); usa il risultato di quell'espressione invece di strlen quando sei preoccupato per la memoria.
Allo stesso modo, puoi fare un controllo prima della chiamata a strcpy per vedere se il tuo buffer di destinazione è abbastanza grande per la stringa di origine: if (sizeof target > strlen(src)) { strcpy (target, src); } .
Sì, se devi passare la matrice a una funzione, devi anche passare la sua dimensione fisica: foo (array, sizeof array / sizeof *array); . - John Bode

    
risposta data 16.01.2013 - 17:38
fonte
2

Penso che l'idea di "cattiva pratica" derivi dal fatto che questo modulo:

char string[] = "october is a nice month";

rende implicitamente una strcpy dal codice macchina di origine allo stack.

È più efficiente gestire solo un collegamento a quella stringa. Come con:

char *string = "october is a nice month";

o direttamente:

strcpy(output, "october is a nice month");

(ma ovviamente nella maggior parte del codice probabilmente non ha importanza)

    
risposta data 02.09.2015 - 23:51
fonte
-3

Non è mai molto tempo, ma è necessario evitare il char di inizializzazione [] per la stringa, perché "string" è const char * e lo si assegna a char *. Quindi se passi questo [] metodo a un metodo che cambia i dati puoi avere un comportamento interessante.

Come encomiabile ho detto che ho mescolato un po 'char [] con char *, che non è buono in quanto differiscono un po'.

Non c'è nulla di sbagliato nell'assegnare dati al char array, ma come intenzione di usare questo array è usarlo come 'string' (char *), è facile dimenticare che non si dovrebbe modificare questo array.

    
risposta data 17.01.2013 - 06:49
fonte

Leggi altre domande sui tag