son - ¿Por qué el prototipo y la definición que no coinciden con una lista de argumentos vacía dan resultados diferentes en GCC y Clang?
que es el prototipo de una función en lenguaje c c++ (4)
Fragmento de código dado (simplificado):
void foo(int a, int b); // declaration with prototype
int main(void)
{
foo(1, 5); // type-checked call (i.e. because of previous prototype)
return 0;
}
void foo() // old-style definition (with empty argument list)
{
}
y opciones de línea de comando (aunque, como verifiqué, no son importantes):
-x c -std=c11 -pedantic -Wall
gcc 7.2 no puede compilarlo con el siguiente mensaje de error:
error: número de argumentos no coincide con el prototipo
mientras que clang 4.0 lo traduce sin ninguna queja.
¿Qué implementación es correcta según el estándar C? ¿Es válido que la definición antigua "cancele" el prototipo anterior?
(C11, 6.7p4 Restricciones) "Todas las declaraciones en el mismo ámbito que se refieran al mismo objeto o función deberán especificar tipos compatibles"
y
(C11, 6.7.6.3p14) "Una lista de identificadores declara solo los identificadores de los parámetros de la función. Una lista vacía en un declarador de función que es parte de una definición de esa función especifica que la función no tiene parámetros. .] "
Mi opinión es que se ha violado la restricción de 6.7p4 y se debe emitir un diagnóstico.
EDITAR:
como señalado por @hvd en realidad no es correcto. 6.7.6.3p14 no significa void foo() {}
proporciona un prototipo para foo
según DR#317 . En ese sentido, la restricción de 6.7p4 no se viola y, por lo tanto, es correcto no molestar a quejarse.
Desde el estándar C (6.7.6.3 declaradores de funciones (incluidos los prototipos))
15 Para que dos tipos de función sean compatibles, ambos deben especificar tipos de devolución compatibles.146) Además, ... Si un tipo tiene una lista de tipos de parámetros y el otro tipo está especificado por una definición de función que contiene una lista de identificadores (posiblemente vacía) , ambos coincidirán en el número de parámetros, y el tipo de cada parámetro prototipo será compatible con el tipo que resulta de la aplicación de las promociones de argumentos por defecto al tipo del identificador correspondiente. (En la determinación de la compatibilidad de tipo y de un tipo compuesto, cada parámetro declarado con función o tipo de matriz se considera que tiene el tipo ajustado y cada parámetro declarado con tipo calificado se considera que tiene la versión no calificada de su tipo declarado).
Y (6.2.7 tipo compatible y tipo compuesto)
2 Todas las declaraciones que se refieran al mismo objeto o función tendrán un tipo compatible; De lo contrario, el comportamiento es indefinido.
Por lo tanto, el programa mostrado en la pregunta tiene un comportamiento indefinido. El compilador puede emitir un mensaje de diagnóstico como lo hizo GCC.
Descargo de responsabilidad: no soy un language-lawyer , pero juego uno en .
Si el compilador no emite un diagnóstico, sería no conforme, y podría considerarse un error si el compilador afirma que está de acuerdo.
C.2011§6.7.6.3¶14 (énfasis mío):
Una lista de identificadores declara solo los identificadores de los parámetros de la función. Una lista vacía en un declarador de función que forma parte de una definición de esa función especifica que la función no tiene parámetros. La lista vacía en un declarador de función que no forma parte de una definición de esa función especifica que no se proporciona información sobre el número o los tipos de parámetros.
Por lo tanto, la definición de foo
no especifica parámetros, mientras que la declaración de foo
anteriormente especificó dos parámetros.
C.2011§6.7.6.3¶15:
Para que dos tipos de funciones sean compatibles, ambos deben especificar tipos de retorno compatibles. 146) Además, las listas de tipos de parámetros, si ambas están presentes , coincidirán en el número de parámetros y en el uso del terminador de puntos suspensivos; Los parámetros correspondientes tendrán tipos compatibles.
146) Si ambos tipos de funciones son "estilo antiguo", los tipos de parámetros no se comparan.
Por lo tanto, los dos declaradores de foo
no son compatibles.
¡Dang! Del comentario de @ hvd :
Está bien establecido que
void foo()
no proporciona un prototipo, incluso en una definición. DR#317 El tipo defoo
en esa definición esvoid foo()
, novoid foo(void)
, yvoid foo()
yvoid foo(int, int)
son tipos compatibles. Esta respuesta es incorrecta.
La parte resaltada del texto anterior del estándar es la laguna que permite el desacuerdo en el número de argumentos, pero los tipos compatibles. Aunque la definición de la función especifica una función que no toma argumentos, ya que la lista de tipos de parámetros realmente falta, en realidad no hay incompatibilidad entre el tipo de foo
en su prototipo de función y el tipo de foo
en la definición de función.
Por lo tanto, clang 4.0 parece tener razón, ya que no hay una violación de restricción.
Mi argumento original se vuelve inválido, por lo que elimino esa parte de mi respuesta original.
En los comentarios presentaste el siguiente ejemplo:
void foo () {}
int main () { foo(1, 2); return 0; }
Y preguntó por qué el compilador no se queja por este caso. Esto se trata en realidad here , pero en pocas palabras: C.2011 aún acepta la sintaxis de definición de función K&R C. Entonces, mientras void foo() {}
es una definición que no acepta argumentos, el prototipo que se usa para la validación de argumentos es el mismo que void foo();
, porque la lista de argumentos vacía se analiza como K&R. La sintaxis de C moderna para forzar la verificación correcta de los argumentos sería usar void foo(void) {}
lugar.
No tengo una cita de un estándar ( edite: consulte C11
, capítulo 6.7.6.3/P14 ), pero según mi entendimiento, gcc
tiene razón para gritar, ya que usted se contradice.
Prometió que en la definición de la función, en la lista de declaración, tendrá dos parámetros de tipo int
, pero no están allí. En el caso de una definición de función, y una lista vacía significa que la función no debe tomar ningún parámetro. Así que hay una violación de restricción y gcc
tiene razón para quejarse.
Parece que este es un problema en clang que no produce una advertencia al menos.
Citas:
Capítulo §6.7, P4 ( Restricciones )
Todas las declaraciones en el mismo ámbito que se refieran al mismo objeto o función deberán especificar tipos compatibles.
luego, el capítulo §6.7.6.3, P14,
Una lista de identificadores declara solo los identificadores de los parámetros de la función. Una lista vacía en un declarador de función que forma parte de una definición de esa función especifica que la función no tiene parámetros. La lista vacía en un declarador de función que no forma parte de una definición de esa función especifica que no se proporciona información sobre el número o los tipos de parámetros.
Por lo tanto, esto constituye una infracción de restricción y justifica que se emita un diagnóstico.