Можно ли написать программу без использования функции main ()? - PullRequest
32 голосов
/ 13 августа 2011

Мне постоянно задают этот вопрос в интервью:

Написать программу без использования функции main()?

Один из моих друзей показал мне код с помощью макросов, но я не мог этого понять.

Так что вопрос:

Реально ли написать и скомпилировать программу без main()?

Ответы [ 20 ]

1 голос
/ 13 августа 2011

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

Для старой школы c языком по умолчанию было что-то вроде 'start' или '_start', определенное в так называемой crt (c runtime?), Которая выполняет несколько домашних работ, необходимых для стандартных функций c, таких как подготовка кучи памяти, инициализация областей статических переменных, синтаксический анализ командной строки в argc / argv и т. д.

Возможно, вы могли бы переопределить функцию точки входа, если вы позаботились о том, чтобы не использовать стандартные функции, которые требуют тех бытовых вещей (например, malloc (), free (), printf (), любые определения классов имеют собственный конструктор, .. .) Довольно ограниченно, но не невозможно, если вы используете функции, предоставляемые o / s, а не стандартным временем выполнения c.

Например, вы можете создать простой helloworld, используя функцию write () в дескрипторе 1.

1 голос
/ 28 октября 2014

Я понимаю, что это старый вопрос, но я только что узнал об этом и должен был поделиться.Вероятно, он не будет работать со всеми компоновщиками, но, по крайней мере, можно обмануть ld (я использую версию 2.24.51.20140918), думая, что есть функция main- , выполнив это:

int main[] {};

или даже просто

int main;

Затем вы можете применить один из вышеупомянутых трюков, чтобы позволить программе выполнить некоторый код, например, с помощью конструктора:

struct Main
{
    Main()
    {
        cout << "Hello World!\n";
        exit(0);
    }
} main_;

exit(0) предназначен для предотвращения "вызова" массива.Хорошо повеселиться: -)

1 голос
/ 13 августа 2011

Когда код на C или C ++ выполняется, он выполняется по известному начальному адресу, здесь код инициализирует среду выполнения, инициализирует указатель стека, выполняет инициализацию данных, вызывает статические конструкторы, , а затем переходит к Основной ().

Код, который делает это, связан с вашим кодом во время сборки компоновщиком. В GCC он обычно находится в crt0.s, с коммерческим компилятором маловероятно, что этот код будет вам доступен.

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

Если вы пишете код для «голой» системы без ОС или, по крайней мере, без ОС в смысле загрузчика процессов (встроенные системы часто включают ядро ​​RTOS, которое запускается после main ()), то теоретически вы можете вызывать точку входа в C-код как хотите, так как обычно у вас есть полный контроль над кодом запуска во время выполнения. Но делать это было бы глупо и несколько извращенно.

Некоторые среды RTOS, такие как VxWorks, и большинство каркасов приложений в целом включают main ()) или его эквивалент) в свой код библиотеки, чтобы он выполнялся перед кодом пользовательского приложения. Например, приложения VxWorks запускаются из usrAppInit (), а приложения Win32 запускаются из WinMain ().

0 голосов
/ 09 сентября 2014

1) Использование макроса, который определяет main

#include<stdio.h>
#define fun main
int fun(void)
{
printf("stackoverfow");
return 0;
}

Выход:

StackOverflow

2) Использование оператора вставки токенов В приведенном выше решении есть слово «основной». Если нам не разрешено даже писать main, мы можем использовать оператор вставки токена (подробнее см. Здесь)

#include<stdio.h>
#define fun m##a##i##n
int fun()
{
printf("stackoverflow");
return 0;
}
0 голосов
/ 19 декабря 2012

Напишите класс и напечатайте свое имя в конструкторе этого класса и объявите ГЛОБАЛЬНЫЙ ОБЪЕКТ этого класса.Таким образом, конструктор класса выполняется перед main.Таким образом, вы можете оставить основное поле пустым и при этом напечатать свое имя.

class MyClass
{
   myClass()
   {
       cout << "printing my name..." <<endl;
   }
};

MyClass gObj; // this will trigger the constructor.

int main()
{
   // nothing here...
}
0 голосов
/ 13 августа 2011

Может быть, можно скомпилировать раздел .data и заполнить его кодом?

0 голосов
/ 14 августа 2011

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

#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)

int begin()
{
    printf(" hello ");
}

Здесь main написан трюками макросов.Это может быть не сразу понятно, но в конечном итоге приводит к главному.Если это правильный ответ на ваш вопрос, это можно сделать очень легко, например, так:

# include <stdio.h>
# define m main

int m()
{
    printf("Hell0");
}
0 голосов
/ 14 августа 2011

Функция main - это только метка по умолчанию для адреса, с которого программа начнет выполнение. Технически, да, это возможно, но вы должны установить имя функции, которая начнет выполнение в вашей среде.

0 голосов
/ 16 января 2018

да, можно написать программу без main ().

Но она косвенно использует main ().

следующая программа поможет вам понять ..

#include<stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,r,e)
int begin()
{
printf(” you are inside main() which is hidden“);
}

Оператор ## называется операцией вставки токена или объединения токена.То есть мы можем объединить два или более символов с ним.

Во 2-й строке программы -

определить decode (s, t, u, m, p, e, d) m## s ## u ## t

Что препроцессор делает здесь.Макодекод (s, t, u, m, p, e, d) расширяется как «msut» (оператор ## объединяет m, s, u & t в msut).Логика заключается в том, что когда вы передаете (s, t, u, m, p, e, d) в качестве аргумента, он объединяет 4-й, 1-й, 3-й и 2-й символы (токены)

Теперь посмотрите на третью строкупрограммы -

определение начала декодирования (a, n, i, m, a, r, e)

Здесь препроцессор заменяет макрос «begin» расширением декодирования (a,п, я, м, а, г, д).В соответствии с определением макроса в предыдущей строке аргумент должен быть расширен таким образом, чтобы 4-й, 1-й, 3-й и 2-й символы были объединены.В аргументе (a, n, i, m, a, r, e) 4-й, 1-й, 3-й и 2-й символы - это «m», «a», «i» и «n».

поэтому он заменит begin by main () препроцессором до того, как программа будет передана компилятору.Вот и все ...

0 голосов
/ 13 августа 2011

Это зависит от того, что они означают.

Они означали:

Напишите программу без функции main ().

Тогда, вообще говорянет.
Но есть способы обмана.

  • Вы можете использовать препроцессор, чтобы скрыть main () на виду.
  • Большинство компиляторов позволяют вам указатьточка входа в ваш код.
    По умолчанию это main (int, char * []) ​​

Или они означают:

Написать программу, которая запускаетсякод без использования main (для запуска вашего кода).

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

Примечание. Компилятору разрешено оптимизировать эти объекты для отложенной загрузки (нообычно нет) но для безопасности просто поместите глобал в тот же файл, что и основная функция (которая может быть пустой).

...