2
votes

String vs String Literal: Affectation directe ou strcpy / strncpy?

Je pense que j'ai un problème pour comprendre les chaînes et les chaînes littérales.

C'est ce que j'ai appris de ma classe, en passant à une fonction, const char * indique qu'il s'agit d'une chaîne littérale, tandis que char * indique une chaîne.

Supposons que j'ai une déclaration struct:

strcpy(input_node->name, input_name);// Suppose it's safe in this case.

et une fonction, voici la fonction que je veux implémenter:

input_node->name = input_name;

Cette fonction est censée affecter input_name au nom de membre de la structure.

Ma confusion vient de là. Dans le corps de load_buffer, dois-je écrire:

void load_buffer(struct node *input_node, char *input_name);

Ou dois-je utiliser strcpy / strncpy pour faire cela?

struct node{
    char *name;
    struct node *next;
};

Pour résumer, je ne sais pas si je devrais utiliser l'affectation directe ou les fonctions de la famille strcpy pour attribuer une chaîne / chaîne littérale au membre de ma structure.

Merci pour votre aide. :)


7 commentaires

input_node-> name = input_name; devrait convenir, étant donné que le input_name ne change pas plus tard.


Cela dépend de la sémantique que vous souhaitez: la struct doit-elle posséder la chaîne référencée, et comment doit-elle être allouée?


Si vous avez un pointeur, vous devez le faire pointer vers un endroit valide avant d'utiliser strcpy . Quant à savoir lequel utiliser, cela dépend vraiment. Utiliserez-vous toujours des chaînes littérales? Avez-vous besoin de modifier les chaînes? Ajouter à eux?


Non, je n'ai pas besoin de modifier la chaîne, la "chaîne" que j'utiliserai est lue à partir d'un fichier, cela indique-t-il que je devrais utiliser l'affectation directe au lieu des fonctions de la famille strcpy?


"string" sera-t-il le même pour tous les nœuds?


Non, la raison pour laquelle j'utilise des guillemets doubles pour inclure une chaîne est que je ne sais pas si je dois utiliser une chaîne ou une chaîne littérale pour décrire cette "chaîne". La chaîne / chaîne littérale réelle est par ligne qui se trouve dans mon fichier, mais cela ne fait pas partie de cette question de toute façon.


Si vous lisez une chaîne dans un fichier, vous ne pouvez pas simplement faire des affectations, car les affectations feront simplement pointer tous les pointeurs vers le même tampon (le tampon que vous lisez). Vous devez allouer de la mémoire pour chaque chaîne. Soit dynamiquement, soit transformez vos pointeurs en tableaux.


3 Réponses :


3
votes

En cas d'assignation de pointeur, les pointeurs de chaque nœud pointeront vers le même emplacement. Ainsi, les nœuds pointeront toujours vers la valeur mise à jour. Si vous avez l'intention de faire en sorte que chaque nœud contienne une entrée différente, cette approche ne répond pas à vos besoins.

input_node->name = malloc(strlen(input_name)+1); //Allocate memory first.
strcpy(input_node->name, input_name);// Suppose it's safe in this case.

Dans le cas de strcpy , les pointeurs de chaque nœud pointeront vers un emplacement différent. Avant cela, vous devez créer de la mémoire pour chaque pointeur.

input_node->name = input_name;

Pour visualiser: entrez la description de l'image ici


1 commentaires

vous faites malloc () , vous devez free () .



3
votes

... lors du passage à une fonction, const char * indique qu'il s'agit d'un littéral de chaîne, tandis que char * indique une chaîne.

Pas exactement. const char * déclare que la fonction n'essaiera pas de modifier la chaîne. Il convient donc parfaitement aux chaînes littérales car elles ne peuvent pas être modifiées.

Pour votre question, la réponse est cela dépend de vos besoins réels . Mais simplement stocker le pointeur passé s'il est dangereux si la structure peut persister après la fonction et si la chaîne peut être modifiée dans l'appelant. Regardons le code suivant:

void load_buffer(struct node *input_node, const char *input_name) {
    input_node->name = strdup(name);  // allocation errors should be tested...
}

Les deux objets node auront leur membre name pointant vers la chaîne buf de l'appelant et pointeront tous les deux sur "bar" (le contenu de buf à la fin de la boucle)!

Si vous souhaitez conserver la valeur de la chaîne au moment de l'appel, vous devez la copier dans la mémoire allouée:

void load_buffer(struct node *input_node, const char *input_name) {
    input_node->name = name;
}

struct node nodes[2];
char buf[4];
const char *data[] = { "foo", "bar"};
for (int i=0; i<2; i++) {
    strcpy(buf, data[i]);    // suppose it is read from somewhere (file, stdin, socket)
    load_buffer(node + i, buf);
}

Mais vous devez alors libérer le nom code> membre, lorsque le nœud n'est plus utilisé ...


0 commentaires

1
votes

D'abord à aborder une certaine terminologie:

  • ceci est une chaîne littérale: "Je suis une chaîne littérale"
  • c'est un type: char * (aka pointeur vers char)
  • c'est aussi un type: const char * (aka pointer to constant char)
  • il s'agit de la déclaration d'une variable de type char * : char * str
  • il s'agit de la déclaration d'une variable de type const char * : const char * cstr

Un pointeur n'est pas une chaîne littérale. Un pointeur peut pointer sur une chaîne littérale, un tableau, juste sur un seul élément ou peut être nul.

En C , une chaîne est un tableau de caractères terminé par null. P >

En C , vous pouvez affecter une variable char * à un littéral de chaîne, mais la modification du littéral de chaîne est illégale, il est donc fortement conseillé de ne jamais le faire. La raison pour laquelle cela est autorisé est historique.

char* a = "asd"; // allowed, but frowned upon
// a points to a string literal, so we can say a is a string
// a is not a string literal

char b = 'x';
char* c = &b;
// c points to a single char
// we cannot say c is a string

char d[10] = "asd";
// d is a char array. Its content is a string, so we can say d is a string.
// d is not a string literal
// the string literal is copied into the array d

char* e = d; // or equivalent char* e = &d[0];
// e points to a string

char f[4] = {'a', 's', 'd', '\0'};
// f is an array. Its content is a string, so we can say f is a string

char* g = f;
// g points to a string. We can say g is a string

char h[3] = {'a', 's', 'd'};
// h is an array. Its content is NOT a string, because the char array is not null terminated

char* i = h;
// i is not a string

Et maintenant, revoyez tout ce qui précède, mais ne remplacez pas char par const char et tous les commentaires sont toujours valables (sauf que `const char * a =" asd "est maintenant ok).


Et maintenant au problème en cours.

Il existe deux scénarios:

  1. Chaque nœud a sa propre chaîne et "possède" cette chaîne. Chaque nœud est responsable de l'allocation de mémoire pour la chaîne et de la libération de cette mémoire. La chaîne vit aussi longtemps que le nœud vit. Dans ce cas, utilisez malloc et strcpy pour créer une chaîne pour chaque nœud. N'oubliez pas de libérer la chaîne lorsque le nœud est détruit. C'est le scénario le plus courant et probablement ce que vous voulez.

  2. Un nœud ne possède pas sa propre chaîne, mais pointe plutôt vers une chaîne externe. Il n'est pas permis d'allouer ni de libérer de la mémoire pour cette chaîne. Il existe une autre entité chargée de gérer la durée de vie de cette chaîne et de s'assurer que la chaîne est active au moins tant que le nœud est actif. La chaîne peut survivre au nœud sans fuite de mémoire.

Par exemple, considérez ce scénario:

  • il existe une liste R de ressources chaîne. Cette liste possède ces ressources. Cette liste utiliserait le scénario 1
  • nous devons conserver deux ordres de tri différents de R. Nous avons donc deux listes A et B qui utiliseraient le scénario 2: chaque nœud de A et B pointe simplement vers une chaîne de R.


0 commentaires