Er en struct pekere garantert å være representert uten polstring biter?

stemmer
2

Jeg har en lenket liste, som lagrer grupper av innstillinger for min søknad:

typedef struct settings {
  struct settings* next;
  char* name;
  char* title;
  char* desc;
  char* bkfolder;
  char* srclist;
  char* arcall;
  char* incfold;
} settings_row;
settings_row* first_profile = { 0 };

#define SETTINGS_PER_ROW 7

Når jeg legger verdiene inn i denne strukturen, jeg ønsker ikke å ha for å nevne alle elementene. Jeg vil heller behandle det som et navngitt array - verdiene blir lastet inn i rekkefølge fra en fil og plassert trinnvis inn i struct. Deretter, når jeg trenger å bruke verdiene, tilgang jeg dem ved navn.

//putting values incrementally into the struct
void read_settings_file(settings_row* settings){
    char* field = settings + sizeof(void*);
    int i = 0;
    while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW);
}

//accessing components by name
void settings_info(settings_row* settings){
    printf(Settings 'profile': %s\n, settings.title);
    printf(Description: %s\n, settings.desc);
    printf(Folder to backup to: %s\n, settings.bkfolder);
}

Men jeg lurer på, siden disse er alle pekere (og det vil bare noen gang være tips i denne struct), vil kompilatoren legge padding på noen av disse verdiene? Er de garantert å være i denne rekkefølgen, og har ingenting mellom verdiene? Vil min tilnærming fungerer noen ganger, men mislykkes midlertidig?

redigere for avklaring

Jeg innser at kompilatoren kan puten noen verdier av en struct - men på grunn av innholdet av struct (en struct pekere) Jeg tenkte dette kan ikke være et problem. Siden den mest effektive måten for en 32 bits prosessor for å håndtere dataene er i 32 bit biter, dette er hvordan kompilatoren pads verdier i en struct (ie. En int, kort, int i en struct vil legge til 2 bytes padding etter kort , for å gjøre den om til en 32 bit blokk, og justere det neste int til den neste 32 bit del). Men siden en 32 bits prosessor bruker bits adresser 32 (og en 64 bits prosessor bruker bits adresser 64 (tror jeg)), ville padding være helt unødvendig siden alle verdiene i struct (adresser, som er effektive i sin natur) er i ideell 32 bit biter?

Jeg håper noen minne-representasjon / kompilator-atferd guru kan komme kaste lys over hvorvidt en kompilator noensinne ville ha en grunn til å pad disse verdiene

Publisert på 25/06/2009 klokken 23:01
kilden bruker
På andre språk...                            


8 svar

stemmer
4

Under POSIX regler, er alle pekere (både funksjonspekere og datapekere) alt som kreves for å være av samme størrelse; etter bare ISO C, alle data pekere er konvertible til ' void *' og tilbake uten tap av informasjon (men funksjonspekere trenger ikke være konvertible til ' void *' uten tap av informasjon, eller vice versa).

Derfor, hvis skrevet riktig, vil koden fungere. Det er ikke skrevet helt riktig, selv om! Ta i betraktning:

void read_settings_file(settings_row* settings)
{
    char* field = settings + sizeof(void*);
    int i = 0;
    while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW)
        ;
}

La oss anta at du bruker en 32-bit maskin med 8-bits tegn; argumentet er ikke alle som signifikant forskjellig hvis du bruker 64-bits maskiner. Oppgaven til ' field' er alle feil, fordi settings + 4er en peker til det femte element (regnet fra 0) av en oppstilling av ' settings_row' strukturer. Hva du trenger å skrive er:

void read_settings_file(settings_row* settings)
{
    char* field = (char *)settings + sizeof(void*);
    int i = 0;
    while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW)
        ;
}

Skuespillerne før tilsetting er viktig!


C-standard (ISO / IEC 9899: 1999):

6.3.2.3 Pekere

En peker til å annullere kan omdannes til eller fra en peker til enhver ufullstendig eller objekttype. En peker til enhver ufullstendig eller objekttype kan omdannes til en peker for å annullere og tilbake igjen; Resultatet skal sammenligne lik den opprinnelige pekeren.

[...]

En peker til en funksjon av en type kan omdannes til en peker til en funksjon av en annen type og tilbake igjen; Resultatet skal sammenligne lik den opprinnelige pekeren. Hvis en ombygd pekeren blir brukt til å kalle en funksjon som type er ikke kompatibel med den spisse-å skrive, er atferden udefinert.

Svarte 25/06/2009 kl. 23:20
kilden bruker

stemmer
4

I mange tilfeller pekere er naturlige ordet størrelser, så kompilatoren vil neppe pad hvert medlem, men som ikke gjør det en god idé. Hvis du ønsker å behandle det som en matrise bør du bruke en matrise.

Jeg tenker høyt her, så det er nok mange feil, men kanskje du kan prøve denne metoden:

enum
{
    kName = 0,
    kTitle,
    kDesc,
    kBkFolder,
    kSrcList,
    kArcAll,
    kIncFold,
    kSettingsCount
};

typedef struct settings {
    struct settings* next;
    char *settingsdata[kSettingsCount];
} settings_row;

Sett data:

settings_row myRow;
myRow.settingsData[kName] = "Bob";
myRow.settingsData[kDescription] = "Hurrrrr";
...

Lese data:

void read_settings_file(settings_row* settings){
    char** field = settings->settingsData;
    int i = 0;
    while(read_value_into(field[i]) && i++ < SETTINGS_PER_ROW);
}
Svarte 25/06/2009 kl. 23:12
kilden bruker

stemmer
2

Det er ikke garantert av C-standarden. Jeg har en snikende mistanke om at jeg ikke har tid til å sjekke akkurat nå uansett, at den garanterer ingen polstring mellom char * felt, det vil si at sammenhengende felt av samme type i en struct er garantert å være layout-kompatibel med en matrise av den typen. Men selv om det, er du på egen hånd mellom innstillingene * og den første char *, og også mellom siste char * og slutten av struct. Men du kan bruke offsetoffor å håndtere den første utgaven, og jeg tror ikke det andre påvirker din nåværende kode.

Men hva du vil er nesten helt sikkert garantert av kompilatoren, som et sted i dokumentasjonen vil sette ut sine regler for struct layout, og vil ganske sikkert si at alle pekere til data er ordet størrelse, og at en struct kan være på størrelse med 8 ord uten ekstra polstring. Men hvis du ønsker å skrive svært portabel kode, må du bare bruke de garantier i standarden.

Rekkefølgen på feltene er garantert. Jeg tror ikke du vil se intermitterende feil - AFAIK forskyvningen av hvert felt i den struct vil være konsekvent for en gitt implementering (som betyr kombinasjonen av kompilatoren og plattform).

Du kan hevde at sizeof (innstillinger *) == sizeof (char *) og sizeof (settings_row) == sizeof (char *) * 8. Hvis både de holder, er det ikke rom for noen polstring i struct, siden feltene ikke er lov til å "overlapping". Hvis du noen gang truffet en plattform hvor de ikke holder, vil du finne det ut.

Likevel, hvis du ønsker en matrise, ville jeg være tilbøyelig til å si bruke en matrise, med inline aksessoregenskaper funksjoner eller makroer for å få de enkelte felt. Enten trikset fungerer eller ikke, er det enda lettere å ikke tenke på det i det hele tatt.

Svarte 26/06/2009 kl. 00:30
kilden bruker

stemmer
1

Det er ikke garantert, men det vil fungere fint i de fleste tilfeller. Det vil ikke være forbigående, det vil enten jobbe eller ikke jobbe på en bestemt plattform med en bestemt bygge. Siden du bruker alle pekere, vil de fleste kompilatorer ikke rotet med noen padding.

Også, hvis du ønsket å være tryggere, kan du gjøre det til en union.

Svarte 25/06/2009 kl. 23:11
kilden bruker

stemmer
1

Teknisk sett kan du stole bare på ordre; kompilatoren kan sette padding. Hvis forskjellige pekere var av forskjellig størrelse, eller hvis størrelse pekeren ikke var en naturlig ord størrelse, det kan sette padding.

Praktisk talt, kan du komme unna med det. Jeg vil ikke anbefale det, det er en dårlig, skitne triks.

Du kan nå dine mål med en annen indirekte nivå (hva betyr ikke at løse?), Eller ved å bruke en midlertidig rekke initialisert til å peke på de ulike medlemmene av strukturen.

Svarte 25/06/2009 kl. 23:09
kilden bruker

stemmer
1

Selv ikke en kopi, sannsynligvis besvarer dette spørsmålet:

Hvorfor er ikke sizeof for en struct lik summen av sizeof av hvert medlem?

Det er ikke uvanlig for applikasjoner å skrive en hel struct til en fil og lese den ut igjen. Men dette lider av muligheten for at en dag filen må leses på en annen plattform, eller av en annen versjon av kompilator som pakker struct annerledes. (Selv om dette kan behandles av spesialskrevet kode som forstår originalemballasjen format).

Svarte 25/06/2009 kl. 23:05
kilden bruker

stemmer
0

Det virker for meg at denne tilnærmingen skaper flere problemer enn det løser. Når du leser denne koden seks måneder fra nå, vil du fortsatt være klar over alle spissfindigheter i hvordan kompilatoren pads en struct? Vil noen andre, gjorde som ikke skrive koden?

Hvis du må bruke struct, bruke det i den kanoniske måten og bare skrive en funksjon som tildeler verdier til hvert felt separat. Du kan også bruke en matrise og lage makroer for å gi feltnavn til indekser.

Hvis du får for "smart" om å optimalisere koden din, vil du ende opp med tregere kode uansett, siden kompilatoren ikke vil være i stand til å optimalisere det også.

Svarte 25/06/2009 kl. 23:31
kilden bruker

stemmer
0

Du kan ikke gjøre det på den måten du prøver. Kompilatoren tillates å puten hvilken som helst og alle medlemmer av struct. Jeg tror ikke det er lov til å endre rekkefølgen på feltene.

De fleste kompilatorer har en egenskap som kan brukes til struct å pakke det (dvs. å gjøre det til en samling av tettpakket lagring uten polstring), men ulempen er at dette vanligvis påvirker ytelsen. Pakket flagget vil trolig tillate deg å bruke struct slik du vil, men det kan ikke være bærbar tvers av ulike plattformer.

Padding er designet for å gjøre felttilgang så effektiv som mulig på målet arkitektur. Det er best ikke å bekjempe den med mindre du har til (dvs. går struct til en disk eller over et nettverk.)

Svarte 25/06/2009 kl. 23:04
kilden bruker

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