Declarations
Una declaración es una construcción del lenguaje C que introduce uno o más identificadores en el programa y especifica su significado y propiedades.
Las declaraciones pueden aparecer en cualquier ámbito. Cada declaración termina con un punto y coma (al igual que una sentencia ) y consiste en dos (until C23) tres (since C23) partes distintas:
specifiers-and-qualifiers
declarators-and-initializers
(opcional)
;
|
(1) | ||||||||
attr-spec-seq
specifiers-and-qualifiers
declarators-and-initializers
;
|
(2) | (desde C23) | |||||||
attr-spec-seq
;
|
(3) | (desde C23) | |||||||
donde
| specifiers-and-qualifiers | - |
lista separada por espacios, en cualquier orden,
|
| declarators-and-initializers | - | lista separada por comas de declaradores (cada declarador proporciona información de tipo adicional y/o el identificador a declarar). Los declaradores pueden ir acompañados de inicializadores . Las declaraciones enum , struct , y union pueden omitir declaradores , en cuyo caso solo introducen las constantes de enumeración y/o etiquetas. |
| attr-spec-seq | - | (C23) lista opcional de atributos , aplicados a las entidades declaradas, o forma una declaración de atributo si aparece sola. |
Por ejemplo,
int a, *b=NULL; // "int" es el especificador de tipo, // "a" es un declarador // "*b" es un declarador y NULL es su inicializador const int *f(void); // "int" es el especificador de tipo // "const" es el calificador de tipo // "*f(void)" es el declarador enum COLOR {RED, GREEN, BLUE} c; // "enum COLOR {RED, GREEN, BLUE}" es el especificador de tipo // "c" es el declarador
El tipo de cada identificador introducido en una declaración se determina por una combinación del tipo especificado por el especificador de tipo y las modificaciones de tipo aplicadas por su declarador . El tipo de una variable también podría inferirse si se utiliza el especificador auto . (desde C23)
Atributos (desde C23) pueden aparecer en especificadores-y-calificadores , en cuyo caso se aplican al tipo determinado por los especificadores precedentes.
Contenidos |
Declaradores
Cada declarador es uno de los siguientes:
| identifier attr-spec-seq (opcional) | (1) | ||||||||
(
declarator
)
|
(2) | ||||||||
*
attr-spec-seq
(opcional)
qualifiers
(opcional)
declarator
|
(3) | ||||||||
noptr-declarator
[
static
(opcional)
qualifiers
(opcional)
expression
]
noptr-declarator
|
(4) | ||||||||
noptr-declarator
(
parameters-or-identifiers
)
|
(5) | ||||||||
D
como un puntero calificado
cvr
al tipo determinado por
S
.
D
como un arreglo de
N
objetos del tipo determinado por
S
.
noptr-declarator
es cualquier otro declarador excepto declaradores de puntero sin paréntesis.
D
como una función que toma los parámetros
params
y retorna
S
.
noptr-declarator
es cualquier otro declarador excepto un declarador de puntero sin paréntesis.
El razonamiento detrás de esta sintaxis es que cuando el identificador declarado por el declarador aparece en una expresión de la misma forma que el declarador, tendría el tipo especificado por la secuencia de especificadores de tipo.
struct C { int member; // "int" es el especificador de tipo // "member" es el declarador } obj, *pObj = &obj; // "struct C { int member; }" es el especificador de tipo // el declarador "obj" define un objeto de tipo struct C // el declarador "*pObj" declara un puntero a C, // el inicializador "= &obj" proporciona el valor inicial para ese puntero int a = 1, *p = NULL, f(void), (*pf)(double); // el especificador de tipo es "int" // el declarador "a" define un objeto de tipo int // el inicializador "=1" proporciona su valor inicial // el declarador "*p" define un objeto de tipo puntero a int // el inicializador "=NULL" proporciona su valor inicial // el declarador "f(void)" declara una función que toma void y retorna int // el declarador "(*pf)(double)" define un objeto de tipo puntero // a función que toma double y retorna int int (*(*foo)(double))[3] = NULL; // el especificador de tipo es int // 1. el declarador "(*(*foo)(double))[3]" es un declarador de array: // el tipo declarado es "/declarador anidado/ array de 3 int" // 2. el declarador anidado es "*(*foo)(double))", que es un declarador de puntero // el tipo declarado es "/declarador anidado/ puntero a array de 3 int" // 3. el declarador anidado es "(*foo)(double)", que es un declarador de función // el tipo declarado es "/declarador anidado/ función que toma double y retorna // puntero a array de 3 int" // 4. el declarador anidado es "(*foo)" que es un declarador de puntero (entre paréntesis, como requiere // la sintaxis del declarador de función). // el tipo declarado es "/declarador anidado/ puntero a función que toma double // y retorna puntero a array de 3 int" // 5. el declarador anidado es "foo", que es un identificador. // La declaración introduce el identificador "foo" para referirse a un objeto de tipo // "puntero a función que toma double y retorna puntero a array de 3 int" // El inicializador "= NULL" proporciona el valor inicial de este puntero. // Si "foo" se usa en una expresión de la forma del declarador, su tipo sería // int. int x = (*(*foo)(1.2))[0];
El final de cada declarador que no forma parte de otro declarador es un sequence point .
En todos los casos, attr-spec-seq es una secuencia opcional de atributos (desde C23) . Cuando aparece inmediatamente después del identificador, se aplica al objeto o función que se está declarando.
Definiciones
Una definición es una declaración que proporciona toda la información sobre los identificadores que declara.
Toda declaración de un enum o un typedef es una definición.
Para las funciones, una declaración que incluye el cuerpo de la función es una definición de función :
int foo(double); // declaración int foo(double x) { return x; } // definición
Para objetos, una declaración que asigna almacenamiento ( automático o estático , pero no extern) es una definición, mientras que una declaración que no asigna almacenamiento ( declaración externa ) no lo es.
extern int n; // declaración int n = 10; // definición
Para las structs y las unions , las declaraciones que especifican la lista de miembros son definiciones:
struct X; // declaración struct X { int n; }; // definición
Redeclaración
Una declaración no puede introducir un identificador si otra declaración para el mismo identificador en el mismo ámbito aparece antes, excepto que
- Declaraciones de objetos con vinculación (externa o interna) pueden repetirse:
extern int x; int x = 10; // CORRECTO extern int x; // CORRECTO static int n; static int n = 10; // CORRECTO static int n; // CORRECTO
- Non-VLA typedef puede repetirse siempre que nombre el mismo tipo:
typedef int int_t; typedef int int_t; // OK
struct X; struct X { int n; }; struct X;
Estas reglas simplifican el uso de archivos de cabecera.
Notas
|
En C89, las declaraciones dentro de cualquier sentencia compuesta (ámbito de bloque) deben aparecer al principio del bloque, antes de cualquier sentencia . Además, en C89, las funciones que devuelven int pueden ser declaradas implícitamente por el operador de llamada a función y los parámetros de función de tipo int no tienen que ser declarados cuando se utilizan definiciones de función de estilo antiguo. |
(hasta C99) |
Los declaradores vacíos están prohibidos; una declaración simple debe tener al menos un declarador o declarar al menos una etiqueta de struct/union/enum, o introducir al menos una constante de enumeración.
|
Si cualquier parte de un declarador es un arreglo de longitud variable (VLA), el tipo completo del declarador se conoce como "tipo modificable variablemente". Los tipos definidos a partir de tipos modificables variablemente también son modificables variablemente (VM). Las declaraciones de cualquier tipo modificable variablemente solo pueden aparecer en el ámbito de bloque o ámbito de prototipo de función y no pueden ser miembros de estructuras o uniones. Aunque un VLA solo puede tener duración de almacenamiento automática o asignada, un tipo VM como un puntero a un VLA puede ser estático. Existen otras restricciones sobre el uso de tipos VM, consulte goto , switch . longjmp |
(desde C99) |
|
static_asserts se consideran declaraciones desde el punto de vista de la gramática de C (para que puedan aparecer en cualquier lugar donde pueda aparecer una declaración), pero no introducen ningún identificador y no siguen la sintaxis de declaración. |
(desde C11) |
|
Atributos
las declaraciones también se consideran declaraciones (para que puedan aparecer en cualquier lugar donde pueda aparecer una declaración), pero no introducen ningún identificador. Un solo
|
(desde C23) |
Referencias
- Estándar C23 (ISO/IEC 9899:2024):
-
- 6.7 Declarations (p: TBD)
- Estándar C17 (ISO/IEC 9899:2018):
-
- 6.7 Declarations (p: 78-105)
- Estándar C11 (ISO/IEC 9899:2011):
-
- 6.7 Declarations (p: 108-145)
- Estándar C99 (ISO/IEC 9899:1999):
-
- 6.7 Declarations (p: 97-130)
- Estándar C89/C90 (ISO/IEC 9899:1990):
-
- 3.5 Declarations
Véase también
|
Documentación de C++
para
Declarations
|