Namespaces
Variants

Storage-class specifiers

From cppreference.net

Especificar la duración de almacenamiento y vinculación de objetos y funciones:

  • auto - duración automática y sin vinculación
  • register - duración automática y sin vinculación; no se puede obtener la dirección de esta variable
  • static - duración estática y vinculación interna (a menos que esté en ámbito de bloque)
  • extern - duración estática y vinculación externa (a menos que ya esté declarada interna)
  • _Thread_local (hasta C23) thread_local (desde C23) - duración de almacenamiento de hilo
(desde C11)

Contenidos

Explicación

Los especificadores de clase de almacenamiento aparecen en declaraciones y expresiones de literales compuestos (desde C23) . Como máximo puede usarse un especificador , excepto que _Thread_local (hasta C23) thread_local (desde C23) puede combinarse con static o extern para ajustar el linkage (desde C11) . Los especificadores de clase de almacenamiento determinan dos propiedades independientes de los nombres que declaran: duración de almacenamiento e linkage .

1) El especificador auto solo está permitido para objetos declarados en ámbito de bloque (excepto en listas de parámetros de función). Indica duración de almacenamiento automático y sin vinculación, que son los valores predeterminados para este tipo de declaraciones.
2) El especificador register solo está permitido para objetos declarados en ámbito de bloque, incluyendo listas de parámetros de función. Indica duración de almacenamiento automático y sin vinculación (que es el valor predeterminado para este tipo de declaraciones), pero adicionalmente sugiere al optimizador almacenar el valor de esta variable en un registro de CPU si es posible. Independientemente de si esta optimización se realiza o no, las variables declaradas como register no pueden usarse como argumentos para el operador de dirección , no pueden usar _Alignas (hasta C23) alignas (desde C23) (desde C11) , y los arreglos register no son convertibles a punteros.
3) El static especificador especifica tanto la duración de almacenamiento estático (a menos que se combine con _Thread_local ) (desde C11) como el enlace interno (a menos que se use en el ámbito de bloque). Puede usarse con funciones en el ámbito de archivo y con variables tanto en el ámbito de archivo como de bloque, pero no en listas de parámetros de función.
4) El extern especificador especifica duración de almacenamiento estático (a menos que se combine con _Thread_local (hasta C23) thread_local (desde C23) ) (desde C11) y vinculación externa. Puede usarse con declaraciones de funciones y objetos tanto en ámbito de archivo como de bloque (excluyendo listas de parámetros de funciones). Si extern aparece en una redeclaración de un identificador que ya fue declarado con vinculación interna, la vinculación permanece interna. En caso contrario (si la declaración previa era externa, sin vinculación, o no está en ámbito), la vinculación es externa.
5) _Thread_local (hasta C23) thread_local (desde C23) indica duración de almacenamiento de hilo . No puede utilizarse con declaraciones de funciones. Si se utiliza en una declaración de un objeto, debe estar presente en cada declaración del mismo objeto. Si se utiliza en una declaración de ámbito de bloque, debe combinarse con static o extern para determinar la vinculación.
(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 ámbito de archivo
auto para objetos en ámbito de bloque

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

Las declaraciones de funciones en el ámbito de bloque pueden usar extern o ninguna en absoluto. Las declaraciones de funciones en el ámbito de archivo pueden usar extern o static .

Los parámetros de función no pueden usar ningún especificador de clase de almacenamiento aparte de register . Nótese que static tiene un significado especial en parámetros de función de tipo array.

Duración de almacenamiento

Cada objeto tiene una propiedad llamada storage duration , que limita el lifetime del objeto. Existen cuatro tipos de storage duration en C:

  • automatic duración de almacenamiento automático. El almacenamiento se asigna cuando se entra en el bloque en el que se declaró el objeto y se desasigna cuando se sale de él por cualquier medio ( goto , return , alcanzando el final). Una excepción son los VLA ; su almacenamiento se asigna cuando se ejecuta la declaración, no al entrar al bloque, y se desasigna cuando la declaración sale del ámbito, no cuando se sale del bloque (desde C99) . Si el bloque se entra recursivamente, se realiza una nueva asignación para cada nivel de recursión. Todos los parámetros de función y objetos de ámbito de bloque no static tienen esta duración de almacenamiento, así como los literales compuestos utilizados en ámbito de bloque (hasta C23)
  • static duración de almacenamiento estático. La duración del almacenamiento es toda la ejecución del programa, y el valor almacenado en el objeto se inicializa solo una vez, antes de la función main . Todos los objetos declarados static y todos los objetos con enlace interno o externo que no estén declarados _Thread_local (hasta C23) thread_local (desde C23) (desde C11) tienen esta duración de almacenamiento.
  • thread duración de almacenamiento. La duración de almacenamiento es la ejecución completa del hilo en el que fue creado, y el valor almacenado en el objeto se inicializa cuando el hilo comienza. 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 (hasta C23) thread_local (desde C23) tienen esta duración de almacenamiento.
(desde C11)

Vinculación

La vinculación se refiere a la capacidad de un identificador (variable o función) de ser referenciado 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 tipos de vinculación:

  • sin vinculación . La variable o función solo puede ser referenciada desde el ámbito en el que se encuentra (ámbito de bloque). Todas las variables de ámbito de bloque que no están declaradas extern tienen esta vinculación, así como todos los parámetros de función y todos los identificadores que no son funciones o variables.
  • internal linkage . La variable o función puede ser referenciada desde todos los ámbitos en la unidad de traducción actual. Todas las variables de ámbito de archivo declaradas static o constexpr (desde C23) tienen este tipo de vinculación, y todas las funciones de ámbito de archivo declaradas static (las declaraciones de funciones estáticas solo están permitidas en el ámbito de archivo).
  • external linkage . La variable o función puede ser referenciada desde cualquier otra unidad de traducción en todo el programa. Todas las variables de ámbito de archivo que no están declaradas static o constexpr (since C23) tienen este tipo de vinculación, todas las declaraciones de funciones de ámbito de archivo que no están declaradas static , todas las declaraciones de funciones de ámbito de bloque, y adicionalmente, todas las variables o funciones declaradas extern tienen este tipo de vinculación a menos que una declaración previa con vinculación interna sea visible en ese punto.

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

Vinculación y bibliotecas

Las declaraciones con enlace externo comúnmente se ponen a disposición en archivos de cabecera para que todas las unidades de traducción que #include el archivo puedan referirse al mismo identificador que está definido en otro lugar.

Cualquier declaración con vinculación interna que aparezca en un archivo de encabezado resulta en un objeto separado y distinto en cada unidad de traducción que incluye ese archivo.

Interfaz de la biblioteca, archivo de cabecera "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 variable con enlace externo
static const int size = 5; // definición de variable de solo lectura con enlace interno
enum { MAX = 10 };         // definición de constante
inline int sum (int a, int b) { return a + b; } // definición de función en línea
#endif // FLIB_H

Implementación de la biblioteca, archivo fuente "flib.c":

#include "flib.h"
static void local_f(int s) {} // definición con enlace interno (solo se usa en este archivo)
static int local_state;       // definición con enlace interno (solo se usa en este archivo)
int state;                       // definición con enlace externo (usado por main.c)
void f(void) { local_f(state); } // definición con enlace externo (usado por main.c)

Código de aplicación, archivo fuente "main.c":

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

Palabras clave

auto , register , static , extern , _Thread_local thread_local

Notas

La palabra clave _Thread_local se utiliza generalmente a través de la macro de conveniencia thread_local , definida en el encabezado <threads.h> .

(hasta C23)

Los typedef y constexpr (desde C23) especificadores están formalmente listados como especificadores de clase de almacenamiento en la gramática del lenguaje C, pero no especifican almacenamiento.

El especificador auto también se utiliza para la inferencia de tipos.

(desde C23)

Los nombres en el ámbito de archivo que son const y no son extern tienen enlace externo en C (como valor predeterminado para todas las declaraciones en el ámbito de archivo), pero enlace interno en C++.

Ejemplo

#include <stdio.h>
#include <stdlib.h>
// static storage duration
int A;
int main(void)
{
    printf("&A = %p\n", (void*)&A);
    // automatic storage duration
    int A = 1;   // hides global A
    printf("&A = %p\n", (void*)&A);
    // allocated storage duration
    int* ptr_1 = malloc(sizeof(int));   // start allocated storage duration
    printf("address of int in allocated memory = %p\n", (void*)ptr_1);
    free(ptr_1);                        // stop allocated storage duration
}

Salida posible:

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

Referencias

  • Estándar C23 (ISO/IEC 9899:2024):
  • 6.2.2 Enlaces de identificadores (p: 35-36)
  • 6.2.4 Duración de almacenamiento de objetos (p: 36-37)
  • 6.7.1 Especificadores de clase de almacenamiento (p: 97-100)
  • Estándar C17 (ISO/IEC 9899:2018):
  • 6.2.2 Enlaces de identificadores (p: 29-30)
  • 6.2.4 Duración de almacenamiento de objetos (p: 30)
  • 6.7.1 Especificadores de clase de almacenamiento (p: 79)
  • Estándar C11 (ISO/IEC 9899:2011):
  • 6.2.2 Enlaces de identificadores (p: 36-37)
  • 6.2.4 Duración de almacenamiento de objetos (p: 38-39)
  • 6.7.1 Especificadores de clase de almacenamiento (p: 109-110)
  • Estándar C99 (ISO/IEC 9899:1999):
  • 6.2.2 Enlaces de identificadores (p: 30-31)
  • 6.2.4 Duración de almacenamiento de objetos (p: 32)
  • 6.7.1 Especificadores de clase de almacenamiento (p: 98-99)
  • Estándar C89/C90 (ISO/IEC 9899:1990):
  • 3.1.2.2 Enlaces de identificadores
  • 3.1.2.4 Duración de almacenamiento de objetos
  • 3.5.1 Especificadores de clase de almacenamiento

Véase también

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