variable - why static functions are used in c




¿Por qué declara main como una compilación de matriz? (4)

Vi un fragmento de código en CodeGolf que pretende ser una bomba compiladora, donde main se declara como una gran matriz. Probé la siguiente versión (sin bomba):

int main[1] = { 0 };

Parece compilar bien bajo Clang y con solo una advertencia bajo GCC:

advertencia: 'main' suele ser una función [-Wmain]

El binario resultante es, por supuesto, basura.

Pero, ¿por qué se compila en absoluto? ¿Está incluso permitido por la especificación C? La sección que creo que es relevante dice:

5.1.2.2.1 Inicio del programa

La función llamada al inicio del programa se llama main. La implementación no declara ningún prototipo para esta función. Se definirá con un tipo de retorno int y sin parámetros [...] o con dos parámetros [...] o de alguna otra manera definida por la implementación.

¿"Alguna otra manera definida por la implementación" incluye una matriz global? (Me parece que la especificación todavía se refiere a una función ).

Si no, ¿es una extensión del compilador? ¿O una característica de las cadenas de herramientas, que tiene algún otro propósito y decidieron ponerla a disposición a través de la interfaz?


El problema es que main no es un identificador reservado. El estándar C solo dice que en los sistemas alojados generalmente hay una función llamada main. Pero nada en el estándar le impide abusar del mismo identificador para otros fines siniestros.

GCC le da una advertencia petulante "main es generalmente una función", insinuando que el uso del identificador main para otros propósitos no relacionados no es una idea brillante.

Ejemplo tonto:

#include <stdio.h>

int main (void)
{
  int main = 5;
  main:

  printf("%d\n", main);
  main--;

  if(main)
  {
    goto main;
  }
  else
  {
    int main (void);
    main();
  }
}

Este programa imprimirá repetidamente los números 5,4,3,2,1 hasta que se desborde la pila y se bloquee (no intente esto en casa). Desafortunadamente, el programa anterior es un programa C estrictamente conforme y el compilador no puede evitar que lo escriba.


Es porque C permite un entorno "no alojado" o independiente que no requiere la función main . Esto significa que el nombre main se libera para otros usos. Es por eso que el lenguaje como tal permite tales declaraciones. La mayoría de los compiladores están diseñados para admitir ambos (la diferencia es principalmente cómo se realiza la vinculación) y, por lo tanto, no rechazan construcciones que serían ilegales en el entorno alojado.

La sección a la que se refiere en el estándar se refiere al entorno alojado, el correspondiente para independiente es:

En un entorno independiente (en el que la ejecución del programa C puede tener lugar sin ningún beneficio de un sistema operativo), el nombre y el tipo de la función llamada al inicio del programa están definidos por la implementación. Cualquier instalación de biblioteca disponible para un programa independiente, que no sea el conjunto mínimo requerido por la cláusula 4, está definida por la implementación.

Si luego lo vincula como de costumbre, se volverá malo ya que el vinculador normalmente tiene poco conocimiento sobre la naturaleza de los símbolos (qué tipo tiene o incluso si es una función o variable). En este caso, el enlazador resolverá felizmente las llamadas a main a la variable llamada main . Si no se encuentra el símbolo, dará como resultado un error de enlace.

Si lo está vinculando como de costumbre, básicamente está tratando de usar el compilador en la operación alojada y luego no define main como se supone que significa un comportamiento indefinido según el apéndice J.2:

el comportamiento no está definido en las siguientes circunstancias:

  • ...
  • El programa en un entorno alojado no define una función llamada main utilizando uno de los formularios especificados (5.1.2.2.1)

El propósito de la posibilidad independiente es poder usar C en entornos donde (por ejemplo) bibliotecas estándar o inicialización CRT no se proporciona. Esto significa que el código que se ejecuta antes de que se llame a main (esa es la inicialización de CRT que inicializa el tiempo de ejecución de C) podría no proporcionarse y se espera que lo proporcione usted mismo (y puede decidir tener un main o decidir no hacerlo) .


Solo se compila porque no usa las opciones adecuadas (y funciona porque los enlazadores a veces solo se preocupan por los nombres de los símbolos, no por su tipo ).

$ gcc -std=c89 -pedantic -Wall x.c
x.c:1:5: warning: ISO C forbids zero-size array main [-Wpedantic]
 int main[0];
     ^
x.c:1:5: warning: main is usually a function [-Wmain]

main es, después de compilar, solo otro símbolo en un archivo de objeto como muchos otros (funciones globales, variables globales, etc.).

El vinculador vinculará el símbolo main independientemente de su tipo. De hecho, el enlazador no puede ver el tipo de símbolo ( puede ver que no está en la .text .text, pero no le importa).

Con gcc, el punto de entrada estándar es _start, que a su vez llama a main () después de preparar el entorno de ejecución. Por lo tanto, saltará a la dirección de la matriz de enteros, lo que generalmente dará como resultado una mala instrucción, un defecto o algún otro mal comportamiento.

Todo esto, por supuesto, no tiene nada que ver con el estándar C.





main