c++ declaration storage-class-specifier

c++ - ¿Dónde en una declaración se puede colocar un especificador de clase de almacenamiento?



declaration storage-class-specifier (3)

Por ejemplo, consideremos el especificador de clase de almacenamiento static . Aquí hay algunos ejemplos de usos válidos y mal formados de este especificador de clase de almacenamiento:

static int a; // valid int static b; // valid static int* c; // valid int static* d; // valid int* static e; // ill-formed static int const* f; // valid int static const* g; // valid int const static* h; // valid int const* static i; // ill-formed typedef int* pointer; static pointer j; // valid pointer static k; // valid

(Las declaraciones marcadas como "válidas" fueron aceptadas por Visual C ++ 2012, g ++ 4.7.2 y Clang ++ 3.1. Las declaraciones marcadas como "con formato incorrecto" fueron rechazadas por todos los compiladores).

Esto parece extraño porque el especificador de la clase de almacenamiento se aplica a la variable declarada. Es la variable declarada que es static , no el tipo de la variable declarada. ¿Por qué e y i mal formados, pero k está bien formado?

¿Cuáles son las reglas que rigen la colocación válida de los especificadores de clase de almacenamiento? Si bien he usado static en este ejemplo, la pregunta se aplica a todos los especificadores de clase de almacenamiento. Preferiblemente, una respuesta completa debe citar secciones relevantes del estándar de lenguaje C ++ 11 y explicarlas.


En resumen, en cualquier lugar del especificador de declaración (consulte la sección 7.1 en ISO / IEC 14882-2012), es decir, antes del * . Los calificadores después de * están asociados con el declarador de puntero, no con el especificador de tipo, y la static no tiene sentido dentro del contexto de un declarador de puntero.

Considere los siguientes casos: Puede declarar un int normal y un puntero a un int en la misma lista de declaración, como esto:

int a, *b;

esto se debe a que el especificador de tipo es int , entonces tiene dos declaraciones utilizando ese especificador de tipo int , a , y un declarador de puntero *a que declara un puntero a int . Ahora considera:

int a, static b; // error int a, *static b; // error int a, static *b; // error

lo que debería verse mal (como lo son), y la razón (como se define en las secciones 7.1 y 8.1) es que C y C ++ requieren que sus especificadores de almacenamiento vayan con su especificador de tipo, no en su declarador. Así que ahora debería quedar claro que lo siguiente también es incorrecto, ya que los tres anteriores también son incorrectos:

int *static a; // error

Tu último ejemplo,

typedef int* pointer; static pointer j; // valid pointer static k; // valid

son válidos y equivalentes, ya que el tipo de pointer se define como un especificador de tipo y puede poner su especificador de tipo y su especificador de almacenamiento en cualquier orden. Tenga en cuenta que ambos son equivalentes y serían equivalentes a decir

static int *j; static int *k;

o

int static *j; int static *k;


Según 7.1, la estructura [simplificada] de la declaración de C ++ es

decl-specifier-seq init-declarator-list;

Según 7.1 / 1, los especificadores de clase de almacenamiento pertenecen a la parte "común" inicial decl-specifier-seq .

Por 8/1, init-declarator-list es una secuencia de declaradores.

Según 8/4, la parte * de la declaración de puntero es parte de un declarador individual en esa secuencia. Esto significa inmediatamente que todo lo que sigue a * es parte de ese declarador individual. Esta es la razón por la que algunas de sus ubicaciones de especificador de clase de almacenamiento no son válidas. La sintaxis del declarador no permite la inclusión de especificadores de clase de almacenamiento.

La razón es bastante obvia: dado que se supone que los especificadores de clase de almacenamiento se aplican a todos los declaradores en toda la declaración, se colocan en la parte "común" de la declaración.

Yo diría que una situación más interesante (y algo relacionada) tiene lugar con especificadores que pueden estar presentes tanto en decl-specifier-seq como en los declaradores individuales, como const specifier. Por ejemplo, en la siguiente declaración.

int const *a, *b;

¿se aplica const a todos los declaradores o solo al primero? La gramática dicta la interpretación anterior: esa const aplica a todos los declaradores, es decir, es parte de decl-specifier-seq .


Si emplea la "Regla de oro" (que también no se aplica solo a los punteros), se sigue de forma natural e intuitiva, y evita muchos errores y pitfalls al declarar variables en C / C ++. La "regla de oro" no debe ser violada (hay raras excepciones, como const aplicada a array typedefs, que se propaga const al tipo base, y referencias, que venían con C ++).

K&R, Apéndice A, Sección 8.4, Significado de los declaradores, establece:

Cada declarador se toma como una afirmación de que cuando una construcción de la misma forma que el declarador aparece en una expresión, produce un objeto del tipo indicado y la clase de almacenamiento.

Para declarar una variable en C / C ++, realmente debería pensar en la expresión que debería aplicarle para obtener el tipo base.

1) Debe haber un nombre de variable

2) Luego viene la expresión como válida * fuera de la declaración, aplicada al nombre de la variable

3) Luego viene la información restante y las propiedades de la declaración como tipo de base y almacenamiento.

El almacenamiento no es una característica que siempre se puede conferir al resultado de las expresiones, al contrario de la constancia, por ejemplo. Sólo tiene sentido en la declaración. Así que el almacenamiento debe venir a otro lugar que no esté en 2.

int * const *pp; /*valid*/ int * static *pp; /*invalid, this clearly shows how storage makes no sense for 2 and so breaks */ /*the golden rule. */ /*It''s not a piece of information that goes well in the middle of a expression.*/ /*Neither it''s a constraint the way const is, it just tells the storage of */ /*what''s being declared. */

Creo que K&R quería que usáramos el razonamiento invertido al declarar variables, con frecuencia no es el hábito común. Cuando se usa, evita la mayoría de errores y dificultades de declaración complejos.

* válido no es en sentido estricto, ya que se producen algunas variaciones, como x [], x [tamaño, no indexación], constness, etc. Así que 2 es una expresión que se mapea bien (para el uso de la declaración), "igual forma ", que refleja el uso de la variable , pero no estrictamente .

Bonificación de la regla de oro para los no iniciados

#include <iostream> int (&f())[3] { static int m[3] = {1, 2, 3}; return m; } int main() { for(int i = 0; i < sizeof(f()) / sizeof(f()[0]); ++i) std::cout << f()[i] << std::endl; return 0; }

En el contexto de las declaraciones, & no es una operación para obtener una dirección, simplemente indica qué es una referencia.

  • f() : f es una función
  • & return : su retorno es una referencia.
  • referencia [3] : la referencia es a una matriz de 3 elementos
  • int array [i] : un elemento es un int

Entonces, tiene una función que devuelve una referencia a una matriz de 3 enteros, y como tenemos la información de tiempo de compilación adecuada del tamaño de la matriz, podemos verificarla con sizeof cualquier momento =)

La punta de oro final, para cualquier cosa que se pueda colocar antes del tipo, cuando se encuentra en varias declaraciones, se debe aplicar a todas las variables a la vez y, por lo tanto, no se puede aplicar individualmente.

Esta const no se puede poner antes de int :

int * const p;

Entonces lo siguiente es válido:

int * const p1, * const p2;

Este puede:

int const *p; // or const int *p;

Así que lo siguiente no es válido:

int const *p1, const *p2;

La const intercambiable se aplica para todos:

int const *p1, *p2; // or const int *p1, *p2;

Convenciones de la Declaración

Por eso, siempre pongo todo lo que no se puede poner antes del tipo, más cerca de la variable ( int *a , int &b ), y cualquier cosa que se pueda poner antes, pongo antes ( volatile int c ).

Hay mucho más sobre este tema en http://nosubstance.me/post/constant-bikeshedding/ .