Zoeken…


Opmerkingen

Type qualifiers zijn de sleutelwoorden die extra semantiek over een type beschrijven. Ze vormen een integraal onderdeel van typeaanduidingen. Ze kunnen zowel op het bovenste niveau van een aangifte (direct van invloed op de identifier) of op subniveaus (alleen relevant voor pointers, van invloed op de doorverwijswaarden) voorkomen:

keyword Opmerkingen
const Voorkomt de mutatie van het gedeclareerde object (door op het bovenste niveau te verschijnen) of voorkomt de mutatie van de puntwaarde (door naast een pointer-subtype te verschijnen).
volatile Informeert de compiler dat het gedeclareerde object (op het hoogste niveau) of de puntwaarde (in aanwijzersubtypen) de waarde ervan kan wijzigen als gevolg van externe omstandigheden, niet alleen als gevolg van de programmabesturingsstroom.
restrict Een optimalisatietip, alleen relevant voor pointers. Verklaart de bedoeling dat gedurende de levensduur van de aanwijzer geen andere aanwijzers zullen worden gebruikt om toegang te krijgen tot hetzelfde puntige object.

De volgorde van typekwalificaties met betrekking tot opslagklasse-specificatie ( static , extern , auto , register ), type-modificatie ( signed , unsigned , short , long ) en type-specificatie ( int , char , double , etc.) wordt niet afgedwongen, maar het is een goede gewoonte om ze in de bovengenoemde volgorde te plaatsen:

static const volatile unsigned long int a = 5; /* good practice */
unsigned volatile long static int const b = 5; /* bad practice */

Kwalificaties op het hoogste niveau

/* "a" cannot be mutated by the program but can change as a result of external conditions */
const volatile int a = 5;

/* the const applies to array elements, i.e. "a[0]" cannot be mutated */    
const int arr[] = { 1, 2, 3 };

/* for the lifetime of "ptr", no other pointer could point to the same "int" object */
int *restrict ptr;

Kwalificaties subtype aanwijzer

/* "s1" can be mutated, but "*s1" cannot */
const char *s1 = "Hello";

/* neither "s2" (because of top-level const) nor "*s2" can be mutated */
const char *const s2 = "World";

/* "*p" may change its value as a result of external conditions, "**p" and "p" cannot */
char *volatile *p;

/* "q", "*q" and "**q" may change their values as a result of external conditions */
volatile char *volatile *volatile q;

Onaanpasbare (const) variabelen

const int a = 0; /* This variable is "unmodifiable", the compiler
                    should throw an error when this variable is changed */
int b = 0; /* This variable is modifiable */

b += 10; /* Changes the value of 'b' */
a += 10; /* Throws a compiler error */

De const kwalificatie betekent alleen dat we niet het recht hebben om de gegevens te wijzigen. Het betekent niet dat de waarde niet achter onze rug kan veranderen.

_Bool doIt(double const* a) {
   double rememberA = *a;
   // do something long and complicated that calls other functions

   return rememberA == *a;
}

Tijdens de uitvoering van de andere aanroepen *a kan er *a zijn gewijzigd, en daarom kan deze functie false of true teruggeven.

Waarschuwing


Variabelen met const kwalificatie kunnen nog steeds worden gewijzigd met behulp van pointers:

const int a = 0;

int *a_ptr = (int*)&a; /* This conversion must be explicitly done with a cast */
*a_ptr += 10;          /* This has undefined behavior */

printf("a = %d\n", a); /* May print: "a = 10" */

Maar dit is een fout die leidt tot ongedefinieerd gedrag. De moeilijkheid hier is dat dit zich in eenvoudige voorbeelden als dit kan gedragen, maar dan mis kan gaan wanneer de code groeit.

Vluchtige variabelen

Het volatile sleutelwoord vertelt de compiler dat de waarde van de variabele op elk moment kan veranderen als gevolg van externe omstandigheden, niet alleen als gevolg van de programmastroom.

De compiler zal niets optimaliseren dat te maken heeft met de vluchtige variabele.

volatile int foo; /* Different ways to declare a volatile variable */
int volatile foo;

volatile uint8_t * pReg; /* Pointers to volatile variable */
uint8_t volatile * pReg;

Er zijn twee hoofdredenen om vluchtige variabelen te gebruiken:

  • Om te communiceren met hardware met geheugen toegewezen I / O-registers.
  • Bij gebruik van variabelen die buiten de programmabesturingsstroom zijn gewijzigd (bijvoorbeeld in een onderbrekingsserviceroutine)

Laten we dit voorbeeld bekijken:

int quit = false;

void main() 
{
    ... 
    while (!quit) {
      // Do something that does not modify the quit variable
    } 
    ...
}

void interrupt_handler(void) 
{
  quit = true;
}

De compiler mag opmerken dat de while-lus de variabele quit niet wijzigt en de lus omzet in een eindeloze while (true) lus while (true) . Zelfs als de variabele quit is ingesteld op de signaalhandler voor SIGINT en SIGTERM , weet de compiler dat niet.

Als quit als volatile wordt verklaard, vertelt de compiler dat de lus niet moet worden geoptimaliseerd en wordt het probleem opgelost.

Hetzelfde probleem treedt op bij toegang tot hardware, zoals we in dit voorbeeld zien:

uint8_t * pReg = (uint8_t *) 0x1717;

// Wait for register to become non-zero 
while (*pReg == 0) { } // Do something else

Het gedrag van de optimizer is om de waarde van de variabele eenmaal te lezen, het is niet nodig om deze opnieuw te lezen, omdat de waarde altijd hetzelfde zal zijn. Dus we eindigen met een oneindige lus. Om de compiler te dwingen te doen wat we willen, passen we de verklaring aan om:

uint8_t volatile * pReg = (uint8_t volatile *) 0x1717;


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow