Ricerca…


introduzione

In C, una stringa non è un tipo intrinseco. Una C-string è la convenzione per avere una matrice unidimensionale di caratteri che viene terminata da un carattere null, da un '\0' .

Ciò significa che una stringa C con un contenuto di "abc" avrà quattro caratteri 'a' , 'b' , 'c' e '\0' .

Vedere l' introduzione di base all'esempio di stringhe .

Sintassi

  • char str1 [] = "Ciao, mondo!"; / * Modificabile * /
  • char str2 [14] = "Ciao, mondo!"; / * Modificabile * /
  • char * str3 = "Ciao, mondo!"; / * Non modificabile * /

Calcola la lunghezza: strlen ()

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

int main(int argc, char **argv) 
{
    /* Exit if no second argument is found. */
    if (argc != 2) 
    {
        puts("Argument missing.");
        return EXIT_FAILURE;
    }

    size_t len = strlen(argv[1]);
    printf("The length of the second argument is %zu.\n", len);

    return EXIT_SUCCESS;
}

Questo programma calcola la lunghezza del suo secondo argomento di input e memorizza il risultato in len . Quindi stampa quella lunghezza sul terminale. Ad esempio, se eseguito con i parametri program_name "Hello, world!" , il programma uscirà The length of the second argument is 13. perché la stringa Hello, world! è lungo 13 caratteri.

strlen conta tutti i byte dall'inizio della stringa fino a, ma non incluso, il carattere NUL terminante, '\0' . Come tale, può essere utilizzato solo quando la stringa è garantita per essere terminata NUL.

Inoltre, tieni presente che se la stringa contiene caratteri Unicode, strlen non ti dirà quanti caratteri ci sono nella stringa (dal momento che alcuni caratteri potrebbero essere più byte). In questi casi, è necessario contare i caratteri ( es . Unità di codice) da soli. Considera l'output del seguente esempio:

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

int main(void) 
{
    char asciiString[50] = "Hello world!";
    char utf8String[50] = "Γειά σου Κόσμε!"; /* "Hello World!" in Greek */

    printf("asciiString has %zu bytes in the array\n", sizeof(asciiString));
    printf("utf8String has %zu bytes in the array\n", sizeof(utf8String));
    printf("\"%s\" is %zu bytes\n", asciiString, strlen(asciiString));
    printf("\"%s\" is %zu bytes\n", utf8String, strlen(utf8String));
}

Produzione:

asciiString has 50 bytes in the array
utf8String has 50 bytes in the array
"Hello world!" is 12 bytes
"Γειά σου Κόσμε!" is 27 bytes

Copia e concatenazione: strcpy (), strcat ()

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

int main(void)
{
  /* Always ensure that your string is large enough to contain the characters
   * and a terminating NUL character ('\0')!
   */
  char mystring[10];

  /* Copy "foo" into `mystring`, until a NUL character is encountered. */
  strcpy(mystring, "foo");
  printf("%s\n", mystring);

  /* At this point, we used 4 chars of `mystring`, the 3 characters of "foo",
   * and the NUL terminating byte.
   */

  /* Append "bar" to `mystring`. */
  strcat(mystring, "bar");
  printf("%s\n", mystring);

  /* We now use 7 characters of `mystring`: "foo" requires 3, "bar" requires 3
   * and there is a terminating NUL character ('\0') at the end.
   */

  /* Copy "bar" into `mystring`, overwriting the former contents. */
  strcpy(mystring, "bar");
  printf("%s\n", mystring);

  return 0;
}

Uscite:

foo
foobar
bar

Se si aggiunge o si copia da una stringa esistente, assicurarsi che sia NUL-terminato!

Le stringhe letterali (ad es. "foo" ) saranno sempre terminate NUL dal compilatore.

Comparsa: strcmp (), strncmp (), strcasecmp (), strncasecmp ()

Le strcase* non sono Standard C, ma un'estensione POSIX.

La funzione strcmp confronta lessicograficamente due array di caratteri con terminazione null. Le funzioni restituiscono un valore negativo se il primo argomento compare prima del secondo in ordine lessicografico, zero se si confronta uguale o positivo se il primo argomento compare dopo il secondo in ordine lessicografico.

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

void compare(char const *lhs, char const *rhs)
{
    int result = strcmp(lhs, rhs); // compute comparison once
    if (result < 0) {
        printf("%s comes before %s\n", lhs, rhs);
    } else if (result == 0) {
        printf("%s equals %s\n", lhs, rhs);
    } else { // last case: result > 0
        printf("%s comes after %s\n", lhs, rhs);
    }
}

int main(void)
{
    compare("BBB", "BBB");
    compare("BBB", "CCCCC");
    compare("BBB", "AAAAAA");
    return 0;
}

Uscite:

BBB equals BBB
BBB comes before CCCCC
BBB comes after AAAAAA

Come strcmp , la funzione strcasecmp confronta anche lessicograficamente i suoi argomenti dopo aver tradotto ciascun carattere nel suo corrispondente in lettere minuscole:

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

void compare(char const *lhs, char const *rhs)
{
    int result = strcasecmp(lhs, rhs); // compute case-insensitive comparison once
    if (result < 0) {
        printf("%s comes before %s\n", lhs, rhs);
    } else if (result == 0) {
        printf("%s equals %s\n", lhs, rhs);
    } else { // last case: result > 0
        printf("%s comes after %s\n", lhs, rhs);
    }
}

int main(void)
{
    compare("BBB", "bBB");
    compare("BBB", "ccCCC");
    compare("BBB", "aaaaaa");
    return 0;
}

Uscite:

BBB equals bBB
BBB comes before ccCCC
BBB comes after aaaaaa

strncmp e strncasecmp confrontano al massimo n caratteri:

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

void compare(char const *lhs, char const *rhs, int n)
{
    int result = strncmp(lhs, rhs, n); // compute comparison once
    if (result < 0) {
        printf("%s comes before %s\n", lhs, rhs);
    } else if (result == 0) {
        printf("%s equals %s\n", lhs, rhs);
    } else { // last case: result > 0
        printf("%s comes after %s\n", lhs, rhs);
    }
}

int main(void)
{
    compare("BBB", "Bb", 1);
    compare("BBB", "Bb", 2);
    compare("BBB", "Bb", 3);
    return 0;
}

Uscite:

BBB equals Bb
BBB comes before Bb
BBB comes before Bb

Tokenizzazione: strtok (), strtok_r () e strtok_s ()

La funzione strtok spezza una stringa in stringhe più piccole, o token, usando un set di delimitatori.

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

int main(void)
{
    int toknum = 0;
    char src[] = "Hello,, world!";
    const char delimiters[] = ", !";
    char *token = strtok(src, delimiters);
    while (token != NULL)
    {
        printf("%d: [%s]\n", ++toknum, token);
        token = strtok(NULL, delimiters);
    }
    /* source is now "Hello\0, world\0\0" */
}

Produzione:

1: [Hello]
2: [world]

La stringa di delimitatori può contenere uno o più delimitatori e diverse stringhe delimitatori possono essere utilizzate con ogni chiamata a strtok .

Le chiamate a strtok per continuare a tokenizzare la stessa stringa sorgente non dovrebbero passare di nuovo la stringa sorgente, ma passare NULL come primo argomento. Se viene passata la stessa stringa sorgente, il primo token verrà invece ridenominato. Cioè, dati gli stessi delimitatori, strtok restituirebbe semplicemente il primo token di nuovo.

Notare che come strtok non alloca nuova memoria per i token, modifica la stringa di origine . Cioè, nell'esempio precedente, la stringa src verrà manipolata per produrre i token a cui fa riferimento il puntatore restituito dalle chiamate a strtok . Ciò significa che la stringa di origine non può essere const (quindi non può essere una stringa letterale). Significa anche che l'identità del byte di delimitazione viene persa (cioè nell'esempio il "," e "!" Vengono effettivamente eliminati dalla stringa di origine e non è possibile stabilire quale carattere delimitatore corrisponde).

Si noti inoltre che più delimitatori consecutivi nella stringa di origine vengono considerati come uno; nell'esempio, la seconda virgola viene ignorata.

strtok non è né thread-safe né ri-entrant perché usa un buffer statico durante l'analisi. Ciò significa che se una funzione chiama strtok , nessuna funzione che chiama mentre sta usando strtok può anche usare strtok , e non può essere chiamata da alcuna funzione che sta usando strtok .

Un esempio che dimostra i problemi causati dal fatto che strtok non è rientrante è il seguente:

char src[] = "1.2,3.5,4.2";
char *first = strtok(src, ","); 

do 
{
    char *part;
    /* Nested calls to strtok do not work as desired */
    printf("[%s]\n", first);
    part = strtok(first, ".");
    while (part != NULL)
    {
        printf(" [%s]\n", part);
        part = strtok(NULL, ".");
    }
} while ((first = strtok(NULL, ",")) != NULL);

Produzione:

[1.2]
 [1]
 [2]

L'operazione prevista è che il ciclo do while esterno crei tre token costituiti da ogni stringa decimale ( "1.2" , "3.5" , "4.2" ), per ognuno dei quali lo strtok chiama il ciclo interno dovrebbe dividerlo in un separato stringhe di cifre ( "1" , "2" , "3" , "5" , "4" , "2" ).

Tuttavia, poiché strtok non è rientranti, ciò non si verifica. Invece il primo strtok crea correttamente il token "1.2 \ 0" e il ciclo interno crea correttamente i token "1" e "2" . Ma poi lo strtok nel ciclo esterno è alla fine della stringa usata dal ciclo interno e restituisce immediatamente NULL. La seconda e la terza sottostringa dell'array src non vengono affatto analizzate.

C11

Le librerie C standard non contengono una versione thread-safe o re-entrant ma altre, come POSIX ' strtok_r . Nota che su MSVC l'equivalente strtok , strtok_s è thread-safe.

C11

C11 ha una parte opzionale, Annex K, che offre una versione thread-safe e re-entry chiamata strtok_s . Puoi provare la funzione con __STDC_LIB_EXT1__ . Questa parte facoltativa non è ampiamente supportata.

La funzione strtok_s differisce dalla funzione strtok_r POSIX strtok_r dall'archiviazione all'esterno della stringa che viene tokenizzata e controllando i vincoli di runtime. Sui programmi scritti correttamente, però, strtok_s e strtok_r comportano allo stesso modo.

Usando strtok_s con l'esempio ora si ottiene la risposta corretta, in questo modo:

/* you have to announce that you want to use Annex K */ 
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>

#ifndef __STDC_LIB_EXT1__
# error "we need strtok_s from Annex K"
#endif

char src[] = "1.2,3.5,4.2";  
char *next = NULL;
char *first = strtok_s(src, ",", &next);

do 
{
    char *part;
    char *posn;

    printf("[%s]\n", first);
    part = strtok_s(first, ".", &posn);
    while (part != NULL)
    {
        printf(" [%s]\n", part);
        part = strtok_s(NULL, ".", &posn);
    }
} 
while ((first = strtok_s(NULL, ",", &next)) != NULL);

E l'output sarà:

[1.2]
 [1]
 [2]
[3.5]
 [3]
 [5]
[4.2]
 [4]
 [2]

Trova la prima / ultima occorrenza di un carattere specifico: strchr (), strrchr ()

Le funzioni strchr e strrchr trovano un carattere in una stringa, cioè in una matrice di caratteri con terminazione NUL. strchr restituisce un puntatore alla prima occorrenza e strrchr all'ultima.

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

int main(void)
{
    char toSearchFor = 'A';

    /* Exit if no second argument is found. */
    if (argc != 2)
    {
        printf("Argument missing.\n");
        return EXIT_FAILURE;
    }

    {
        char *firstOcc = strchr(argv[1], toSearchFor);
        if (firstOcc != NULL) 
        {
            printf("First position of %c in %s is %td.\n", 
              toSearchFor, argv[1], firstOcc-argv[1]); /* A pointer difference's result 
                                     is a signed integer and uses the length modifier 't'. */
        }
        else
        {
            printf("%c is not in %s.\n", toSearchFor, argv[1]);
        }
    }

    {
        char *lastOcc = strrchr(argv[1], toSearchFor);
        if (lastOcc != NULL)
        {
            printf("Last position of %c in %s is %td.\n",
              toSearchFor, argv[1], lastOcc-argv[1]);
        }
    }

    return EXIT_SUCCESS;
}

Output (dopo aver generato un eseguibile chiamato pos ):

$ ./pos AAAAAAA
First position of A in AAAAAAA is 0.
Last position of A in AAAAAAA is 6.
$ ./pos BAbbbbbAccccAAAAzzz
First position of A in BAbbbbbAccccAAAAzzz is 1.
Last position of A in BAbbbbbAccccAAAAzzz is 15.
$  ./pos qwerty             
A is not in qwerty.

Un uso comune per strrchr è estrarre un nome di file da un percorso. Ad esempio per estrarre myfile.txt da C:\Users\eak\myfile.txt :

char *getFileName(const char *path)
{
    char *pend;

    if ((pend = strrchr(path, '\')) != NULL)
        return pend + 1;

    return NULL;
}

Iterating Over the Characters in a String

Se conosciamo la lunghezza della stringa, possiamo usare un ciclo for per scorrere i suoi caratteri:

char * string = "hello world"; /* This 11 chars long, excluding the 0-terminator. */
size_t i = 0;
for (; i < 11; i++) {
    printf("%c\n", string[i]);    /* Print each character of the string. */
}

In alternativa, possiamo usare la funzione standard strlen() per ottenere la lunghezza di una stringa se non sappiamo quale sia la stringa:

size_t length = strlen(string);
size_t i = 0; 
for (; i < length; i++) {
    printf("%c\n", string[i]);    /* Print each character of the string. */
}

Infine, possiamo approfittare del fatto che le stringhe in C sono garantite per essere terminate con null (cosa che abbiamo già fatto passandole a strlen() nell'esempio precedente ;-)). Possiamo scorrere l'array indipendentemente dalle sue dimensioni e interrompere l'iterazione una volta raggiunto un carattere null:

size_t i = 0;
while (string[i] != '\0') {       /* Stop looping when we reach the null-character. */
    printf("%c\n", string[i]);    /* Print each character of the string. */
    i++;
}

Introduzione di base alle stringhe

In C, una stringa è una sequenza di caratteri che viene terminata da un carattere nullo ('\ 0').

Possiamo creare stringhe usando stringhe letterali , che sono sequenze di caratteri circondate da virgolette doppie; per esempio, prendi la stringa letterale "hello world" . I valori letterali stringa vengono automaticamente annullati.

Possiamo creare stringhe usando diversi metodi. Ad esempio, possiamo dichiarare un char * e inizializzarlo per puntare al primo carattere di una stringa:

char * string = "hello world";

Quando si inizializza un char * su una costante di stringa come sopra, la stringa stessa viene solitamente allocata in dati di sola lettura; string è un puntatore al primo elemento dell'array, che è il carattere 'h' .

Poiché la stringa letterale è allocata nella memoria di sola lettura, non è modificabile 1 . Qualsiasi tentativo di modificarlo porterà a comportamenti non definiti , quindi è meglio aggiungere const per ottenere un errore in fase di compilazione come questo

char const * string = "hello world";

Ha un effetto simile a 2 come

char const string_arr[] = "hello world";

Per creare una stringa modificabile, puoi dichiarare una matrice di caratteri e inizializzarne il contenuto usando una stringa letterale, in questo modo:

char modifiable_string[] = "hello world";

Questo è equivalente al seguente:

char modifiable_string[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0'};

Poiché la seconda versione utilizza l'inizializzatore racchiuso tra virgolette, la stringa non viene automaticamente terminata da null a meno che un carattere '\0' sia incluso esplicitamente nell'array di caratteri in genere come ultimo elemento.


1 Non modificabile implica che i caratteri nella stringa letterale non possono essere modificati, ma ricorda che la string del puntatore può essere modificata (può indicare da qualche altra parte o può essere incrementata o decrementata).

2 Entrambe le stringhe hanno un effetto simile, nel senso che i caratteri di entrambe le stringhe non possono essere modificati. Dovrebbe essere notato che la string è un puntatore al char ed è un valore l modificabile in modo che possa essere incrementato o puntare a qualche altra posizione mentre l'array string_arr è un valore-l non modificabile, non può essere modificato.

Creazione di matrici di stringhe

Una serie di stringhe può significare un paio di cose:

  1. Un array i cui elementi sono char * s
  2. Una matrice i cui elementi sono matrici di char

Possiamo creare una serie di puntatori di caratteri in questo modo:

char * string_array[] = {
    "foo",
    "bar",
    "baz"
};

Ricorda: quando assegniamo stringhe letterali a char * , le stringhe vengono allocate nella memoria di sola lettura. Tuttavia, l'array string_array è allocato nella memoria di lettura / scrittura. Ciò significa che possiamo modificare i puntatori dell'array, ma non possiamo modificare le stringhe a cui puntano.

In C, il parametro di main argv (la matrice di argomenti della riga di comando passati quando il programma è stato eseguito) è un array di char * : char * argv[] .

Possiamo anche creare array di matrici di caratteri. Poiché le stringhe sono matrici di caratteri, una serie di stringhe è semplicemente una matrice i cui elementi sono matrici di caratteri:

char modifiable_string_array_literals[][4] = {
    "foo",
    "bar",
    "baz"
};

Questo è equivalente a:

char modifiable_string_array[][4] = {
    {'f', 'o', 'o', '\0'},
    {'b', 'a', 'r', '\0'},
    {'b', 'a', 'z', '\0'}
};

Si noti che si specifica 4 come dimensione della seconda dimensione dell'array; ciascuna delle stringhe nel nostro array è in realtà 4 byte poiché è necessario includere il carattere con terminazione nulla.

strstr

/* finds the next instance of needle in haystack 
   zbpos: the zero-based position to begin searching from
   haystack: the string to search in
   needle: the string that must be found
   returns the next match of `needle` in `haystack`, or -1 if not found
*/
int findnext(int zbpos, const char *haystack, const char *needle)
{
    char *p; 

    if (((p = strstr(haystack + zbpos, needle)) != NULL)
        return p - haystack;

    return -1;
}

strstr ricerca l'argomento haystack (primo) per la stringa puntata needle . Se trovato, strstr restituisce l'indirizzo dell'occorrenza. Se non riesce a trovare l' needle , restituisce NULL. Usiamo zbpos modo che non continuiamo a trovare lo stesso ago più e più volte. Per saltare la prima istanza, aggiungiamo un offset di zbpos . Un clone di Blocco note potrebbe chiamare findnext come questo, al fine di implementare il suo dialogo "Trova successivo":

/*
    Called when the user clicks "Find Next"
    doc: The text of the document to search
    findwhat: The string to find
*/
void onfindnext(const char *doc, const char *findwhat)
{
    static int i;

    if ((i = findnext(i, doc, findwhat)) != -1)
        /* select the text starting from i and ending at i + strlen(findwhat) */
    else
        /* display a message box saying "end of search" */
}

Stringhe letterali

I valori letterali delle stringhe rappresentano array di char statici a terminazione nulla di char . Poiché hanno una durata di archiviazione statica, una stringa letterale o un puntatore allo stesso array sottostante può essere tranquillamente utilizzata in diversi modi che non è possibile per un puntatore a un array automatico. Ad esempio, restituire una stringa letterale da una funzione ha un comportamento ben definito:

const char *get_hello() {
    return "Hello, World!";  /* safe */
}

Per ragioni storiche, gli elementi dell'array corrispondenti a una stringa letterale non sono formalmente const . Tuttavia, qualsiasi tentativo di modificarli ha un comportamento indefinito . In genere, un programma che tenta di modificare la matrice corrispondente a una stringa letterale si bloccherà o funzionerà in altro modo.

char *foo = "hello";
foo[0] = 'y';  /* Undefined behavior - BAD! */

Laddove un puntatore punta a una stringa letterale - o dove a volte può farlo - è consigliabile dichiarare il const referente di quel puntatore per evitare di coinvolgere accidentalmente un comportamento non definito.

const char *foo = "hello";
/* GOOD: can't modify the string pointed to by foo */

D'altra parte, un puntatore verso o nell'array sottostante di un letterale stringa non è di per sé intrinsecamente speciale; il suo valore può essere liberamente modificato per indicare qualcos'altro:

char *foo = "hello";
foo = "World!"; /* OK - we're just changing what foo points to */

Inoltre, sebbene gli inizializzatori per i char di tipo char possano avere la stessa forma dei letterali di stringa, l'uso di tale inizializzatore non conferisce le caratteristiche di una stringa letterale sull'array inizializzato. L'inizializzatore indica semplicemente la lunghezza e il contenuto iniziale dell'array. In particolare, gli elementi sono modificabili se non dichiarati esplicitamente const :

char foo[] = "hello";
foo[0] = 'y';  /* OK! */

Azzeramento di una stringa

È possibile chiamare memset per azzerare una stringa (o qualsiasi altro blocco di memoria).

Dove str è la stringa da azzerare e n è il numero di byte nella stringa.

#include <stdlib.h> /* For EXIT_SUCCESS */
#include <stdio.h>
#include <string.h>


int main(void)
{
  char str[42] = "fortytwo";
  size_t n = sizeof str; /* Take the size not the length. */

  printf("'%s'\n", str);

  memset(str, '\0', n);

  printf("'%s'\n", str);

  return EXIT_SUCCESS;
}

stampe:

'fortytwo'
''

Un altro esempio:

#include <stdlib.h> /* For EXIT_SUCCESS */
#include <stdio.h>
#include <string.h>


#define FORTY_STR "forty"
#define TWO_STR "two"

int main(void)
{
  char str[42] = FORTY_STR TWO_STR;
  size_t n = sizeof str; /* Take the size not the length. */
  char * point_to_two = strstr(str, TWO_STR);

  printf("'%s'\n", str);

  memset(point_to_two, '\0', n);

  printf("'%s'\n", str);

  memset(str, '\0', n);

  printf("'%s'\n", str);

  return EXIT_SUCCESS;
}

stampe:

'fortytwo'
'forty'
''

strspn e strcspn

Data una stringa, strspn calcola la lunghezza della sottostringa iniziale (span) costituita esclusivamente da un elenco specifico di caratteri. strcspn è simile, tranne che calcola la lunghezza della sottostringa iniziale costituita da qualsiasi carattere tranne quelli elencati:

/*
  Provided a string of "tokens" delimited by "separators", print the tokens along
  with the token separators that get skipped.
*/
#include <stdio.h>
#include <string.h>

int main(void)
{
    const char sepchars[] = ",.;!?";
    char foo[] = ";ball call,.fall gall hall!?.,";
    char *s;
    int n;

    for (s = foo; *s != 0; /*empty*/) {
        /* Get the number of token separator characters. */
        n = (int)strspn(s, sepchars);

        if (n > 0)
            printf("skipping separators: << %.*s >> (length=%d)\n", n, s, n);

        /* Actually skip the separators now. */
        s += n;

        /* Get the number of token (non-separator) characters. */
        n = (int)strcspn(s, sepchars);

        if (n > 0)
            printf("token found: << %.*s >> (length=%d)\n", n, s, n);

        /* Skip the token now. */
        s += n;
    }

    printf("== token list exhausted ==\n");

    return 0;
}

Funzioni analoghe che utilizzano stringhe di caratteri ampi sono wcsspn e wcscspn ; sono usati allo stesso modo.

Copia di stringhe

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.

Converti stringhe in numero: atoi (), atof () (pericoloso, non usarle)

Avvertenza: le funzioni atoi , atol , atoll e atof sono intrinsecamente insicuri, poiché: Se il valore del risultato non può essere rappresentato, il comportamento non è definito. (7.20.1p1)

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
    int val;
    if (argc < 2)
    {
        printf("Usage: %s <integer>\n", argv[0]);
        return 0;
    }

    val = atoi(argv[1]);

    printf("String value = %s, Int value = %d\n", argv[1], val);

    return 0;
}

Quando la stringa da convertire è un intero decimale valido compreso nell'intervallo, la funzione funziona:

$ ./atoi 100
String value = 100, Int value = 100
$ ./atoi 200
String value = 200, Int value = 200

Per le stringhe che iniziano con un numero, seguito da qualcos'altro, viene analizzato solo il numero iniziale:

$ ./atoi 0x200
0
$ ./atoi 0123x300
123

In tutti gli altri casi, il comportamento non è definito:

$ ./atoi hello
Formatting the hard disk...

A causa delle ambiguità di cui sopra e di questo comportamento indefinito, la famiglia di funzioni atoi non dovrebbe mai essere utilizzata.

  • Per convertire in long int , usa strtol() invece di atol() .
  • Per convertire in double , usa strtod() invece di atof() .
C99
  • Per convertire in long long int , usa strtoll() invece di atoll() .

lettura / scrittura di dati formattati a stringa

Scrivi dati formattati su stringa

int sprintf ( char * str, const char * format, ... );

usa la funzione sprintf per scrivere i dati float sulla stringa.

#include <stdio.h>
int main ()
{
  char buffer [50];
  double PI = 3.1415926;
  sprintf (buffer, "PI = %.7f", PI);
  printf ("%s\n",buffer);
  return 0;
}

Leggi i dati formattati dalla stringa

int sscanf ( const char * s, const char * format, ...);

usa la funzione sscanf per analizzare i dati formattati.

#include <stdio.h>
int main ()
{
  char sentence []="date : 06-06-2012";
  char str [50];
  int year;
  int month;
  int day;
  sscanf (sentence,"%s : %2d-%2d-%4d", str, &day, &month, &year);
  printf ("%s -> %02d-%02d-%4d\n",str, day, month, year);
  return 0;
}

Converti in sicurezza le stringhe in numero: funzioni strtoX

C99

Dal momento che C99 la libreria C ha un set di funzioni di conversione sicure che interpretano una stringa come un numero. I loro nomi sono di forma strtoX , dove X è uno di l , ul , d , ecc per determinare il tipo di destinazione della conversione

double strtod(char const* p, char** endptr);
long double strtold(char const* p, char** endptr);

Forniscono il controllo che una conversione ha avuto un over o underflow:

double ret = strtod(argv[1], 0); /* attempt conversion */

/* check the conversion result. */
if ((ret == HUGE_VAL || ret == -HUGE_VAL) && errno == ERANGE) 
    return;  /* numeric overflow in in string */
else if (ret == HUGE_VAL && errno == ERANGE) 
    return; /* numeric underflow in in string */

/* At this point we know that everything went fine so ret may be used */

Se la stringa in effetti non contiene alcun numero, questo uso di strtod restituisce 0.0 .

Se ciò non è soddisfacente, è possibile utilizzare il parametro addizionale endptr . È un puntatore al puntatore che verrà puntato alla fine del numero rilevato nella stringa. Se è impostato su 0 , come sopra, o NULL , viene semplicemente ignorato.

Questo parametro endptr indica se è avvenuta una conversione corretta e, in caso affermativo, dove il numero è terminato:

char *check = 0;
double ret = strtod(argv[1], &check); /* attempt conversion */

/* check the conversion result. */
if (argv[1] == check) 
    return; /* No number was detected in string */
else if ((ret == HUGE_VAL || ret == -HUGE_VAL) && errno == ERANGE) 
    return; /* numeric overflow in in string */
else if (ret == HUGE_VAL && errno == ERANGE) 
    return; /* numeric underflow in in string */

/* At this point we know that everything went fine so ret may be used */

Esistono funzioni analoghe per la conversione in tipi interi più ampi:

long strtol(char const* p, char** endptr, int nbase);
long long strtoll(char const* p, char** endptr, int nbase);
unsigned long strtoul(char const* p, char** endptr, int nbase);
unsigned long long strtoull(char const* p, char** endptr, int nbase);

Queste funzioni hanno un terzo parametro nbase che contiene la base numerica in cui è scritto il numero.

long a = strtol("101",   0, 2 ); /* a = 5L */
long b = strtol("101",   0, 8 ); /* b = 65L */
long c = strtol("101",   0, 10); /* c = 101L */
long d = strtol("101",   0, 16); /* d = 257L */
long e = strtol("101",   0, 0 ); /* e = 101L */
long f = strtol("0101",  0, 0 ); /* f = 65L */
long g = strtol("0x101", 0, 0 ); /* g = 257L */

Il valore speciale 0 per nbase significa che la stringa viene interpretata nello stesso modo in cui i numeri letterali vengono interpretati in un programma C: un prefisso di 0x corrisponde a una rappresentazione esadecimale, altrimenti uno 0 è ottale e tutti gli altri numeri sono visti come decimali.

Quindi il modo più pratico per interpretare un argomento da linea di comando come un numero sarebbe

int main(int argc, char* argv[] {
    if (argc < 1)
        return EXIT_FAILURE; /* No number given. */

    /* use strtoull because size_t may be wide */
    size_t mySize = strtoull(argv[1], 0, 0);

    /* then check conversion results. */

     ...

    return EXIT_SUCCESS;
}

Ciò significa che il programma può essere chiamato con un parametro in ottale, decimale o esadecimale.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow