Espacios de nombres
Variantes

Especificadores para clases de almacenamiento

De cppreference.com

Los siguientes especificadores determinan la duración de almacenamiento y el tipo de enlazamiento para objetos y funciones:

  • auto - duración automática y sin enlace
  • register - duración automática y sin enlace;no se puede tomar la dirección de esta variable
  • static - duración estática y enlace interno (a menos que sea en el ámbito del bloque)
  • extern - duración estática y enlace externo (a menos que ya se haya declarado como interno)
  • _Thread_local - duración del almacenamiento del hilo
(desde C11)

Explicación

Los especificadores para clases de almacenamiento aparecen en las declaraciones. Como máximo se puede utilizar un especificador, a excepción de _Thread_local que se puede combinar con static o extern para ajustar el enlace (desde C11). Los especificadores para clases de almacenamiento determinan dos propiedades independientes de los nombres que declaran: duración del almacenamiento y enlace.

1) El especificador auto sólo está permitido para los objetos declarados en el ámbito del bloque (excepto las listas de parámetros de función). Este indica la duración del almacenamiento automático y la ausencia de enlaces, que son los valores por defecto para este tipo de declaraciones.
2) El especificador register sólo está permitido para los objetos declarados en el ámbito del bloque, incluidas las listas de parámetros de funciones. Este indica la duración de almacenamiento sea automático y sin enlaces (que es el valor por defecto para este tipo de declaraciones), pero adicionalmente indica al optimizador que almacene el valor de esta variable en un registro de la CPU si es posible. Independientemente de si esta optimización tiene lugar o no, , las variables declaradas como register no pueden ser usadas como argumentos para la adirección del operador, no pueden usar alignas (desde C11), y los matrices de register no son convertibles a punteros.
3) El especificador static especifica tanto la duración del almacenamiento estático (a menos que se combine con _Thread_local) (desde C11) como el enlace interno (a menos que se utilice en el ámbito del bloque). Se puede utilizar con funciones en el ámbito de archivo y con variables tanto en el ámbito del archivo como de bloque, pero no en las listas de parámetros de funciones.
4) El especificador extern especifica la duración del almacenamiento estático (a menos que se combine con _Thread_local) (desde C11) y el enlace externo. Se puede utilizar con declaraciones de función y de objeto tanto en el ámbito de archivo como en el de bloque (excluyendo las listas de parámetros de función). Si extern aparece en una redeclaración de un identificador que ya fue declarado con enlace interno, el enlace sigue siendo interno. De lo contrario (si la declaración previa era externa, sin vinculación, o no está en el ámbito), la vinculación es externa.
5) _Thread_local indica la duración del almacenamiento del hilo. No se puede utilizar con declaraciones de función. Si se utiliza en una declaración de un objeto, debe estar presente en todas las declaraciones del mismo objeto. Si se utiliza en una declaración de ámbito de bloque, debe combinarse con una conexión ya sea static o extern para decidir el tipo de enlace.
(desde C11)

Si no se proporciona ningún especificador de clase de almacenamiento, los valores predeterminados son:

extern para todas las funciones
extern para objetos en el ámbito de archivo
auto para objetos en el ámbito del bloque

Para cualquier estructura o unión declarada con un especificador de clase de almacenamiento, la duración del almacenamiento (pero no la vinculación) se aplica a sus miembros, recursivamente.

Las declaraciones de función en el ámbito de aplicación del bloque pueden usar extern o bien ninguna . Las declaraciones de función en el ámbito de archivo pueden utilizar extern o static.

Los parámetros de función no pueden utilizar ningún especificador de clase de almacenamiento que no sea register. Tenga en cuenta que static tiene un significado especial en los parámetros de función del tipo matriz.

Duración del almacenamiento

Cada objeto tiene una propiedad llamada duración del almacenamiento, que limita el tiempo de vida del objeto. Hay cuatro tipos de duración de almacenamiento en C:

  • Tiempo de almacenamiento automatic. El almacenamiento se asigna cuando se introduce el bloque en el que se declaró el objeto y se anula su asignación cuando se sale por cualquier medio (goto, return, llegar al final). Una excepción son los VLAs; su almacenamiento se asigna cuando se ejecuta la declaración, no en la entrada de bloque, y se desasigna cuando la declaración sale del ámbito, no que cuando se sale del bloque (desde C99). Si el bloque se introduce recursivamente, se realiza una nueva asignación para cada nivel de recursividad. Todos los parámetros de función y los objetos no static del ámbito de bloque tienen esta duración de almacenamiento, así como los literales compuestos utilizados en el ámbito de bloque.
  • Tiempo de almacenamiento static. La duración de almacenamiento es la ejecución completa del programa, y el valor almacenado en el objeto se inicializa sólo una vez, antes de la función main. Todos los objetos declarados static y todos los objetos con enlaces internos o externos que no sean declarados _Thread_local (desde C11) tienen esta duración de almacenamiento.
  • Tiempo de almacenamiento thread. La duración de almacenamiento es la ejecución completa del hilo en el que se ha creado, y el valor almacenado en el objeto se inicializa cuando se inicia el hilo. Cada hilo tiene su propio objeto, distinto. Si el hilo que ejecuta la expresión que accede a este objeto no es el hilo que ejecutó su inicialización, el comportamiento está definido por la implementación. Todos los objetos declarados _Thread_local tienen esta duración de almacenamiento.
(desde C11)

Enlazado

El enlazado se refiere a la capacidad de un identificador (variable o función) para ser mencionado en otros ámbitos. Si una variable o función con el mismo identificador se declara en varios ámbitos, pero no puede ser referenciada desde todos ellos, entonces se generan varias instancias de la variable. Se reconocen los siguientes vínculos:

  • sin enlazado. Sólo se puede hacer referencia al identificador desde el ámbito en el que se encuentra. Todos los parámetros de función y todas las variables de alcance de bloque que no son {tt|extern}} (incluidas las declaradas static) tienen este enlace.
  • enlazado interno. El identificador se puede consultar desde todos los ámbitos de la unidad de traducción actual. Todos los identificadores de alcance de archivo static (tanto funciones como variables) tienen este vínculo.
  • enlazado externo. El identificador se puede consultar desde cualquier otra unidad de traducción de todo el programa. Todas las funciones no static, todas las variables extern (a menos que se haya declarado anteriormente static) y todas las variables file-scope non-static tienen este vínculo.

Si aparece el mismo identificador con enlaces internos y externos en la misma unidad de traducción, el comportamiento es indefinido. Esto es posible cuando se utilizan definiciones tentativas.

Enlaces y bibliotecas

Las declaraciones con enlace externo suelen estar disponibles en los archivos de encabezado, de modo que todas las unidades de traducción que contienen #include en el archivo pueden referirse al mismo identificador que se define en otra parte.

Cualquier declaración con enlace interno que aparezca en un archivo de encabezado resulta en un objeto separado y distinto en cada unidad de traducción que incluya ese archivo.

Interfaz de biblioteca:

// flib.h
#ifndef FLIB_H
#define FLIB_H
void f(void);              // declaración de función con enlace externo
extern int state;          // declaración de la variable con enlace externo
static const int size = 5; // definición de una variable de sólo lectura con enlace interno
enum { MAX = 10 };         // definición de una constante
inline int sum (int a, int b) { return a+b; } // definición de la función inline
#endif // FLIB_H

Implementación de la biblioteca:

// flib.c
#include "flib.h"
static void local_f(int s) {}  // definición con enlace interno (sólo se utiliza en este archivo)
static int local_state;        // definición con enlace interno (sólo se utiliza en este archivo)

int state;                     // definición con enlace externo (utilizado por main.c)
void f(void) {local_f(state);} // definición con enlace externo (utilizado por main.c)

Código de la aplicación:

// main.c 
#include "flib.h"
int main(void)
{
    int x[MAX] = {size}; // utiliza la constante y la variable de sólo lectura
    state = 7;           // modifica el estado en flib.c
    f();                 // llama a f() en flib.c
}

Terminos clave

auto, register, static, extern, _Thread_local

Observaciones

El termino clave _Thread_local se utiliza normalmente a través de la práctica macro thread_local, definida en la cabecera threads.h.

El especificador typedef} se lista formalmente como un especificador de clase de almacenamiento en la gramática del lenguaje C, pero se utiliza para declarar nombres de tipos y no para especificar el tipo de almacenamiento.

Los nombres en el ámbito de archivo que sean const y no extern tienen un enlace externo en C (por defecto para todas las declaraciones de ámbito de archivo), pero un enlace interno en C++.

Ejemplo

#include <stdio.h>
#include <stdlib.h>
 
/* duración del almacenamiento estático */
int A;
 
int main(void)
{
    printf("&A = %p\n", (void*)&A);
 
    /* duración del almacenamiento automático */
    int A = 1;   // hides global A
    printf("&A = %p\n", (void*)&A);

    /* duración del almacenamiento asignado *//
    int *ptr_1 = malloc(sizeof(int));   /* inicio de la duración del almacenamiento asignado */
    printf("address of int in allocated memory = %p\n", (void*)ptr_1);
    free(ptr_1);                         /* final de la duración del almacenamiento asignado */

}

Posible salida:

&A = 0x600ae4
&A = 0x7ffefb064f5c
address of int in allocated memory = 0x1f28c30

Referencias

  • Standard C11 (ISO/IEC 9899:2011):
  • 6.2.2 Linkages of identifiers (p: 36-37)
  • 6.2.4 Storage durations of objects (p: 38-39)
  • 6.7.1 Storage-class specifiers (p: 109-110)
  • Standard C99 (ISO/IEC 9899:1999):
  • 6.2.2 Linkages of identifiers (p: 30-31)
  • 6.2.4 Storage durations of objects (p: 32)
  • 6.7.1 Storage-class specifiers (p: 98-99)
  • Standard C89/C90 (ISO/IEC 9899:1990):
  • 3.1.2.2 Linkages of identifiers
  • 3.1.2.4 Storage durations of objects
  • 3.5.1 Storage-class specifiers

Véase también

Documentación de C++ para Especificadores para clases de almacenamiento