Domanda

Mi è stato chiesto di scrivere un modello strdup creando una struttura String sull'heap che contiene una copia del sorgente. Penso di aver codificato con successo lo strdup, ma non sono sicuro di aver creato un Struct sull'heap ...

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;
}   
È stato utile?

Soluzione

Sì, hai creato una struttura sull'heap. Non lo hai popolato correttamente e avrai problemi a cancellarlo: non sono sicuro che i compiti lo coprano o no. Allo stato attuale, è più probabile che tu ottenga un danneggiamento della memoria o, se sei fortunato, una perdita di memoria rispetto al rilascio di una di queste stringhe.

Codice compatibile con gli standard C89 e C99

Il tuo codice, in qualche modo corretto ...

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;
}

Questo codice funzionerà sia in C89 che in C99 (ad eccezione dei commenti C99 / C ++). Probabilmente puoi ottimizzarlo per funzionare con 'struct hack' (salva un puntatore nella struttura - ma solo se hai un compilatore C99). L'asserzione è una gestione degli errori non ottimale. Il codice non si difende da un puntatore nullo per l'input. In questo contesto, né la lunghezza né la capacità offrono alcun vantaggio: ci devono essere altre funzioni nella suite che saranno in grado di utilizzare tali informazioni.

Come già accennato, incontrerai problemi nell'eliminazione della struttura della stringa quando il valore restituito non è un puntatore alla stringa. Devi apportare alcune delicate modifiche al puntatore.


Codice che funziona solo con lo standard C99

In C99, la sezione 6.7.2.1, paragrafo 16, descrive i "membri dell'array flessibile":

  

Come caso speciale, può essere l'ultimo elemento di una struttura con più di un membro nominato   avere un tipo di array incompleto; questo è chiamato un membro flessibile dell'array. Con due   eccezioni, il membro flessibile dell'array viene ignorato. Innanzitutto, la dimensione della struttura deve essere   uguale all'offset dell'ultimo elemento di una struttura altrimenti identica che sostituisce il   membro dell'array flessibile con un array di lunghezza non specificata. 106) Secondo, quando a. (oppure - >)   l'operatore ha un operando di sinistra che è (un puntatore a) una struttura con un membro di array flessibile   e l'operando giusto nomina quel membro, si comporta come se quel membro fosse sostituito   con l'array più lungo (con lo stesso tipo di elemento) che non renderebbe la struttura   più grande dell'oggetto a cui si accede; lo scostamento dell'array deve rimanere quello dell'array   membro dell'array flessibile, anche se diverso da quello dell'array sostitutivo. Se questo   l'array non avrebbe elementi, si comporta come se avesse un elemento ma il comportamento è   indefinito se si tenta di accedere a quell'elemento o di generare un puntatore in passato   esso.

     

106 La lunghezza non è specificata per consentire il fatto che le implementazioni possano dare ai membri dell'array diversi   allineamenti secondo le loro lunghezze.

Utilizzando un "membro flessibile dell'array", il codice potrebbe diventare:

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;
}

Questo codice è stato accettato come pulito da GCC 4.0.1 a parte una dichiarazione per la funzione (opzioni -Wall -Wextra ). Il codice precedente richiede un cast su 'String * string = (String *) space;' per dire al compilatore intendevo quello che ho detto; Ho risolto il problema e ho lasciato un commento per mostrare l'originale.


Utilizzo di "struct hack"

Prima di C99, le persone usavano spesso lo 'hack hack' per gestirlo. È molto simile al codice mostrato nella domanda, tranne per il fatto che la dimensione dell'array è 1, non 0. Lo standard C non consente dimensioni dell'array di dimensioni zero.

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;
}

Codice che utilizza un'estensione non standard di GCC per C89 e C99

La notazione di array di dimensioni zero è accettata da GCC a meno che non sia difficile da colpire: specificare lo standard ISO C e richiedere l'accuratezza pedante. Questo codice, pertanto, viene compilato correttamente a meno che non si utilizzi 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;
}

Tuttavia, non dovresti essere addestrato in estensioni non standard al linguaggio C prima di avere una conoscenza approfondita delle basi dello standard C. Questo è semplicemente ingiusto per te; non puoi dire se ciò che ti viene detto di fare sia ragionevole, ma i tuoi tutor non dovrebbero indurti in errore costringendoti a usare cose non standard. Anche se ti hanno avvisato del fatto che non è standard, non è giusto per te. C è abbastanza difficile da imparare senza imparare cose complicate che sono in qualche modo specifiche del compilatore.

Altri suggerimenti

Hai allocato un po 'di memoria sull'heap, ma non la stai usando come se fosse la tua struttura. La variabile string nella tua funzione è di tipo char * , non di tipo struct String . Penso che stai duplicando le funzionalità di strdup () abbastanza ragionevolmente, ma non capisco il motivo della struttura.

Nota: probabilmente dovresti controllare la tua chiamata a malloc () per errore e tornare in modo appropriato. La pagina man di strdup () dovrebbe spiegare esattamente cosa dovrebbe fare la tua funzione.

Hai. Malloc, new, etc usano tutti l'heap.

Sì, malloc restituisce memoria sull'heap.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top