¿Cuál es el fundamento detrás de las definiciones tentativas en C?

5 minutos de lectura

avatar de usuario
Incinerador de basuras

Considere el siguiente programa. ¿Esto dará algún error de compilación?

#include <stdio.h>
int s=5;
int s;
int main(void)
{
     printf("%d",s);
}

A primera vista, parece que el compilador dará un error de redefinición de variables, pero el programa es perfectamente válido de acuerdo con el estándar C. (Ver demostración en vivo aquí http://ideone.com/Xyo5SY).

Una definición tentativa es cualquier declaración de datos externos que no tiene especificador de clase de almacenamiento ni inicializador.

C99 6.9.2/2

Una declaración de un identificador para un objeto que tiene un alcance de archivo sin un inicializador y sin un especificador de clase de almacenamiento o con el especificador de clase de almacenamiento estático constituye una definición tentativa. Si una unidad de traducción contiene una o más definiciones tentativas para un identificador, y la unidad de traducción no contiene una definición externa para ese identificador, entonces el comportamiento es exactamente como si la unidad de traducción contuviera una declaración de alcance de archivo de ese identificador, con el tipo compuesto como del final de la unidad de traducción, con un inicializador igual a 0.

Mi pregunta es, ¿cuál es la justificación para permitir definiciones tentativas? ¿Hay algún uso de esto en C? ¿Por qué C permite definiciones tentativas?

  • No creo que haya razones valiosas para eso porque C++ nunca lo ha tenido, entre muchos otros lenguajes de programación.

    – edmz

    18/10/2015 a las 18:51


  • @edmz C++ no es C, ni los dos lenguajes (incluso en su C preestandarizado con clases y formularios K&R C) nunca han sido compatibles, por lo que C++ no tiene lugar en una discusión sobre el estándar ANSI C. La razón de las definiciones tentativas es la misma razón por la que aún puede definir funciones C con sintaxis K&R (main(c,v)int c; char **v;{ ... }): compatibilidad con versiones anteriores. Actual compatibilidad con versiones anteriores. Como en, puede ejecutar una base de código C que no se ha tocado desde 1973 a través de un compilador moderno y aún se compilará.

    – Braden Mejor

    1 mayo 2021 a las 20:00


avatar de usuario
Shafik Yaghmour

Las definiciones provisionales se crearon como una forma de unir modelos incompatibles que existían antes de C89. Esto está cubierto en el justificación C99 sección 6.9.2 Definiciones de objetos externos que dice:

Antes de C90, las implementaciones variaban ampliamente con respecto a los identificadores de referencia directa con enlace interno (ver §6.2.2). El comité C89 inventó el concepto de definición tentativa para manejar esta situación. Una definición tentativa es una declaración que puede o no actuar como una definición: si una definición real se encuentra más adelante en la unidad de traducción, entonces la definición tentativa solo actúa como una declaración. Si no, entonces la definición tentativa actúa como una definición real. En aras de la coherencia, se aplican las mismas reglas a los identificadores con enlace externo, aunque no son estrictamente necesarias.

y sección 6.2.2 de la justificación C99 dice:

El modelo de definición que se usará para objetos con enlace externo fue un problema importante de estandarización C89. El problema básico era decidir qué declaraciones de un objeto definen el almacenamiento para el objeto y cuáles simplemente hacen referencia a un objeto existente. Un problema relacionado era si se permiten múltiples definiciones de almacenamiento o si solo una es aceptable. Las implementaciones anteriores a C89 exhiben al menos cuatro modelos diferentesenumerados aquí en orden creciente de restricción:

  • ¿Cómo conoce y entiende tan bien el estándar C y C++? Eres realmente un genio.

    – Destructor

    25/10/2015 a las 16:36

  • @PravasiMeet Lo sé bien porque paso mucho tiempo leyendo el estándar y los documentos relacionados y las preguntas SO. Solo se trata de práctica y experiencia. Cuanta más experiencia tengas, más problemas interesantes tendrás que resolver y solo se construye a partir de ahí.

    – Shafik Yaghmour

    26/10/2015 a las 19:24

  • No creo que la segunda cita sea realmente relevante. Las definiciones provisionales son una característica local de una unidad de traducción. es puramente compilador propiamente dicho característica, ningún enlazador involucrado. No tiene relación con el enlace externo y los modelos Ref/Def que lo acompañan. No veo la Justificación en 6.2.2 dibujando ninguna conexión entre los modelos Ref/Def y las definiciones tentativas.

    – Ant

    11 de noviembre de 2021 a las 7:27


avatar de usuario
R.. GitHub DEJAR DE AYUDAR A ICE

Aquí hay un ejemplo de un caso en el que es útil:

void (*a)();

void bar();
void foo()
{
    a = bar;
}

static void (*a)() = foo;

/* ... code that uses a ... */

El punto clave es que la definición de foo tiene que referirse a ay la definición de a tiene que referirse a foo. También deberían ser posibles ejemplos similares con estructuras inicializadas.

  • En este caso específico, podría evitar la necesidad de una declaración tentativa agregando un extern a la primera línea (por lo que es sólo una declaración). Donde realmente necesita la declaración tentativa es si quiere a ser – estar static (alcance del archivo)

    – Chris Dodd

    18/10/2015 a las 17:29

  • @ChrisDodd: Sí, me lo perdí. lo cambiaré Gracias.

    – R.. GitHub DEJA DE AYUDAR A ICE

    18/10/2015 a las 17:32

  • Otra forma de evitar esto sería void foo(); void (*a)() = foo;

    –MM

    23 de octubre de 2015 a las 4:43

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad