Pregunta

Para ser verdaderamente compatible con los estándares, ¿todas las funciones en C (excepto la principal) deben tener un prototipo, incluso si solo se usan después de su definición en la misma unidad de traducción?

¿Fue útil?

Solución

Depende de lo que quiera decir con "realmente compatible con los estándares". Sin embargo, la respuesta breve es "es una buena idea asegurarse de que todas las funciones tengan un prototipo de alcance antes de ser utilizadas".

Una respuesta más calificada señala que si la función acepta argumentos variables (especialmente la familia de funciones printf () ), entonces un prototipo debe estar dentro del alcance para cumplir estrictamente con los estándares. Esto es cierto para C89 (de ANSI) y C90 (de ISO; lo mismo que C89 excepto por la numeración de la sección). Sin embargo, aparte de las funciones 'varargs', las funciones que devuelven un int no tienen que declararse, y las funciones que devuelven algo que no sea un int necesitan una declaración que muestre el tipo de retorno pero no necesita el prototipo para la lista de argumentos.

Tenga en cuenta, sin embargo, que si la función toma argumentos que están sujetos a 'promociones normales' en ausencia de prototipos (por ejemplo, una función que toma un char o short : ambos se convierten a int ; más en serio, tal vez, una función que toma un float en lugar de un double ), luego Se necesita un prototipo. El estándar era laxo sobre esto para permitir que el código C antiguo se compilara en compiladores conformes estándar; el código anterior no se escribió para preocuparse por garantizar que las funciones se declararan antes de su uso, y por definición, el código anterior no usaba prototipos ya que no estaban disponibles en C hasta que hubo un estándar.

C99 no permite 'int implícito' ... eso significa ambos casos extraños como ' static a; ' (un int por defecto) y también declaraciones de funciones implícitas. Estos se mencionan (junto con otros 50 cambios importantes) en el prólogo de ISO / IEC 9899: 1999, que compara ese estándar con las versiones anteriores:

  
      
  • eliminar int implícito
      & # 8230;
  •   
  • eliminar declaración de función implícita
  •   

En ISO / IEC 9899: 1990, & # 167; 6.3.2.2 Llamadas de función declaró:

  

Si la expresión que precede a la lista de argumentos entre paréntesis en una llamada a función consiste   únicamente de un identificador, y si no hay una declaración visible para este identificador, el identificador está implícitamente   declarada exactamente como si, en el bloque más interno que contiene la llamada a la función, la declaración:

extern int identifier();
     

apareció.38

     

38 Es decir, un identificador con alcance de bloque declarado tener enlace externo con función de tipo sin   información de parámetros y devolver un int . Si, de hecho, no se define como que tiene el tipo & # 8220; función   devolviendo int , & # 8221; el comportamiento es indefinido.

Este párrafo falta en la norma de 1999. No he (todavía) rastreado el cambio de verborrea que permite static a; en C90 y no lo permite (requiere static int a; ) en C99.

Tenga en cuenta que si una función es estática, puede definirse antes de usarse y no es necesario que vaya precedida de una declaración. Se puede persuadir a GCC para que se marchite si se define una función no estática sin una declaración que la preceda ( -Wmissing-prototypes ).

Otros consejos

Un prototipo es una declaración de función que especifica los tipos de parámetros de la función.

Pre-ANSI C (el lenguaje descrito por la primera edición de 1978 de Kernighan & amp; Ritchie's " The C Programming Language ") no tenía prototipos; no era posible que una declaración de función describiera el número o los tipos de los parámetros. Le correspondía a la persona que llamaba pasar el número correcto y el tipo de argumentos.

ANSI C introdujo "prototipos", declaraciones que especifican los tipos de los parámetros (una característica prestada de principios de C ++).

A partir de C89 / C90 (los estándares ANSI e ISO describen el mismo lenguaje), es legal llamar a una función sin declaración visible; Se proporciona una declaración implícita. Si la declaración implícita es incompatible con la definición real (digamos, llamando a sqrt (" foo ") , entonces el comportamiento no está definido. Ni esta declaración implícita ni una declaración no prototipo pueden ser compatibles con un función variable, por lo que cualquier llamada a una función variable (como printf o scanf ) debe tener un prototipo visible.

C99 descartó declaraciones implícitas. Cualquier llamada a una función sin una declaración visible es una violación de restricción, que requiere un diagnóstico del compilador. Pero esa declaración aún no se requiere para ser un prototipo; puede ser una declaración de estilo antiguo que no especifica tipos de parámetros.

C11 no realizó cambios significativos en esta área.

Entonces, incluso a partir del estándar ISO C de 2011, las declaraciones y definiciones de funciones antiguas (que han sido "obsoletas" desde 1989) todavía están permitidas en el código conforme.

Para todas las versiones de C que se remontan a 1989, por cuestiones de estilo, hay muy pocas razones para no usar prototipos para todas las funciones. Las declaraciones y definiciones de estilo antiguo se mantienen solo para evitar romper el código antiguo.

No, las funciones no siempre necesitan un prototipo. El único requisito es que una función se declare " antes de usarlo. Hay dos formas de declarar una función: escribir un prototipo, o escribir la función en sí (llamada " definición. & Quot;) Una definición siempre es una declaración, pero no todas las declaraciones son definiciones.

Sí, cada función debe tener un prototipo, pero ese prototipo puede aparecer en una declaración separada o como parte de la definición de la función. Las definiciones de funciones escritas en C89 y versiones posteriores naturalmente tienen prototipos, pero si escribe cosas en el estilo clásico de K & amp; R, entonces:

main (argc, argv)

  int argc;
  char **argv;

{
  ...
}

entonces la definición de función no tiene prototipo. Si escribe el estilo ANSI C (C89), entonces:

main (int argc, char **argv) { ... }

entonces la definición de la función tiene un prototipo.

Un buen consejo al escribir nuevas funciones es escribirlas al revés con main en la parte inferior para que cuando cambie de opinión sobre los argumentos de la función o el tipo de retorno no tenga que arreglar el prototipo también. La fijación constante de prototipos y el manejo de todas las advertencias del compilador cuando están desactualizados se vuelve realmente tedioso.

Una vez que sus funciones funcionen bien juntas, mueva el código a un módulo bien nombrado y coloque los prototipos en un archivo .h del mismo nombre. Se ahorra mucho tiempo. La mayor ayuda de productividad que he encontrado en 5 años.

A lo mejor de mi conocimiento (en ANSI C89 / ISO C90), no. No estoy seguro acerca de C99; Sin embargo, esperaría lo mismo.

Nota personal: solo escribo prototipos de funciones cuando ...

  1. Necesito (cuando A () llama a B () y B () llama a A ()), o
  2. Estoy exportando la función; de lo contrario, se siente superfluo.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top