Question

On m'a demandé d'écrire un modèle strdup en créant une structure String sur le tas, qui contient une copie du source. Je pense avoir correctement codé le strdup, mais je ne suis pas sûr d'avoir créé un Struct sur le tas ...

typedef 
struct String {
    int length;
    int capacity;
    unsigned check;
    char ptr[0];
} String;

char* modelstrdup(char* src){
    int capacity =0, length=0, i = 0 ;
    char *string;
    while ( src[length] != '\0'){
        length++;
    }
    capacity = length;
    string = malloc(sizeof(String) + capacity + 1);
    while ( i < length ){
        string[i] = src[i];
        i++;
    }
    string[i+1] = '\0';

    return string;
}   
Était-ce utile?

La solution

Oui, vous avez créé une structure sur le tas. Vous ne l'avez pas renseigné correctement et vous allez avoir du mal à le supprimer. Je ne sais pas si les devoirs l'ont couvert ou non. Dans l'état actuel des choses, vous risquez davantage de subir une corruption de mémoire ou, si vous êtes chanceux, une fuite de mémoire plutôt que de libérer l'une de ces chaînes.

Code fonctionnant avec les standards C89 et C99

Votre code, un peu corrigé ...

typedef 
struct String {
    int length;
    int capacity;
    char *ptr;
} String;

char* modelstrdup(char* src){
    int length = strlen(src);
    char *space = malloc(sizeof(String) + length + 1);
    //String *string = space;  // Original code - compilers are not keen on it
    String *string = (String *)space;
    assert(space != 0);
    string->ptr = space + sizeof(String);  // or sizeof(*string)
    string->length = length;
    string->capacity = length + 1;
    strcpy(string->ptr, src);
    return string->ptr;
}

Ce code fonctionnera aussi bien en C89 qu'en C99 (sauf pour les commentaires C99 / C ++). Vous pouvez probablement l'optimiser pour qu'il fonctionne avec le 'struct hack' (enregistre un pointeur dans la structure - mais seulement si vous avez un compilateur C99). L'assertion est une gestion d'erreur non optimale. Le code ne se défend pas contre un pointeur nul pour l'entrée. Dans ce contexte, ni la longueur ni la capacité ne fournissent d’avantage - il doit y avoir d’autres fonctions dans la suite qui pourront utiliser ces informations.

Comme déjà indiqué, vous allez avoir des problèmes pour supprimer la structure de chaîne lorsque la valeur restituée n'est pas un pointeur sur la chaîne. Vous devez effectuer de délicats ajustements de pointeur.

Code fonctionnant avec le standard C99 uniquement

En C99, la section 6.7.2.1, paragraphe 16, décrit les "membres de groupe flexibles":

  

Comme cas particulier, le dernier élément d’une structure comportant plusieurs membres nommés peut   avoir un type de tableau incomplet; c'est ce qu'on appelle un membre de groupe flexible. Avec deux   exceptions, le membre du tableau flexible est ignoré. Premièrement, la taille de la structure doit être   égal au décalage du dernier élément d'une structure par ailleurs identique qui remplace le   membre de tableau flexible avec un tableau de longueur non spécifiée. 106) Deuxièmement, quand a. (ou - >)   l'opérateur a un opérande gauche qui est (un pointeur sur) une structure avec un membre de tableau flexible   et le bon opérande nomme ce membre, il se comporte comme si ce membre était remplacé   avec le plus long tableau (avec le même type d'élément) qui ne rendrait pas la structure   plus grand que l'objet auquel on accède; le décalage du tableau doit rester celui du   membre de groupe flexible, même si cela diffère de celui du groupe de remplacement. Si ce   tableau aurait pas d'éléments, il se comporte comme s'il avait un élément, mais le comportement est   indéfini si une tentative est faite pour accéder à cet élément ou pour générer un pointeur un passé   il.

     

106 La longueur n'est pas spécifiée pour tenir compte du fait que les implémentations peuvent donner des membres de tableau différents   alignements en fonction de leur longueur.

En utilisant un "membre de groupe flexible", votre code pourrait devenir:

typedef 
struct String {
    int length;
    int capacity;
    char ptr[];
} String;

char* modelstrdup(char* src){
    int length = strlen(src);
    String *string = malloc(sizeof(String) + length + 1);
    assert(string != 0);
    string->length = length;
    string->capacity = length + 1;
    strcpy(string->ptr, src);
    return string->ptr;
}

Ce code a été accepté comme propre par GCC 4.0.1, à l'exception d'une déclaration de la fonction (options -Wall -Wextra ). Le code précédent nécessite un transtypage sur 'String * string = (String *) space;' dire au compilateur que je voulais dire ce que j'ai dit; J'ai maintenant corrigé cela et laissé un commentaire pour afficher l'original.

Utilisation du 'struct hack'

Avant C99, les gens utilisaient souvent le 'struct hack' pour gérer cela. Il est très similaire au code indiqué dans la question, sauf que la dimension du tableau est 1 et non 0. Le standard C n'autorise pas les dimensions d'un tableau de taille zéro.

typedef struct String {
    size_t length;
    size_t capacity;
    char ptr[1];
} String;

char* modelstrdup(char* src)
{
    size_t length = strlen(src);
    String *string = malloc(sizeof(String) + length + 1);
    assert(string != 0);
    string->length = length;
    string->capacity = length + 1;
    strcpy(string->ptr, src);
    return string->ptr;
}

Code utilisant une extension non standard de GCC vers C89 et C99

La notation de tableau de taille zéro est acceptée par GCC, à moins que vous ne le mettiez dans le moule - spécifiez le standard ISO C et demandez une précision pédante. Par conséquent, ce code est compilé correctement à moins que vous n'utilisiez gcc -Wall -Wextra -std = c99 -pedantic :

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

typedef
struct String {
    int length;
    int capacity;
    char ptr[0];
} String;

char* modelstrdup(char* src){
    int length = strlen(src);
    String *string = malloc(sizeof(String) + length + 1);
    assert(string != 0);
    string->length = length;
    string->capacity = length + 1;
    strcpy(string->ptr, src);
    return string->ptr;
}

Cependant, vous ne devriez pas être formé à des extensions non standard du langage C avant de bien maîtriser les bases du standard C. Cela est tout simplement injuste pour vous; vous ne pouvez pas savoir si ce que l'on vous dit de faire est raisonnable, mais vos tuteurs ne doivent pas vous induire en erreur en vous obligeant à utiliser des contenus non standard. Même s'ils vous ont averti que ce n'est pas standard, ce n'est pas juste pour vous. C est assez difficile à apprendre sans apprendre des trucs compliqués, spécifiques au compilateur.

Autres conseils

Vous avez alloué de la mémoire sur le tas, mais vous ne l'utilisez pas comme s'il s'agissait de votre structure. La variable string de votre fonction est de type char * , pas de type struct String . Je pense que vous dupliquez suffisamment les fonctionnalités de strdup () , mais je ne comprends pas la raison de la structure.

Remarque: vous devriez probablement vérifier votre appel à malloc () et vous y retourner correctement. La page de manuel relative à strdup () devrait expliquer exactement ce que votre fonction devrait faire.

Vous avez. Malloc, new, etc. utilisent tous le tas.

Oui, malloc renvoie la mémoire sur le tas.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top