Løsning for "dereferencing` tomrommet *' pointer" advarsel i struct i C?

stemmer
1

Jeg prøvde å lage en pseudo super struct å skrive ut rekke structs. Min grunnleggende strukturer er som følger.

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

...

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

Jeg opprettet under struct å skrive ut rekken av ovennevnte strukturer. Jeg fikk dereferencing ugyldig peker feil under kompilering under kodebiten.

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]);
...

Min intensjon er å ha en felles funksjon for å skrive ut alle arrays. Vennligst la meg vite den riktige måten å bruke det.

Setter pris på hjelpen på forhånd.

Edit: Parameteren navn endres til cmncntin fra cmncnt. Beklager det var skrivefeil feil.

Takk, Mathew Liju

Publisert på 14/11/2008 klokken 05:45
kilden bruker
På andre språk...                            


10 svar

stemmer
5

Jeg tror din design kommer til å mislykkes, men jeg er også overbevist om at de andre svarene jeg ser fullt håndtere de dypere årsakene.

Det ser ut til at du prøver å bruke C for å håndtere generiske typer, noe som alltid kommer til å være hårete. Du kan gjøre det, hvis du er forsiktig, men det er ikke lett, og i dette tilfellet, jeg tviler på om det ville være verdt.

Dypere Grunn : La oss anta at vi kommer forbi bare syntaktisk (eller knapt mer enn syntaktiske) problemer. Koden viser at T10CNT inneholder 20 intog T20CNT inneholder 20 long. På moderne 64-bits maskiner - annet enn i henhold Win64 - sizeof(long) != sizeof(int). Derfor bør koden inni utskriftsfunksjonen skal skille mellom dereferencing intmatriser og longarrays. I C ++, det er en regel at du ikke bør prøve å behandle matriser polymorft, og denne typen ting er hvorfor. Den CMNCNT typen inneholder 3 long-verdier; forskjellig fra både T10CNT og T20CNT strukturer i antall, selv om basetype i matrisen sams T20CNT.

Stil Anbefaling : Jeg anbefaler på det sterkeste å unngå ledende understreking på navn. Generelt er navn som begynner med understreking reservert for gjennomføringen å bruke, og å bruke som makroer. Makroer har ingen respekt for omfang; dersom gjennomføringen definerer en makro _cnt det ville vraket koden din. Det er nyanser til hva navnene er reservert; Jeg er ikke i ferd med å gå inn i disse nyansene. Det er mye enklere å tenke 'navn som starter med understrek er reservert', og det vil styre deg unna trøbbel.

Stil Forslag : Din print funksjonen returnerer suksess betingelsesløst. Det er ikke fornuftig; din funksjonen skal returnere noe, slik at den som ringer ikke trenger å teste for å lykkes eller mislykkes (siden det aldri kan mislykkes). En forsiktig koder som observerer at funksjonen returnerer en status vil alltid teste returstatus, og har feilhåndtering kode. At koden vil aldri bli henrettet, så det er død, men det er vanskelig for noen (eller kompilatoren) for å bestemme det.

Surface Fix : Midlertidig, kan vi anta at du kan behandle intog longsom synonymer; men du må få ut av vane å tenke at de er synonymer, skjønt. Den void *Argumentet er den riktige måten å si "denne funksjonen tar en peker av ubestemmelig type". Men inne i funksjon, må du konvertere fra en void *til en bestemt type før du gjør indeksering.

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

(Jeg liker tanken på en filstrøm heter stoutogså. Forslag : Bruk cut'n'paste på fast kildekode - det er tryggere jeg vanligvis bruker! " sed 's/^/ /' file.c" For å forberede koden for cut'n'paste inn en SO svar .)

Hva betyr det støpt linjen gjøre? Jeg er glad du spurte ...

  • Den første operasjon er å omdanne den const void *til en const char *; Dette kan du gjøre byte-størrelse operasjoner på adressen. På dagene før Standard C, char *ble anvendt i stedet for void *som den universelle adresseringsmekanismen.
  • Den neste operasjon legger riktig antall bytes for å komme til starten av ith element i rekken av gjenstander av størrelse elemsize.
  • Den andre kastet så forteller kompilatoren "stol på meg - jeg vet hva jeg gjør" og "behandle denne adressen som adressen til en CMNCNT struktur".

Derfra er lett nok koden. Merk at siden CMNCNT strukturen inneholder longverdi, jeg pleide %ldå fortelle sannheten til fprintf().

Siden du ikke er i ferd med å endre data i denne funksjonen, er det ikke en dårlig idé å bruke constkvalifiseringskamp som jeg gjorde.

Merk at hvis du kommer til å være trofast mot sizeof(long) != sizeof(int), så du trenger to separate blokker av kode (jeg vil foreslå separate funksjoner) å forholde seg til den 'rekke int' og 'rekke long' strukturtypene.

Svarte 15/11/2008 kl. 16:01
kilden bruker

stemmer
2

Den type tomrommet er bevisst forlatt ufullstendig. Fra dette følger det at du ikke kan dereference ugyldig pekere, og verken du kan ta sizeof av det. Dette betyr at du kan ikke bruke senket operatøren å bruke det som en matrise.

I det øyeblikket du tildele noe til en ugyldig peker, alle typer informasjon om den opprinnelige pekte å skrive er tapt, slik at du kan bare deferanseoperasjon hvis du først kastet den tilbake til den opprinnelige pekertypen.

Først og viktigst, passerer du T10CNT*til funksjonen, men du prøver å typecast (og deferanseoperasjon) som til CMNCNT*i funksjon. Dette er ikke gyldig og udefinert atferd.

Du trenger en funksjon printCommonStatistics for hver type oppstillingselementene. Så har en printCommonStatisticsInt, printCommonStatisticsLong, printCommonStatisticsCharsom alle skiller seg ved sin første argumentet (en tar int*, den andre tar long*, og så videre). Du kan lage dem ved hjelp av makroer, for å unngå overflødig kode.

Bestått struct seg selv er ikke en god idé, siden da må du definerer en ny funksjon for hver annen størrelsen på inne utvalg i struct (siden de er alle forskjellige typer). Så bedre passere inneholdt rekke direkte ( struct_array[0]._cnt, kaller funksjonen for hver indeks)

Svarte 14/11/2008 kl. 13:36
kilden bruker

stemmer
1

Funksjonen

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

Fungerer for meg.

Problemet er at på linjen kommenterte "Ptr Line" koden legger en peker til et heltall. Siden vår pekeren er en char * vi beveger oss fremover i minnet sizeof (char) * ii * cmncnt_elemsize, som er det vi ønsker da en røye er en byte. Din kode prøvde å gjøre en tilsvarende ting går fremover sizeof (void) * ii * cmncnt_elemsize, men tomrommet ikke har en størrelse, så kompilatoren ga deg feil.

Jeg ville endre T10CNT og T20CNT til både bruk int eller lang i stedet for en med hver. Du avhengig sizeof (int) == sizeof (lang)

Svarte 15/11/2008 kl. 15:48
kilden bruker

stemmer
1

Du kan ikke gjøre dette:

cmncnt->_cnt[0]

hvis cmnct er et tomrom peker.

Du må angi typen. Du må kanskje revurdere implementeringen.

Svarte 14/11/2008 kl. 05:58
kilden bruker

stemmer
1

Endre funksjonen erklæring til char * slik:

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

hulromtypen ikke forutsetter en bestemt rammestørrelse, mens en char vil anta en byte størrelse.

Svarte 14/11/2008 kl. 05:54
kilden bruker

stemmer
0

Point of Information: Intern Padding kan virkelig skru opp dette.

Betrakt struct {char c [6]; }; - Det har sizeof () = 6. Men hvis du hadde en rekke av disse, kan hvert element være polstret ut en 8 byte justering!

Visse monteringsoperasjoner håndterer ikke skråstilt data elegant måte. (For eksempel, hvis en int spenner over to minne ord.) (JA jeg har blitt bitt av dette før.)

.

Second: I det siste har jeg brukt løst størrelse arrays. (Jeg var dum da ...) Det fungerer hvis du ikke endrer type. (Eller hvis du har en union av typene.)

Eg:

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

disponeres som

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

(Selv om padding / justering kan fortsatt skru deg opp.)

.

Tredje: Tenk:

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

Det løser problemer med å vite hvordan du skriver ut dataene.

.

Fjerde: Du kan bare passere i int / lang rekke til din funksjon i stedet for strukturen. Eg:

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

Aktiveres via:

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

Eller:

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

Dette fungerer mye bedre enn å skjule data i structs. Som du legge til medlemmer i en av struct-tallet, kan oppsettet endres mellom struct og ikke lenger være konsekvent. (Betydning adressen _cnt forhold til starten av struct kan endre seg for _T10CNT og ikke for _T20CNT. Morsomme debugging ganger der. En enkelt struct med en union'ed _cnt nyttelast ville unngå dette.)

Eg:

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

.

Femte: Hvis du må bruke structs ... C ++, iostreams, og templating ville være mye renere å implementere.

Eg:

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. */

Men det er nok ikke det du leter etter ...

Svarte 15/11/2008 kl. 18:17
kilden bruker

stemmer
0

uttrykket

(CMNCNT *)&cmncntin[ii*cmncnt_elmsize]

forsøker å ta adressen til cmncntin [ii * cmncnt_elmsize] og deretter støpt som peker til type (CMNCNT *). Det kan ikke få adressen cmncntin [ii * cmncnt_elmsize] fordi cmncntin har type ugyldig *.

Studien C operatør precedences og sett parentes der det er nødvendig.

Svarte 14/11/2008 kl. 08:32
kilden bruker

stemmer
0

På denne linjen:

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

Du prøver å erklære en ny variabel kalt cmncnt, men en variabel med dette navnet finnes allerede som en parameter til funksjonen. Du vil kanskje bruke en annen variabel navn for å løse dette.

Også kan det være lurt å passere en peker til en CMNCNT til funksjonen i stedet for et tomrom peker, fordi da kompilatoren vil gjøre pekeren aritmetiske for deg og du trenger ikke å kaste den. Jeg ser ikke poenget med å sende en ugyldig peker når alt du gjør med den er kastet den til en CMNCNT. (Som ikke er et veldig beskrivende navn for en datatype, forresten.)

Svarte 14/11/2008 kl. 05:52
kilden bruker

stemmer
-1

Denne linjen er slags torturert, don'tcha tror?

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

Hva med noe mer som

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

Eller enda bedre, hvis cmncnt_elmsize = sizeof (CMNCNT)

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

Det burde også bli kvitt advarsel, siden du ikke lenger er dereferencing et tomrom *.

BTW: Jeg er ikke virkelig sikker på hvorfor du gjør det på denne måten, men hvis cmncnt_elmsize er noen ganger ikke sizeof (CMNCNT), og kan faktisk variere fra samtale til samtale, vil jeg foreslå å tenke nytt om dette design. Jeg antar at det kan være en god grunn for det, men det ser virkelig skjelven til meg. Jeg kan nesten garantere at det er en bedre måte å designe ting.

Svarte 14/11/2008 kl. 14:49
kilden bruker

stemmer
-1

C er ikke min kopp o'java, men jeg tror problemet ditt er at "tomrommet * cmncnt" bør CMNCNT * cmncnt.

Føl deg fri til å korrigere meg nå, C-programmerere, og fortell meg at dette er grunnen til at java programmerere ikke kan ha fine ting.

Svarte 14/11/2008 kl. 05:52
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more