Pregunta ¿Por qué las especificaciones de lenguaje C más antiguas requieren que las variables de función local se declaren por adelantado?


En el lenguaje de programación C, todas las revisiones de lenguaje que he trabajado con las declaraciones de variables implementadas antes de cualquier expresión no declarativa / asignada se evaluarían. C ++ parece haber renunciado a este requisito de todas las versiones. También reconozco que la versión más moderna de C también ha renunciado a este requisito, pero aún no he usado ninguno de esos estándares.

La pregunta que tengo es esta: ¿Qué razón histórica existía para evitar que el lenguaje C declarara libremente bajo demanda en lugar de declararlo por adelantado?

Obviamente, hay una serie de razones que me vienen a la mente desde el punto de vista de la ingeniería, pero ninguna de ellas me parece especialmente plausible.

  1. Evitar que se produzca un oscuro error de comportamiento del compilador (como ciclos de análisis infinitos, una saturación de memoria masiva para la evaluación o algunos casos de esquina raros con Macros).
  2. Previniendo la salida de compilador indeseable. Esto podría ser cualquier cosa, desde la salida de un símbolo confundiendo el proceso de depuración y la facilidad de desarrollo de herramientas de depuración, hasta órdenes de almacenamiento de pila inesperadas.
  3. Legibilidad. También me parece difícil de tragar, ya que C, aunque está diseñado para ser legible en comparación con otros idiomas de la época, no aplicaba este tipo de estructura en casi ningún otro lado. (A menos que vea prototipos como una aplicación similar, pero si recuerdo que se agregaron prototipos en la especificación '89.)
  4. Complejidad de implementación y razones prácticas. Este es el que estoy más inclinado a creer. Como ingenieros tenemos que hacer ciertas consideraciones para enviar un producto viable en un marco de tiempo asignado. Aunque concederé que el panorama profesional para la informática y la ingeniería de software ha cambiado dramáticamente, Business sigue siendo un negocio. Al final del día, estoy seguro de que Bell quería un producto terminado que se pudiera usar en el entorno de programación de Unix para mostrar lo que habían logrado.

¿Alguien tiene alguna buena fuente que respalde alguno de los anteriores? ¿Extrañé algo por completo? Podemos especular desde el amanecer hasta el anochecer, pero estoy buscando buenas referencias difíciles.


32
2018-01-14 18:42


origen


Respuestas:


Mirando al principio (6ª edición Unix, 1975) Manual C de la página principal de Dennis Ritchie, en esa versión las variables de función local podrían solamente ser declarado al comienzo de una función:

El enunciado de función es solo una declaración compuesta que puede tener declaraciones al comienzo.

declaración de función: { declaración-listaoptar  lista de declaraciones }

declaración-lista no está definido (una omisión), pero se puede suponer que tiene gramática:

declaración-lista: declaración  declaración-listaoptar.

No se permite que ninguna otra declaración compuesta contenga declaraciones variables (o de hecho ninguna).

Esto obviamente simplifica la implementación; en el código fuente del compilador temprano c02.c la función de encabezado de función blkhed()solo necesita sumar el espacio de pila utilizado por auto declaraciones variables, al mismo tiempo que registran su desplazamiento de pila, y emiten código para aumentar el puntero de pila en la cantidad apropiada. Al salir de la función (por return o cayendo desde el final) la implementación solo necesita restaurar el puntero de pila guardado.

El hecho de que K & R se sienta necesario para decir eso "las declaraciones de variables (incluidas las inicializaciones) pueden seguir al paréntesis izquierdo que introduce cualquier enunciado compuesto, no solo el que comienza una función"es una pista de que en ese punto era una característica relativamente reciente. También indica que la sintaxis de inicialización de declaración combinada también era una característica reciente, y de hecho en el manual de 1975 los declarantes no pueden tener inicializadores.

El manual de 1975 en la sección 11.1 establece específicamente que:

C no es un lenguaje estructurado por bloques; esto puede considerarse un defecto.

La instrucción de bloque y las declaraciones inicializadas (K & R) abordan ese defecto, y las declaraciones mixtas y el código (C99) son la continuación lógica.


17
2018-01-14 20:11



En C89, se requiere que las definiciones de variables estén al comienzo de un bloque. (Ver el estándar C para la definición de un bloque) Esto fue lo que yo sé que hice para simplificar la forma en que se manejan las variables en el ensamblador. Por ejemplo, echemos un vistazo a una función simple:

void foo()
{
    int i = 5;
    printf("%i\n", i);
}

cuando el gcc traduce esta función en código de ensamblador, la llamada a foo () se reduciría a un montón de instrucciones, incluida la configuración de una pila para el alcance de la función. este marco de pila incluye espacio para las variables definidas en el alcance de la función, y para hacer coincidir el mismo alcance en el lenguaje de nivel superior C, se requirió que se definieran al comienzo del bloque.

Al final, fue sobre la facilidad de implementación, y también la eficiencia, porque al declarar un grupo de variables a la vez, que al comienzo de un bloque, permite al compilador insertarlas en la pila, y alrededor de ~ 89 eso también fue una consideración de rendimiento.

Por supuesto, esta respuesta está horriblemente simplificada y solo tiene como objetivo dar una breve idea de por qué esto se ha hecho de la manera en que se hizo. Para obtener más información, probablemente debería leer algunos borradores del estándar C89 inicial.


10
2018-01-14 19:43



Una respuesta corta que realmente no responde mucho: el lenguaje C inicialmente heredó esta restricción de orden de declaración de su predecesor: lenguaje B. Por qué se hizo de esa manera en el lenguaje B. Desafortunadamente, no sé.

Tenga en cuenta también que en el C naciente (descrito en "C Manual de referencia") era ilegal inicializar variables (incluso locales) con expresiones no constantes.

int a 5;
int b a; /* ERROR in nascent versions of C */

(una nota al margen: en la sintaxis de inicialización CRM no incluía el = personaje). En el caso general, esto efectivamente anuló el beneficio principal de las declaraciones de variables en código: la capacidad de especificar un valor de tiempo de ejecución significativo como un inicializador. Incluso en C89 / 90, mucho más moderno, esta restricción todavía se aplicaba formalmente a los inicializadores agregados (aunque la mayoría de los compiladores lo ignoraban).

int a = 5, b = a;
struct { int x, y; } s = { a, b }; /* ERRROR even in C89/90 */ 

Solo en C99 se hizo posible usar valores de tiempo de ejecución para todos los tipos de inicialización local. Esto finalmente desbloqueó toda la potencia de las declaraciones de variables en código, por lo que es perfectamente lógico que el C99 fue el que las introdujo.


9
2018-01-14 19:50