Buscar..


Observaciones

Los calificadores de tipo son las palabras clave que describen semánticas adicionales sobre un tipo. Son una parte integral de las firmas de tipo. Pueden aparecer tanto en el nivel superior de una declaración (que afecta directamente al identificador) como en los subniveles (relevantes solo para los indicadores, que afectan los valores apuntados a):

Palabra clave Observaciones
const Evita la mutación del objeto declarado (al aparecer en el nivel superior) o evita la mutación del valor apuntado (al lado de un subtipo de puntero).
volatile Informa al compilador que el objeto declarado (en el nivel superior) o el valor apuntado (en los subtipos de puntero) pueden cambiar su valor como resultado de condiciones externas, no solo como resultado del flujo de control del programa.
restrict Una sugerencia de optimización, relevante solo para los punteros. Declara la intención de que durante la vida útil del puntero, no se utilizarán otros punteros para acceder al mismo objeto apuntado.

El ordenamiento de los calificadores de tipo con respecto a los especificadores de clase de almacenamiento ( static , extern , auto , register ), modificadores de tipo (con signed , unsigned , short , long ) y los especificadores de tipo ( int , char , double , etc.) no se aplica, pero La buena práctica es ponerlos en el orden mencionado:

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

Cualificaciones de primer nivel

/* "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;

Cualificaciones de subtipo de puntero

/* "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;

Variables no modificables (const)

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

La calificación const solo significa que no tenemos el derecho de cambiar los datos. No significa que el valor no pueda cambiar a nuestras espaldas.

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

   return rememberA == *a;
}

Durante la ejecución de las otras llamadas *a podría haber cambiado, por lo que esta función puede devolver false o true .

Advertencia


Las variables con calificación const aún podrían cambiarse utilizando los punteros:

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

Pero hacerlo es un error que conduce a un comportamiento indefinido. La dificultad aquí es que esto puede comportarse como se espera en ejemplos simples como este, pero luego salen mal cuando el código crece.

Variables volátiles

La palabra clave volatile le dice al compilador que el valor de la variable puede cambiar en cualquier momento como resultado de condiciones externas, no solo como resultado del flujo de control del programa.

El compilador no optimizará nada que tenga que ver con la variable volátil.

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;

Hay dos razones principales para usar variables volátiles:

  • Para interactuar con el hardware que tiene registros de E / S asignados en memoria.
  • Cuando se usan variables que se modifican fuera del flujo de control del programa (por ejemplo, en una rutina de servicio de interrupción)

Veamos este ejemplo:

int quit = false;

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

void interrupt_handler(void) 
{
  quit = true;
}

Al compilador se le permite notar que el bucle while no modifica la variable quit y convierte el bucle en un bucle while (true) sin fin. Incluso si la variable quit se establece en el controlador de señal para SIGINT y SIGTERM , el compilador no lo sabe.

Declarar quit como volatile indicará al compilador que no optimice el bucle y el problema se resolverá.

El mismo problema ocurre al acceder al hardware, como vemos en este ejemplo:

uint8_t * pReg = (uint8_t *) 0x1717;

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

El comportamiento del optimizador es leer el valor de la variable una vez, no hay necesidad de volver a leerlo, ya que el valor siempre será el mismo. Así terminamos con un bucle infinito. Para forzar al compilador a hacer lo que queremos, modificamos la declaración a:

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


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow