Подтвердить что ты не робот

Почему объявление main как массива компилируется?

Я увидел фрагмент кода на CodeGolf, который предназначен как бомба компилятора, где main объявлен как огромный массив. Я попробовал следующую (без бомбы) версию:

int main[1] = { 0 };

Кажется, он компилируется под Clang и с предупреждением только в GCC:

предупреждение: "main" обычно является функцией [-Wmain]

Результирующий двоичный файл - это, конечно, мусор.

Но почему он вообще компилируется? Это даже разрешено спецификацией C? Раздел, который, по моему мнению, имеет значение, говорит:

5.1.2.2.1 Запуск программы

Функция, вызванная при запуске программы, называется main. Реализация не объявляет прототипа для этой функции. Он должен быть определен с типом возврата int и без параметров [...] или с двумя параметрами [...] или каким-либо другим способом реализации.

Включает ли какой-либо другой способ реализации определенный глобальный массив? (Мне кажется, что спецификация все еще относится к функции.)

Если нет, то это расширение компилятора? Или особенность инструментальных цепей, которая служит другим целям, и они решили сделать ее доступной через интерфейс?

4b9b3361

Ответ 1

Это потому, что C допускает "не-размещенную" или автономную среду, для которой не требуется функция main. Это означает, что имя main освобождается для других целей. Вот почему язык как таковой допускает такие заявления. Большинство компиляторов предназначены для поддержки обоих (разница в основном заключается в том, как выполняется привязка), и поэтому они не запрещают конструкции, которые были бы незаконными в размещенной среде.

Раздел, на который вы ссылаетесь в стандарте, относится к размещенной среде, соответствующая для автономного:

в автономной среде (в которой выполнение программы C может осуществляться без каких-либо преимущество операционной системы), имя и тип функции, вызываемой в программе запуск осуществляется с помощью реализации. Любые библиотеки, доступные для автономного программа, отличная от минимального набора, требуемого в соответствии с пунктом 4, определяется реализацией.

Если вы тогда свяжете его, как обычно, это будет плохо, поскольку линкер обычно мало знает о характере символов (какой тип он имеет или даже если это функция или переменная). В этом случае компоновщик будет с радостью разрешать вызовы main к переменной с именем main. Если символ не найден, это приведет к ошибке ссылки.

Если вы связываете его как обычно, вы в основном пытаетесь использовать компилятор в размещенной операции, а затем не определяете main, поскольку вы должны использовать поведение undefined в соответствии с приложением J.2:

поведение undefined в следующих случаях:

  • ...
  • в размещенной среде не определяется функция с именем главный используя один указанных форм (5.1.2.2.1)

Цель бесплатной возможности - использовать C в средах, где (например) не указаны стандартные библиотеки или инициализация CRT. Это означает, что код, который запускается до main, вызывается (что инициализация CRT, которая инициализирует среду выполнения C), может не предоставляться, и вы должны были предоставить это сами (и вы можете решить, иметь main или может решите не делать этого).

Ответ 2

Если вам интересно, как создать программу в основном массиве: https://jroweboy.github.io/c/asm/2015/01/26/when-is-main-not-a-function.html. В исходном примере имеется только массив char (и более поздний int) с именем main, который заполняется машинными инструкциями.

Основные шаги и проблемы:

  • Получить машинные инструкции основной функции из дампа памяти gdb и скопировать его в массив
  • Пометьте данные в main[] исполняемом, объявив его const (данные, по-видимому, доступны для записи или выполнения)
  • Последняя деталь: измените адрес для фактических данных строки.

Полученный C-код просто

const int main[] = {
    -443987883, 440, 113408, -1922629632,
    4149, 899584, 84869120, 15544,
    266023168, 1818576901, 1461743468, 1684828783,
    -1017312735
};

но приводит к выполнению исполняемой программы на 64-битном ПК:

$ gcc -Wall final_array.c -o sixth
final_array.c:1:11: warning: ‘main’ is usually a function [-Wmain]
 const int main[] = {
           ^
$ ./sixth 
Hello World!

Ответ 3

main - после компиляции - просто еще один символ в объектном файле, как и многие другие (глобальные функции, глобальные переменные и т.д.).

Компонент свяжет символ main независимо от его типа. Действительно, компоновщик вообще не может видеть тип символа (он может видеть, что он не находится в разделе .text, но ему все равно;))

С помощью gcc стандартная точка входа - _start, которая, в свою очередь, вызывает main() после подготовки среды выполнения. Таким образом, он будет переходить к адресу целочисленного массива, что обычно приводит к плохой инструкции, segfault или другому плохому поведению.

Это, конечно, не имеет ничего общего с C-стандартом.

Ответ 4

Проблема заключается в том, что main не является зарезервированным идентификатором. Стандарт C только говорит, что в размещенных системах обычно есть функция main. Но ничто в стандарте не позволяет вам злоупотреблять одним и тем же идентификатором для других зловещих целей.

GCC дает вам самодовольное предупреждение "главное, как правило, функция", намекая, что использование идентификатора main для других несвязанных целей не является блестящей идеей.


Глупый пример:

#include <stdio.h>

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

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

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

Эта программа будет многократно печатать числа 5,4,3,2,1, пока не получится переполнение стека и не сработает (не пробуйте это у себя дома). К сожалению, вышеуказанная программа является строго соответствующей программе на языке C, и компилятор не может помешать вам написать ее.

Ответ 5

Он компилируется только потому, что вы не используете правильные параметры (и работает, потому что линкерам иногда нужны только имена символов, а не их тип).

$ 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]