C Language Copia di stringhe


Esempio

Le assegnazioni di puntatore non copiano le stringhe

È possibile utilizzare l'operatore = per copiare gli interi, ma non è possibile utilizzare l'operatore = per copiare le stringhe in C. Le stringhe in C sono rappresentate come matrici di caratteri con un carattere null terminante, quindi l'utilizzo dell'operatore = salverà solo l'indirizzo ( puntatore) di una stringa.

#include <stdio.h>

int main(void) {
    int a = 10, b;
    char c[] = "abc", *d;

    b = a; /* Integer is copied */
    a = 20; /* Modifying a leaves b unchanged - b is a 'deep copy' of a */
    printf("%d %d\n", a, b); /* "20 10" will be printed */

    d = c; 
    /* Only copies the address of the string - 
    there is still only one string stored in memory */
    
    c[1] = 'x';
    /* Modifies the original string - d[1] = 'x' will do exactly the same thing */

    printf("%s %s\n", c, d); /* "axc axc" will be printed */

    return 0;
}

L'esempio precedente è stato compilato perché abbiamo usato char *d piuttosto che char d[3] . L'utilizzo di quest'ultimo causerebbe un errore del compilatore. Non è possibile assegnare agli array in C.

#include <stdio.h>

int main(void) {
    char a[] = "abc";
    char b[8];

    b = a; /* compile error */
    printf("%s\n", b);

    return 0;
}

Copia di stringhe usando funzioni standard

strcpy()

Per copiare effettivamente le stringhe, la funzione strcpy() è disponibile in string.h . Abbastanza spazio deve essere assegnato per la destinazione prima di copiare.

#include <stdio.h>
#include <string.h>

int main(void) {
    char a[] = "abc";
    char b[8];

    strcpy(b, a); /* think "b special equals a" */
    printf("%s\n", b); /* "abc" will be printed */

    return 0;
}
C99

snprintf()

Per evitare il sovraccarico del buffer, è possibile utilizzare snprintf() . Non è la migliore soluzione per quanto riguarda le prestazioni dal momento che deve analizzare la stringa del modello, ma è l'unica funzione di protezione dal limite del buffer per copiare stringhe prontamente disponibili nella libreria standard, che può essere utilizzata senza ulteriori passaggi.

#include <stdio.h>
#include <string.h>

int main(void) {
    char a[] = "012345678901234567890";
    char b[8];

#if 0
    strcpy(b, a); /* causes buffer overrun (undefined behavior), so do not execute this here! */
#endif

    snprintf(b, sizeof(b), "%s", a); /* does not cause buffer overrun */
    printf("%s\n", b); /* "0123456" will be printed */

    return 0;
}

strncat()

Una seconda opzione, con prestazioni migliori, consiste nell'usare strncat() (una versione di controllo dell'overflow del buffer di strcat() ) - richiede un terzo argomento che indica il numero massimo di byte da copiare:

char dest[32];

dest[0] = '\0';
strncat(dest, source, sizeof(dest) - 1);
    /* copies up to the first (sizeof(dest) - 1) elements of source into dest,
    then puts a \0 on the end of dest */

Nota che questa formulazione usa sizeof(dest) - 1 ; questo è cruciale perché strncat() aggiunge sempre un byte null (buono), ma non lo considera nella dimensione della stringa (causa di confusione e sovrascritture del buffer).

Si noti inoltre che l'alternativa - concatenare dopo una stringa non vuota - è ancora più complessa. Tenere conto:

char dst[24] = "Clownfish: ";
char src[] = "Marvin and Nemo";
size_t len = strlen(dst);

strncat(dst, src, sizeof(dst) - len - 1);
printf("%zu: [%s]\n", strlen(dst), dst);

L'output è:

23: [Clownfish: Marvin and N]

Si noti, tuttavia, che la dimensione specificata come lunghezza non era la dimensione dell'array di destinazione, ma la quantità di spazio rimasto al suo interno, senza contare il byte null del terminale. Questo può causare grossi problemi di sovrascrittura. È anche un po 'dispendioso; per specificare correttamente l'argomento della lunghezza, si conosce la lunghezza dei dati nella destinazione, quindi è possibile specificare l'indirizzo del byte nullo alla fine del contenuto esistente, salvando strncat() dalla nuova scansione:

    strcpy(dst, "Clownfish: ");
    assert(len < sizeof(dst) - 1);
    strncat(dst + len, src, sizeof(dst) - len - 1);
    printf("%zu: [%s]\n", strlen(dst), dst);

Questo produce lo stesso risultato di prima, ma strncat() non deve eseguire la scansione del contenuto esistente di dst prima che inizi a copiare.

strncpy()

L'ultima opzione è la funzione strncpy() . Anche se si potrebbe pensare che dovrebbe venire prima, è una funzione piuttosto ingannevole che ha due trucchi principali:

  1. Se la copia tramite strncpy() colpisce il limite del buffer, non verrà scritto un carattere null terminante.
  2. strncpy() riempie sempre completamente la destinazione, con byte null se necessario.

(Tale implementazione bizzarra è storica e inizialmente era pensata per gestire nomi di file UNIX )

L'unico modo corretto per usarlo è quello di garantire manualmente la terminazione null:

strncpy(b, a, sizeof(b)); /* the third parameter is destination buffer size */
b[sizeof(b)/sizeof(*b) - 1] = '\0'; /* terminate the string */
printf("%s\n", b); /* "0123456" will be printed */

Anche in questo caso, se si dispone di un buffer di grandi dimensioni diventa molto inefficiente utilizzare strncpy() causa di ulteriore riempimento nullo.