web-dev-qa-db-fra.com

Bonne façon d'initialiser une chaîne en C

J'ai vu le code des gens comme:

char *str = NULL;

et j'ai vu que c'est aussi,

char *str;

Je me demande, quelle est la bonne façon d'initialiser une chaîne? et quand êtes-vous censé initialiser une chaîne avec et sans NULL?

13
foobar01

Vous êtes censé le configurer avant de l'utiliser. C'est la seule règle que vous avez à suivre pour éviter un comportement indéfini. Le fait de l'initialiser au moment de la création ou de l'attribuer juste avant de l'utiliser n'est pas pertinent.

Personnellement, je préfère ne jamais avoir de variables définies moi-même sur des valeurs inconnues, donc je vais généralement faire la première à moins qu'elle ne soit définie à proximité (en quelques lignes).

En fait, avec C99, où vous n'avez plus à déclarer les sections locales au sommet des blocs, je reporte généralement sa création jusqu'à ce que ce soit nécessaire, auquel cas il peut également être initialisé.

Notez que les variables reçoivent des valeurs par défaut dans certaines circonstances (par exemple, s'il s'agit d'une durée de stockage statique telle qu'une déclaration au niveau du fichier, en dehors de toute fonction).

Les variables locales ne bénéficient pas de cette garantie. Donc, si votre deuxième déclaration ci-dessus (char *str;) est à l'intérieur d'une fonction, il peut y avoir des déchets et essayer de l'utiliser invoquera le comportement indéfini, redouté, mentionné ci-dessus.

La partie pertinente de la norme C99 6.7.8/10:

Si un objet qui a une durée de stockage automatique n'est pas initialisé explicitement, sa valeur est indéterminée. Si un objet qui a une durée de stockage statique n'est pas initialisé explicitement, alors:

  • s'il a un type pointeur, il est initialisé à un pointeur nul;
  • s'il est de type arithmétique, il est initialisé à zéro (positif ou non signé);
  • s'il s'agit d'un agrégat, chaque membre est initialisé (récursivement) selon ces règles;
  • s'il s'agit d'une union, le premier membre nommé est initialisé (récursivement) selon ces règles.
16
paxdiablo
Je me demande, quelle est la bonne façon d'initialiser une chaîne?

Eh bien, puisque le deuxième extrait de code définit un pointeur non initialisé vers une chaîne, je dirais le premier. :)

En général, si vous voulez jouer la sécurité, il est bon d'initialiser à NULL tous les pointeurs; de cette façon, il est facile de repérer les problèmes dérivés de pointeurs non initialisés, car déréférencer un pointeur NULL entraînera un plantage (en fait, en ce qui concerne le standard, c'est un comportement non défini, mais sur chaque machine sur laquelle j'ai vu que c'est un crash).

Cependant, vous ne devez pas confondre un pointeur NULL vers une chaîne avec une chaîne vide: un pointeur NULL vers une chaîne signifie que ce pointeur ne pointe vers rien, tandis qu'une chaîne vide est un "réel", zéro -length chaîne (c'est-à-dire qu'elle contient juste un caractère NUL).

char * str=NULL; /* NULL pointer to string - there's no string, just a pointer */
const char * str2 = ""; /* Pointer to a constant empty string */

char str3[] = "random text to reach 15 characters ;)"; /* String allocated (presumably on the stack) that contains some text */
*str3 = 0; /* str3 is emptied by putting a NUL in first position */
6
Matteo Italia

c'est une question générale sur les variables c et pas seulement sur les char ptrs.

Il est recommandé d'initialiser une variable au point de déclaration. c'est à dire

char *str = NULL;

c'est une bonne chose. De cette façon, vous n'avez jamais de variables avec des valeurs inconnues. Par exemple, si plus tard dans votre code, vous faites

if(str != NULL)
 doBar(str);

Que va-t-il se passer. str est dans un état inconnu (et presque certainement pas NULL)

Notez que les variables statiques seront initialisées à zéro/NULL pour vous. Ce n'est pas clair d'après la question si vous posez des questions sur les habitants ou la statique

4
pm100

Les variables globales sont initialisées avec les valeurs par défaut par un compilateur, mais les variables locales doivent être initialisées.

2
Max
static const char str[] = "str";

ou

static char str[] = "str";
1
Teme

Parce que free () ne fait rien si vous lui passez une valeur NULL, vous pouvez simplifier votre programme comme ceci:

char *str = NULL;

if ( somethingorother() )
{
    str = malloc ( 100 );

    if ( NULL == str )
        goto error;
}

...

error:

cleanup();
free ( str );

Si, pour une raison quelconque, quelque chose ou une autre () retourne 0, si vous n'avez pas initialisé str, vous libèrerez une adresse aléatoire n'importe où, causant éventuellement un échec.

Je m'excuse pour l'utilisation de goto, je sais que certains le trouvent offensant. :)

1
onemasse

un pointeur unitialisé doit être considéré comme non défini, donc pour éviter de générer des erreurs en utilisant une valeur non définie, il est toujours préférable d'utiliser

char *str = NULL;

aussi parce que

char *str;

ce sera juste un pointeur non alloué vers un endroit qui causera principalement des problèmes lorsqu'il est utilisé si vous oubliez de l'allouer, vous devrez l'allouer DE TOUTE MANIÈRE (ou copier un autre pointeur).

Cela signifie que vous pouvez choisir:

  • si vous savez que vous allez l'allouer peu de temps après sa déclaration, vous pouvez éviter de le définir commeNULL (c'est une sorte de règle à suivre)
  • dans tous les autres cas, si vous voulez être sûr, faites-le. Le seul vrai problème survient si vous essayez de l'utiliser sans l'avoir initialisé.
1
Jack

N'initialisez pas toutes vos variables de pointeur à NULL lors de la déclaration "juste au cas où".

Le compilateur vous avertira si vous essayez d'utiliser une variable de pointeur qui n'a pas été initialisée, sauf lorsque vous la passez par adresse à une fonction (et vous faites généralement cela pour lui donner une valeur) .

Initialiser un pointeur sur NULL n'est pas la même chose que l'initialiser sur une valeur sensible, et l'initialiser à NULL désactive simplement la capacité du compilateur à vous dire que vous n'avez pas initialisé à une valeur raisonnable.

N'initialisez les pointeurs sur NULL lors de la déclaration que si vous obtenez un avertissement du compilateur dans le cas contraire, ou si vous les passez par adresse à une fonction qui s'attend à ce qu'ils soient NULL.

Si vous ne pouvez pas voir à la fois la déclaration d'une variable de pointeur et le point auquel une valeur lui est attribuée pour la première fois dans le même écran plein écran, votre fonction est trop grande.

1

Cela dépend entièrement de la façon dont vous allez l'utiliser. Dans ce qui suit, il est plus logique pas d'initialiser la variable:

int count;
while ((count = function()) > 0)
{
}
1
user207421

Par bon, vous voulez dire sans bug? eh bien, cela dépend de la situation. Mais il y a quelques règles de base que je peux recommander.

Tout d'abord, notez que les chaînes en C ne sont pas comme des chaînes dans d'autres langages.

Ce sont des pointeurs vers un bloc de caractères. La fin de laquelle se termine par un terminateur de 0 octet ou NULL. donc chaîne terminée par null.

Ainsi, par exemple, si vous allez faire quelque chose comme ceci:

char* str;  
gets(str);

ou interagir avec str de quelque manière que ce soit, alors c'est un bug monumental. La raison en est que, comme je viens de le dire, en C, les chaînes ne sont pas des chaînes comme les autres langages. Ce ne sont que des pointeurs. char * str est la taille d'un pointeur et le sera toujours.

Par conséquent, vous devez allouer de la mémoire pour contenir une chaîne.

/* this allocates 100 characters for a string 
   (including the null), remember to free it with free() */
char* str = (char*)malloc(100);
str[0] = 0;

/* so does this, automatically freed when it goes out of scope */
char str[100] = "";

Cependant, parfois, tout ce dont vous avez besoin est un pointeur.
par exemple.

/* This declares the string (not intialized) */
char* str;

/* use the string from earlier and assign the allocated/copied
   buffer to our variable */
str = strdup(other_string);

En général, cela dépend vraiment de la façon dont vous prévoyez d'utiliser le pointeur de chaîne. Ma recommandation est d'utiliser le formulaire de tableau de taille fixe si vous ne l'utilisez que dans le cadre de cette fonction et que la chaîne est relativement petite. Ou initialisez-le à NULL. Ensuite, vous pouvez tester explicitement la chaîne NULL, ce qui est utile lorsqu'elle est passée dans une fonction.

Sachez que l'utilisation du formulaire tableau peut également être un problème si vous utilisez une fonction qui vérifie simplement NULL pour savoir où se trouve la fin de la chaîne. par exemple. Les fonctions strcpy ou strcat ne se soucient pas de la taille de votre tampon. Par conséquent, envisagez d'utiliser une alternative comme strlcpy et strlcat de BSD. Ou strcpy_s et strcat_s (Windows).

De nombreuses fonctions attendent que vous passiez également une adresse correcte. Alors encore une fois, sachez que

char* str = NULL;
strcmp(str, "Hello World");

se plantera beaucoup parce que strcmp n'aime pas avoir NULL passé.

Vous l'avez marqué comme C, mais si quelqu'un utilise C++ et lit cette question, passez à l'utilisation de std :: string lorsque cela est possible et utilisez la fonction membre .c_str () sur la chaîne où vous devez interagir avec une API qui nécessite un chaîne c terminée par null standard.

0
Matt

Votre premier extrait de code est une définition de variable avec initialisation; le deuxième extrait de code est une définition de variable sans initialisation.

La bonne façon d'initialiser une chaîne est de fournir un initialiseur lorsque vous la définissez. L'initialiser à NULL ou autre chose dépend de ce que vous voulez en faire.

Soyez également conscient de ce que vous appelez "chaîne". C n'a pas un tel type: généralement "chaîne" dans un contexte C signifie "tableau de [un certain nombre de] caractères". Vous avez des pointeurs vers char dans les extraits ci-dessus.

Supposons que vous ayez un programme qui veut le nom d'utilisateur dans argv [1] et le copie dans la chaîne "nom". Lorsque vous définissez la variable name, vous pouvez la garder non initialisée, ou l'initialiser à NULL (s'il s'agit d'un pointeur vers char), ou l'initialiser avec un nom par défaut.

int main(int argc, char **argv) {
    char name_uninit[100];
    char *name_ptr = NULL;
    char name_default[100] = "anonymous";

    if (argc > 1) {
        strcpy(name_uninit, argv[1]); /* beware buffer overflow */
        name_ptr = argv[1];
        strcpy(name_default, argv[1]); /* beware buffer overflow */
    }

    /* ... */

    /* name_uninit may be unusable (and untestable) if there were no command line parameters */
    /* name_ptr may be NULL, but you can test for NULL */
    /* name_default is a definite name */
}
0
pmg