2012-12-03 14 views
15

Si prega di considerare il seguente codice.Lanciare tra il puntatore struct in C

typedef struct{ 
    int field_1; 
    int field_2; 
    int field_3; 
    int field_4; 

    uint8_t* data; 
    uint32_t data_size; 
} my_struct; 

void ext_function(inalterable_my_struct* ims, ...); 

voglio permettere ext_function (scritto da parte di terzi) per modificare solo field_3 e field_4 in my_struct. Così faccio la seguente:

typedef struct{ 
    const int field_1; 
    const int field_2; 
    int field_3; 
    int field_4; 

    const uint8_t* data; 
    const uint32_t data_size; 
} inalterable_my_struct; 

void ext_function(inalterable_my_struct* ims, ...); 

E 'sicuro per lanciare puntatori tra my_struct e inalterable_my_struct prima di chiamare ext_function (come mostrato dopo)?

void call_ext_function(my_struct* ms){ 
    inalterable_my_struct* ims = (inalterable_my_struct*)ms; 
    ext_function(ims, ...); 
} 
+0

Se non si desidera 'ext_function' modificare' data', si dovrebbe dichiareremo come 'uint8_t const * dati const;' – Henrik

risposta

7

Non penso che sia una buona idea.

La funzione chiamata può sempre escludere qualsiasi const: ness e modificare i dati se lo desidera.

Se è possibile controllare i punti di chiamata, sarebbe meglio per creare una copia e chiamare la funzione con un puntatore alla copia, quindi copiare di nuovo i due campi che ti interessano:

void call_ext_function(my_struct* ms) 
{ 
    my_struct tmp = *ms; 
    ext_function(&tmp, ...); 
    ms->field_3 = tmp.field_3; 
    ms->field_4 = tmp.field_4; 
} 

molto più pulito, e a meno che non lo facciate migliaia di volte al secondo, la penalizzazione delle prestazioni dovrebbe essere davvero minima.

Potrebbe essere necessario simulare anche i dati basati su puntatore, se la funzione lo tocca.

-2

Probabilmente funzionerà sulla maggior parte dei computer anche se lo standard non dice nulla al riguardo. Probabilmente puoi anche fare qualcosa di più portatile con un sindacato, se proprio devi. Tranne che non cambierà nulla.

Questo è il motivo per cui non cambierà nulla:

$ cat foo.c 
struct foo { 
    const int a; 
    int b; 
}; 

void 
foo(struct foo *foo) 
{ 
    foo->a = 1; 
} 
$ cc -c foo.c 
foo.c: In function ‘foo’: 
foo.c:9: error: assignment of read-only member ‘a’ 
$ cc -Dconst= -c foo.c 
$ 
-2

Scrivendo ai membri che utilizzate per essere const non può essere sicuro. Potrebbero essere stati inseriti nella memoria di sola lettura dal compilatore/linker e dal sistema operativo. Vedi How is read-only memory implemented in C?.

4

Secondo lo standard C99, due struct s non avrebbero tipi compatibili anche se le loro dichiarazioni erano identiche. Dalla sezione 6.7.7.5:

ESEMPIO 2 Dopo le dichiarazioni

typedef struct s1 { int x; } t1, *tp1; 
typedef struct s2 { int x; } t2, *tp2; 

tipo t1 e il tipo puntato da tp1 sono compatibili. Il tipo t1 è anche compatibile con il tipo struct s1, ma non compatibile con i tipi struct s2, t2, il tipo indicato da tp2 o int.

Inoltre, due tipi con differenti qualificatori non sono considerati compatibili:

Per due tipi qualificati per essere compatibili, sia provvede alla identicamente qualificata di tipo compatibile; l'ordine di tipo qualificatori in un elenco di identificatori o qualificatori non influenza il tipo specificato.

Un approccio più pulito sarebbe quello di nascondere il struct tutto, sostituirlo con un manico oscuro (un typedef in cima void*) e fornire funzioni per manipolare gli elementi della struct. In questo modo si manterrebbe il controllo completo sopra la struttura del struct: si sarebbe in grado di rinominare i suoi campi a piacimento, modificare il layout tanto e tutte le volte che lo si desidera, cambiare alla base tipi di campi, e fare altre cose che si normalmente evita quando il layout interno di struct è noto ai tuoi clienti.

2

non penso che sia una buona idea, perché è difficile tenere traccia se la struttura è stata conteggiata o meno (soprattutto se il codice è di grandi dimensioni). Anche la fusione in const non garantisce che non sarà gettato in una non-const structure tardi.

La soluzione fornita da svolgimento è molto buona. Una soluzione alternativa (e più ovvia) sarebbe quella di dividere la struttura in due parti più piccole.

typedef struct{ 
    const int field_1; 
    const int field_2; 
    const uint8_t* data; 
    const uint32_t data_size; 
} inalterable_my_struct; 

typedef struct{ 
    int field_3; 
    int field_4; 
} my_struct; 

void ext_function(const inalterable_my_struct* const ims, my_struct* ms ...); 

Ho fatto il puntatore anche costante nella chiamata sopra, ma che non è necessario.

Problemi correlati