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

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

Я продолжаю задавать этот вопрос в интервью:

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

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

Итак, вопрос:

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

4b9b3361

Ответ 1

В стандартной функции С++ требуется функция main, поэтому вопрос не имеет смысла для стандартного С++.

Вне стандартного С++ вы можете, например, написать специальную программу Windows и использовать одну из функций запуска Microsoft (wMain, winMain, wWinmain). В Windows вы также можете написать программу как DLL и использовать rundll32 для ее запуска.

Кроме того, вы можете создать свою собственную небольшую библиотеку времени исполнения. Когда-то это был общий вид спорта.

Наконец, вы можете получить умные и возразить, что в соответствии со стандартным правилом ODR main не используется, поэтому любая программа подходит. Ба! Хотя, если у интервьюеров необычное хорошее чувство юмора (и они не задали бы вопрос, если бы у них было), они не подумают, что это хороший ответ.

Ответ 2

Нет, вы не можете, если вы не пишете программу в freestanding environment (ядро операционной среды встроенной среды и т.д.), где отправной точкой не должно быть main(). Согласно стандарту С++ main() является отправной точкой любой программы в hosted environment.

В соответствии с:

С++ 03 стандарт 3.6.1 Основная функция

1 Программа должна содержать глобальную функцию main, которая является назначенным началом программы. Реализация определяется, требуется ли программа в автономной среде для определения основной функции. [Примечание: в автономной среде, запуск и окончание - это реализация; startup содержит выполнение конструкторов для объектов области пространства имен со статической продолжительностью хранения; завершение содержит выполнение деструкторов для объектов со статической продолжительностью хранения.


Что такое freestanding environment и что такое hosted environment?
В стандарте С++ существует два типа совместимых реализаций; hosted и freestanding.

Реализация A freestanding - это программа, предназначенная для программ, которые выполняются без использования операционной системы.
Для Ex: Ядро ОС или встроенная среда будут автономной средой.

Программа, использующая средства операционной системы, обычно находится в hosted implementation.

Из С++ 03 Standard Раздел 1.4/7:

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

Далее,
Раздел: 17.4.1.3.2. Кавычки, выполненные отдельно:

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

18.1 Types <cstddef>   
18.2 Implementation properties <limits>   
18.3 Start and termination <cstdlib> 
18.4 Dynamic memory management <new> 
18.5 Type identification <typeinfo> 
18.6 Exception handling <exception> 
18.7 Other runtime support <cstdarg>

Ответ 3

Пример программы без видимой основной функции.

/* 
    7050925.c 
    $ gcc -o 7050925 7050925.c
*/

#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("How mainless!\n");
}

От: http://learnhacking.in/c-program-without-main-function/

Ответ 4

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

#include <stdio.h>

extern void _exit (register int code);

_start()
{
  int retval;
  retval = my_main ();
  _exit(retval);
}

int my_main(void)
{
  printf("Hello\n");
  return 0;
}

Скомпилируйте код с помощью

gcc -o no_main no_main.c -nostartfiles

-nostartfiles не будет включать файл запуска по умолчанию. Вы указываете основной файл записи с помощью _start.

main - не что иное, как предопределенная точка входа для кода пользователя. Поэтому вы можете это назвать, но в конце дня вам нужна точка входа. В языках C/С++ и других языках имя выбирается как main, если вы создаете другой язык или взламываете источники этих компиляторов языка, тогда вы можете изменить имя main на pain, но это принесет боль, поскольку оно будет нарушать стандарты.

Но манипулирование именем функции входа полезно для кода ядра, первой функции, запускаемой в ядре, или кода, написанного для встроенных систем.

Ответ 5

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

Ответ 6

Да, это возможно скомпилировать с основным, но вы не можете пройти фазу связи, хотя.

 g++ -c noMain.cpp -o noMain.o

Ответ 7

Да

$ cat > hwa.S
write = 0x04
exit  = 0xfc
.text
_start:
        movl    $1, %ebx
        lea     str, %ecx
        movl    $len, %edx
        movl    $write, %eax
        int     $0x80
        xorl    %ebx, %ebx
        movl    $exit, %eax
        int     $0x80
.data
str:    .ascii "Hello, world!\n"
len = . -str
.globl  _start
$ as -o hwa.o hwa.S
$ ld hwa.o
$ ./a.out
Hello, world!

Ядро, которое действительно запускает исполняемый файл, ничего не знает о внутренних символах, оно просто переносится в точку входа, указанную в двоичном виде в заголовке исполняемого файла.

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

Чтобы включить модуль C 1...

Mac:~/so$ cat > nomain.S
.text
.globl start
start:
        call   _notmain
Mac:~/so$ as -o nomain.o nomain.S
Mac:~/so$ cat > notmain.c
#include <unistd.h>

void notmain(void) {
  write(1, "hi\n", 3);
  _exit(0);
}
Mac:~/so$ cc -c notmain.c
Mac:~/so$ ld -w nomain.o notmain.o -lc
Mac:~/so$ ./a.out
hi


1. И я также переключаюсь на x86-64 здесь.

Ответ 8

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

struct MainSub
{
   MainSub()
   {
      // do some stuff
   }
};

MainSub mainSub;

int main(int argc, char *argv[]) { return 0; }

Что произойдет, так это то, что материал в конструкторе MainSub будет выполняться до выполнения неиспользуемого main, и вы можете поместить туда логику программы. Это, конечно, требует С++, а не C (также непонятно из вопроса).

Ответ 9

Я думаю, что ссылка на макрос была переименована в главную функцию, а не мой код и демонстрирует это. Компилятор все еще видит основную функцию, но технически там нет основной исходной точки зрения. Я получил его здесь http://www.exforsys.com/forum/c-and-c/96849-without-main-function-how-post412181.html#post412181

#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 ");
}

Ответ 10

Пока вы используете g++, вы можете изменить свою точку входа с помощью опции компоновщика -e, поэтому следующая команда кода и компиляции может позволить вам создать программу без функции main():

#import <iostream>

class NoMain
{
public:
    NoMain()
    {
        std::cout << "Hello World!" << std::endl;
        exit(0);
    }
} mainClass;

Я дал имя файла как noname.cpp, а параметр компиляции:

g++ nomain.cpp -Wl,-e,_mainClass -v

Честно говоря, я не совсем понял, почему код может работать нормально. Я подозреваю, что адрес глобальной переменной mainClass является тем же самым для конструктора класса NoMain. Тем не менее, у меня также есть несколько причин, по которым я могу сказать, что моя догадка не может исправить.

Ответ 11

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

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

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

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

Ответ 12

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

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

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

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

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

Ответ 13

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

int main[] {};

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

int main;

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

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

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

Ответ 14

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

Ответ 15

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

Они имели в виду:

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

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

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

Или они имели в виду:

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

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

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

Ответ 16

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

Ответ 17

Напишите класс и напечатайте свое имя в конструкторе этого класса и объявите GLOBAL OBJECT этого класса. Таким образом, конструктор класса выполняется до main. Таким образом, вы можете оставить основной пустой и еще напечатать свое имя.

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

MyClass gObj; // this will trigger the constructor.

int main()
{
   // nothing here...
}

Ответ 18

1) Использование макроса, определяющего основные

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

Вывод:

StackOverflow

2) Использование оператора привязки маркеров Вышеупомянутое решение имеет слово "главное" в нем. Если нам не разрешено даже писать основную информацию, мы используем оператор привязки маркеров (подробнее см. Это)

#include<stdio.h>
#define fun m##a##i##n
int fun()
{
printf("stackoverflow");
return 0;
}

Ответ 19

В соответствии со стандартами требуется 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 ");
}

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

# include <stdio.h>
# define m main

int m()
{
    printf("Hell0");
}