Domanda

Stavo cercando di creare una pseudo superstruttura per stampare array di strutture. La mia base le strutture sono le seguenti.

/* Type 10 Count */
typedef struct _T10CNT
{
    int _cnt[20];
} T10CNT;

...

/* Type 20 Count */
typedef struct _T20CNT
{
    long _cnt[20];
} T20CNT;
...

Ho creato la seguente struttura per stampare l'array delle strutture sopra menzionate. Ho ricevuto la dereferenziazione dell'errore del puntatore vuoto durante la compilazione dello snippet di codice seguente.

typedef struct _CMNCNT
{
    long  _cnt[3];
} CMNCNT;

static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
    int ii;
    for(ii=0; ii<cmncnt_nelem; ii++)
    {
        CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];
        fprintf(stout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
        fprintf(stout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]); 
        fprintf(stout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
    }
    return SUCCESS;
}

T10CNT struct_array[10];
...
printCommonStatistics(struct_array, NELEM(struct_array), sizeof(struct_array[0]);
...

La mia intenzione è quella di avere una funzione comune per stampare tutti gli array. Per favore fatemi sapere il modo corretto di usarlo.

Apprezzo in anticipo l'aiuto.

Modifica: il nome del parametro viene cambiato in cmncntin da cmncnt. Mi dispiace, è stato un errore di battitura.

Grazie, Mathew Liju

È stato utile?

Soluzione

Penso che il tuo progetto fallirà, ma non sono nemmeno convinto che le altre risposte che vedo affrontino completamente le ragioni più profonde del perché.

Sembra che tu stia cercando di usare C per gestire tipi generici, qualcosa che diventa sempre peloso. Puoi farlo, se stai attento, ma non è facile, e in questo caso, dubito che varrebbe la pena.

Motivo più profondo : supponiamo di superare i semplici problemi sintattici (o poco più che sintattici). Il tuo codice mostra che T10CNT contiene 20 int e T20CNT contiene 20 long . Sui moderni computer a 64 bit - diversi da Win64 - sizeof (long)! = Sizeof (int) . Pertanto, il codice all'interno della funzione di stampa dovrebbe distinguere tra matrici int e matrici lunghe . In C ++, c'è una regola che non dovresti provare a trattare polimorficamente le matrici, e questo genere di cose è il motivo. Il tipo CMNCNT contiene 3 valori long ; diverso in numero dalle strutture T10CNT e T20CNT, sebbene il tipo base dell'array corrisponda a T20CNT.

Raccomandazione di stile : consiglio vivamente di evitare i trattini bassi sui nomi. In generale, i nomi che iniziano con il trattino basso sono riservati per l'implementazione da usare e da usare come macro. Le macro non hanno rispetto per l'ambito; se l'implementazione definisce una macro _cnt, rovinerebbe il tuo codice. Ci sono sfumature a quali nomi sono riservati; Non sto per entrare in quelle sfumature. È molto più semplice pensare che i nomi che iniziano con il carattere di sottolineatura siano riservati e ti eviterà i problemi.

Suggerimenti sullo stile : la tua funzione di stampa restituisce il successo incondizionatamente. Non è ragionevole; la tua funzione non dovrebbe restituire nulla, in modo che il chiamante non debba testare il successo o il fallimento (poiché non può mai fallire). Un programmatore attento che osserva che la funzione restituisce uno stato verificherà sempre lo stato di ritorno e avrà un codice di gestione degli errori. Quel codice non verrà mai eseguito, quindi è morto, ma è difficile per chiunque (o il compilatore) determinarlo.

Correzione di superficie : temporaneamente, possiamo presumere che tu possa trattare int e long come sinonimi; ma devi liberarti dall'abitudine di pensare che siano sinonimi. L'argomento void * è il modo corretto di dire " questa funzione prende un puntatore di tipo indeterminato " ;. Tuttavia, all'interno della funzione, è necessario convertire da void * a un tipo specifico prima di eseguire l'indicizzazione.

typedef struct _CMNCNT
{
    long    count[3];
} CMNCNT;

static void printCommonStatistics(const void *data, size_t nelem, size_t elemsize)
{
    int i;
    for (i = 0; i < nelem; i++)
    {
        const CMNCNT *cmncnt = (const CMNCNT *)((const char *)data + (i * elemsize));
        fprintf(stdout,"STATISTICS_INP: %ld\n", cmncnt->count[0]);
        fprintf(stdout,"STATISTICS_OUT: %ld\n", cmncnt->count[1]); 
        fprintf(stdout,"STATISTICS_ERR: %ld\n", cmncnt->count[2]);
    }
}

(Mi piace anche l'idea di un flusso di file chiamato stout . Suggerimento : usa cut'n'paste sul vero codice sorgente - è più sicuro! I ' generalmente utilizzo " sed 's / ^ / /' file.c " per preparare il codice per tagliare e aggiungere una risposta SO.)

Cosa fa quella linea di cast? Sono contento che tu l'abbia chiesto ...

  • La prima operazione è convertire il const void * in const char * ; questo ti permette di fare operazioni di dimensione byte sull'indirizzo. Nei giorni precedenti lo standard C, char * veniva usato al posto di void * come meccanismo di indirizzamento universale.
  • L'operazione successiva aggiunge il numero corretto di byte per arrivare all'inizio dell'elemento i dell'array di oggetti di dimensione elemsize .
  • Il secondo cast quindi dice al compilatore "fidati di me - so cosa sto facendo " e "tratta questo indirizzo come l'indirizzo di una struttura CMNCNT".

Da lì, il codice è abbastanza semplice. Si noti che poiché la struttura CMNCNT contiene long , ho usato % ld per dire la verità a fprintf () .

Dato che non hai intenzione di modificare i dati in questa funzione, non è una cattiva idea usare il qualificatore const come ho fatto io

Nota che se stai andando

Altri suggerimenti

Il tipo di vuoto è deliberatamente lasciato incompleto. Da ciò, ne consegue che non è possibile dereferenziare i puntatori vuoti, e nemmeno si può prendere la dimensione di esso. Ciò significa che non è possibile utilizzare l'operatore di sottoscrizione utilizzandolo come un array.

Nel momento in cui si assegna qualcosa a un puntatore vuoto, qualsiasi informazione di tipo dell'originale puntato al tipo viene persa, quindi è possibile determinare la differenza solo se si esegue il cast per la prima volta al tipo di puntatore originale.

Primo e il più importante, passi T10CNT * alla funzione, ma provi a digitare (e dereference) quello a CMNCNT * nella tua funzione. Questo non è un comportamento valido e indefinito.

È necessaria una funzione printCommonStatistics per ogni tipo di elementi dell'array. Quindi, avere un printCommonStatisticsInt , printCommonStatisticsLong , printCommonStatisticsChar che differiscono tutti per il loro primo argomento (uno che prende int * , l'altro che prende < codice> lungo * e così via). È possibile crearli utilizzando le macro, per evitare il codice ridondante.

Passare la struttura stessa non è una buona idea, poiché da allora è necessario definire una nuova funzione per ogni diversa dimensione dell'array contenuto all'interno della struttura (poiché sono tutti tipi diversi). Quindi meglio passare direttamente l'array contenuto ( struct_array [0] ._ cnt , chiamare la funzione per ciascun indice)

Cambia la dichiarazione di funzione in char * in questo modo:

static int printCommonStatistics(char *cmncnt, int cmncnt_nelem, int cmncnt_elmsize)

il tipo vuoto non assume alcuna dimensione particolare mentre un carattere assumerà una dimensione in byte.

Non puoi farlo:

cmncnt->_cnt[0]

se cmnct è un puntatore vuoto.

Devi specificare il tipo. Potrebbe essere necessario ripensare la tua implementazione.

La funzione

static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
    char *cmncntinBytes;
    int ii;

    cmncntinBytes = (char *) cmncntin;
    for(ii=0; ii<cmncnt_nelem; ii++)
    {
        CMNCNT *cmncnt = (CMNCNT *)(cmncntinBytes + ii*cmncnt_elmsize);  /* Ptr Line */
        fprintf(stdout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
        fprintf(stdout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]); 
        fprintf(stdout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
    }
    return SUCCESS;
}

Funziona per me.

Il problema è che sulla riga commentata " Ptr Line " il codice aggiunge un puntatore a un numero intero. Poiché il nostro puntatore è un carattere *, avanziamo nella dimensione della memoria di (carattere) * ii * cmncnt_elemsize, che è ciò che vogliamo poiché un carattere è un byte. Il tuo codice ha provato a fare una cosa equivalente spostando avanti sizeof (void) * ii * cmncnt_elemsize, ma void non ha una dimensione, quindi il compilatore ti ha dato l'errore.

Vorrei cambiare T10CNT e T20CNT per usare int o long invece di uno con ciascuno. Dipende da sizeof (int) == sizeof (long)

Su questa linea:

CMNCNT *cmncnt = (CMNCNT *)&cmncnt[ii*cmncnt_elmsize];

Stai provando a dichiarare una nuova variabile chiamata cmncnt, ma esiste già una variabile con questo nome come parametro per la funzione. Potresti voler usare un nome di variabile diverso per risolverlo.

Inoltre potresti voler passare un puntatore a un CMNCNT alla funzione anziché un puntatore vuoto, perché il compilatore eseguirà l'aritmetica del puntatore per te e non dovrai lanciarlo. Non vedo il punto di passare un puntatore vuoto quando tutto ciò che fai con esso è cast su un CMNCNT. (A proposito, non è un nome molto descrittivo per un tipo di dati.)

La tua espressione

(CMNCNT *)&cmncntin[ii*cmncnt_elmsize]

prova a prendere l'indirizzo di cmncntin [ii * cmncnt_elmsize] e quindi lancia quel puntatore per digitare (CMNCNT *). Impossibile ottenere l'indirizzo di cmncntin [ii * cmncnt_elmsize] perché cmncntin ha il tipo void *.

Studia le precedenti dell'operatore C e inserisci le parentesi dove necessario.

Punto di informazione: l'imbottitura interna può davvero rovinare tutto.

Prendi in considerazione struct {char c [6]; }; - Ha sizeof () = 6. Ma se avessi una serie di questi, ogni elemento potrebbe essere completato con un allineamento di 8 byte!

Alcune operazioni di assemblaggio non gestiscono i dati con errori di allineamento. (Ad esempio, se un int comprende due parole di memoria.) (SÌ, mi è stato morso prima.)

.

Secondo: in passato ho usato array di dimensioni variabili. (Ero stupido allora ...) Funziona se non stai cambiando tipo. (O se hai un'unione dei tipi.)

per esempio:.

struct T { int sizeOfArray;  int data[1]; };

Assegnato come

T * t = (T *) malloc( sizeof(T) + sizeof(int)*(NUMBER-1) );
                      t->sizeOfArray = NUMBER;

(Anche se l'imbottitura / allineamento può ancora rovinarti.)

.

Terzo: considerare:

   struct T {
     int sizeOfArray;
     enum FOO arrayType;
     union U { short s; int i; long l; float f; double d; } data [1];
    };

Risolve problemi nel sapere come stampare i dati.

.

Quarto: potresti semplicemente passare l'array int / long alla tua funzione piuttosto che alla struttura. Per esempio:

void printCommonStatistics( int * data, int count )
{
  for( int i=0;  i<count;  i++ )
    cout << "FOO: " << data[i] << endl;
}

Richiamato tramite:

_T10CNT  foo;
printCommonStatistics( foo._cnt, 20 );

o

 int a[10], b[20], c[30];
printCommonStatistics( a, 10 );
printCommonStatistics( b, 20 );
printCommonStatistics( c, 30 );

Funziona molto meglio che nascondere i dati nelle strutture. Man mano che aggiungi membri a una delle tue strutture, il layout può cambiare tra le tue strutture e non essere più coerente. (Significa che l'indirizzo di _cnt relativo all'inizio della struttura può cambiare per _T10CNT e non per _T20CNT. Divertenti tempi di debug lì. Una singola struttura con un payload _cnt unito al sindacato lo eviterebbe.)

per esempio:.

struct FOO {
  union {
         int     bar  [10];
          long biff [20];
   } u;
}

.

Quinto: Se devi usare le strutture ... C ++, iostreams e templating sarebbero molto più puliti da implementare.

per esempio:.

template<class TYPE> void printCommonStatistics( TYPE & mystruct, int count )
{
  for( int i=0;  i<count;  i++ )
    cout << "FOO: " << mystruct._cnt[i] << endl;
}      /* Assumes all mystruct's have a "_cnt" member. */

Ma probabilmente non è quello che stai cercando ...

C non è la mia tazza di java, ma penso che il tuo problema sia che "void * cmncnt" dovrebbe essere CMNCNT * cmncnt.

Sentiti libero di correggermi ora, programmatori C, e dimmi che è per questo che i programmatori Java non possono avere cose carine.

Questa linea è un po 'torturata, non credi?

CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];

Che ne dici di qualcosa di più simile a

CMNCNT *cmncnt = ((CMNCNT *)(cmncntin + (ii * cmncnt_elmsize));

O meglio ancora, se cmncnt_elmsize = sizeof (CMNCNT)

CMNCNT *cmncnt = ((CMNCNT *)cmncntin) + ii;

Anche questo dovrebbe eliminare l'avvertimento, poiché non stai più dereferenziando un vuoto *.

A proposito: non sono davvero sicuro del perché lo stai facendo in questo modo, ma se cmncnt_elmsize a volte non è sizeof (CMNCNT), e in effetti può variare da chiamata a chiamata, suggerirei di ripensare questo disegno. Suppongo che potrebbe esserci una buona ragione per questo, ma mi sembra davvero traballante. Posso quasi garantire che esiste un modo migliore per progettare le cose.

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