Czy jest możliwe napisanie programu bez użycia funkcji main ()?

Ciągle to pytanie zadawane jest w wywiadach:

Napisać program bez użycia funkcji main()?

Jeden z moich znajomych pokazał mi jakiś kod używając makr, ale nie mogłem go zrozumieć.

Więc pytanie brzmi:

Czy naprawdę można napisać i skompilować program bez main()?

Author: Alok Save, 2011-08-13

20 answers

W standardowym C++ wymagana jest funkcja main, więc pytanie nie ma sensu dla standardowego C++.

Poza standardowym C++ można na przykład napisać program specyficzny dla systemu Windows i użyć jednej z niestandardowych funkcji startowych Microsoftu (wmain, winMain, wWinmain). W systemie Windows możesz również napisać program jako DLL i użyć rundll32, aby go uruchomić.

Poza tym możesz stworzyć własną bibliotekę runtime. Kiedyś był to sport powszechny.

Wreszcie możesz zmądrzej i powtórz, że zgodnie ze standardową regułą ODR main nie jest "używany" , więc każdy program się kwalifikuje. Bah! Chociaż jeśli ankieterzy nie mają niezwykłego poczucia humoru (i nie zadawaliby pytania, gdyby tak było), nie pomyślą, że to dobra odpowiedź.

 17
Author: Cheers and hth. - Alf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 19:58:01

Nie możesz, chyba że piszesz program w freestanding environment (jądro systemu operacyjnego wbudowanego itp.), gdzie punkt wyjścia nie musi być main(). Zgodnie ze standardem C++ main() jest punktem wyjścia dowolnego programu w hosted environment.

Zgodnie z:

C++03 standard 3.6.1 Main function

1 program zawiera globalną funkcję o nazwie main, która jest wyznaczonym startem programu. Jest to implementacja-definiowana czy program w do zdefiniowania głównej funkcji wymagane jest środowisko wolnostojące. [Uwaga: w środowisku wolnostojącym, rozruchu i terminacja jest zdefiniowana w implementacji; Start zawiera wykonanie konstruktorów dla obiektów o zasięgu przestrzeni nazw ze statycznym czasem przechowywania; terminacja zawiera wykonanie destruktorów dla obiektów o statycznym czasie przechowywania.


Co to jest freestanding Environment & Co to jest Hosted Environment?
Istnieją dwa rodzaje zgodnych implementacji zdefiniowanych w C++ standard; hosted i freestanding.

A freestanding implementacja jest przeznaczona dla programów, które są wykonywane bez użycia systemu operacyjnego.
Dla Ex: jądro systemu operacyjnego lub środowisko wbudowane byłoby środowiskiem wolnostojącym.

Program korzystający z udogodnień systemu operacyjnego zwykle znajduje się w hosted implementation.

Z sekcji C++03 Standard 1.4/7:

Implementacja wolnostojąca to taka, w której wykonanie może odbywa się bez korzyści systemu operacyjnego i ma zdefiniowany przez implementację zestaw bibliotek, który zawiera pewne biblioteki obsługujące język.

Dalej,
sekcja: 17.4.1.3.2 implementacje wolnostojące cytaty:

Implementacja wolnostojąca ma zdefiniowany przez implementację zestaw nagłówków. Zestaw ten zawiera co najmniej następujące nagłówki, jak pokazano w Tabeli:

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>
 25
Author: Alok Save,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-05 14:31:53

Przykładowy program Bez widocznej głównej funkcji.

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

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

 14
Author: miku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 14:29:01

main oznacza punkt wejścia, punkt, od którego twój kod rozpocznie wykonywanie. chociaż {[2] } nie jest pierwszą uruchomioną funkcją. Jest jeszcze trochę kodu, który uruchamia się przed main i przygotowuje środowisko do uruchomienia kodu. Kod ten wywołuje main. Możesz zmienić nazwę funkcji main, rekompilując Kod pliku startowego crt0.c i zmieniając nazwę funkcji main. Możesz też wykonać następujące czynności:

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

Skompiluj kod z:

gcc -o no_main no_main.c -nostartfiles

-nostartfiles nie będzie zawierać domyślnego pliku startowego. Do pliku głównego wpisujesz _start.

main jest niczym innym jak predefiniowanym punktem wejścia dla kodu użytkownika. Dlatego możesz go nazwać jak chcesz, ale na koniec dnia potrzebujesz punktu wejścia. W C / C++ i innych językach nazwa jest zaznaczona jako main jeśli utworzysz inny język lub zhakujesz źródła tych kompilatorów języka, możesz zmienić nazwę main na pain, ale spowoduje to ból, ponieważ będzie naruszać standardy.

Ale manipulowanie nazwą funkcji wejściowej jest przydatne dla kodu jądra, pierwszej funkcji uruchomionej w jądrze, lub kodu napisanego dla systemów wbudowanych.

 10
Author: phoxis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-14 04:47:35

Mogą odnosić się do programu napisanego dla wolnostojącej implementacji. Standard C++ definiuje dwa rodzaje implementacji. Jedną z nich jest implementacja hostowana . Programy napisane dla tych implementacji muszą posiadać funkcję main. Poza tym nie jest wymagana żadna funkcja main, jeśli wolnostojąca implementacja jej nie wymaga. Jest to przydatne w przypadku jąder systemu operacyjnego lub wbudowanych programów systemowych, które nie działają w systemie operacyjnym.

 8
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 14:50:01

Tak, można skompilować z głównym, ale nie można przejść fazy łączenia.

 g++ -c noMain.cpp -o noMain.o
 5
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 14:14:37

Tak

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

Jądro, które naprawdę uruchamia plik wykonywalny, nie wie nic o symbolach wewnętrznych, po prostu przenosi się do punktu wejścia określonego w pliku binarnym w nagłówku pliku wykonywalnego.

Powodem, dla którego potrzebujesz main jest to, że normalnie twój "główny program" jest tak naprawdę tylko kolejnym modułem. Punkt wejścia znajduje się w dostarczonym przez Bibliotekę kodzie startowym napisanym w jakiejś kombinacji C i assembly, a ten kod biblioteki po prostu wywołuje main, więc zwykle musisz go podać. / Align = "left" / linker bezpośrednio, a Ty Nie.

Aby dołączyć moduł C1...

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. Przechodzę też na x86-64.
 5
Author: DigitalRoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 19:42:51

"bez użycia main" może również oznaczać, że żadna logika nie jest dozwolona w main, ale sama main istnieje. Mogę sobie wyobrazić, że pytanie zostało wyjaśnione, ale ponieważ nie jest wyjaśnione tutaj, jest to inna możliwa odpowiedź:

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

MainSub mainSub;

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

To, co się tutaj stanie, to to, że rzeczy w konstruktorze MainSub wykonają się przed wykonaniem bezużytecznego main i możesz tam umieścić logikę programu. To oczywiście wymaga C++, a nie C (również nie jest jasne z pytania).

 3
Author: eran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 15:20:34

Myślę, że odwołanie do makra polegało na zmianie nazwy głównej funkcji, poniższy kod nie jest moim kodem i to demonstruje. Kompilator nadal widzi główną funkcję, ale technicznie nie ma głównej z punktu widzenia źródła. Mam to tutaj.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 ");
}
 2
Author: eon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 14:20:39

Tak długo, jak używasz g++, możesz zmienić swój punkt wejścia za pomocą opcji linker -e, więc poniższy kod i Komenda compile mogą pozwolić Ci stworzyć program bez funkcji main():

#import <iostream>

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

Podałem nazwę pliku jako noname.cpp, A opcja kompilacji to:

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

Prawdę mówiąc, nie do końca rozumiałem, dlaczego kod może działać poprawnie. Podejrzewam, że adres zmiennej globalnej mainClass jest taki sam dla konstruktora klasy NoMain. Jednak mam też kilka powodów, dla których domyślam się, że może się nie zgadzać.

 2
Author: Joohae Kim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-26 19:09:26

Pomijając specyficzne standardy językowe, większość linkujących loader zapewnia pewne sposoby deklarowania nazwy funkcji (punktu wejścia), która musi być wykonana podczas ładowania binarnego jest ładowana do pamięci.

Dla języka old school C, domyślnym było coś w rodzaju "start" lub "_start", zdefiniowane w tzw CRT (C runtime?), który wykonuje kilka zadań potrzebnych dla standardowych funkcji c, takich jak przygotowanie sterty pamięci, inicjalizacja statycznych obszarów zmiennych, parsowanie linii poleceń do argc / argv itp.

Możliwe, że możesz nadpisać funkcję punktu wejścia, jeśli będziesz wystarczająco ostrożny, aby nie używać standardowych funkcji, które wymagają tych rzeczy domowych (np. malloc (), free (), printf (), dowolne definicje klas mają własny konstruktor, ...) Dość restrykcyjne, ale nie niemożliwe, jeśli używasz funkcji dostarczanych przez o/s, a nie przez standardowe środowisko uruchomieniowe C.

Na przykład, możesz utworzyć prosty helloworld używając funkcji write () na deskryptorze 1.

 1
Author: shr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 15:17:25

Gdy kod C lub c++ uruchamia się pod znanym adresem startowym, kod inicjuje środowisko pracy, inicjuje wskaźnik stosu, inicjalizuje dane, wywołuje statyczne konstruktory, następnie przeskakuje do main().

Kod, który to robi, jest powiązany z Twoim kodem w czasie kompilacji przez linkera. W GCC jest zwykle w crt0.s, z komercyjnym kompilatorem jest mało prawdopodobne, aby ten kod był dostępny dla Ciebie.

W końcu musi się gdzieś zacząć i {[0] } jest tylko symboliczną nazwą dla tej lokalizacji. Jest on określony przez standard języka tak, aby Programiści wiedzieli, jak go nazwać, w przeciwnym razie kod nie byłby przenośny z jednego łańcucha narzędzi do drugiego.

Jeśli piszesz kod dla systemu "bare-metal" bez systemu operacyjnego lub przynajmniej bez systemu operacyjnego w sensie loadera procesów (systemy wbudowane często zawierają jądro RTOS , które jest uruchamiane po main ()), to teoretycznie możesz nazwać punkt wejścia kodu C, jak chcesz, ponieważ nie masz dostępu do systemu. zwykle mają pełną kontrolę nad kodem startowym w czasie wykonywania. Ale czynienie tego byłoby głupie i nieco przewrotne.

Niektóre środowiska RTO, takie jak VxWorks, i większość frameworków aplikacji w ogóle zawierają main() )lub jego odpowiednik) w swoim własnym kodzie bibliotecznym tak, że działa przed kodem aplikacji użytkownika. Na przykład aplikacje VxWorks zaczynają się od usrAppInit (), a Aplikacje Win32 od WinMain ().

 1
Author: Clifford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-19 19:27:47

Zdaję sobie sprawę, że to stare pytanie, ale właśnie to odkryłem i musiałem się podzielić. Prawdopodobnie nie będzie działać ze wszystkimi linkerami, ale jest przynajmniej możliwe, aby oszukać ld (używam wersji 2.24.51.20140918), aby pomyśleć, że istnieje funkcja main - robiąc to:

int main[] {};

Lub nawet po prostu

int main;

Możesz następnie zastosować jedną z wyżej wymienionych sztuczek, aby pozwolić programowi wykonać jakiś kod, np. poprzez użycie konstruktora:

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

{[4] } jest zapobieganie array from being "called". Dobra zabawa: -)

 1
Author: JorenHeit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-29 12:49:18

Może uda się skompilowaćsekcja danych i wypełnić go kodem?

 0
Author: Bytemain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 14:15:40

To zależy co mają na myśli.

Czy mieli na myśli:

Napisz program bez funkcji main ().

To ogólnie rzecz biorąc nie.
Ale są sposoby na oszukiwanie.

  • możesz użyć pre-procesora, aby ukryć main() na widoku.
  • Większość kompilatorów pozwala określić punkt wejścia do kodu.
    Domyślnie jest to main (int,char*[])

Czy chodziło im o:

Napisać program uruchamiający kod bez użycia main (aby uruchomić kod).

Jest to stosunkowo prosta sztuczka. Wszystkie obiekty w globalnej przestrzeni nazw uruchamiają swoje konstruktory przed wejściem main() i zniszczeniem po wyjściu main (). Wszystko, co musisz zrobić, to zdefiniować klasę z konstruktorem, który uruchamia kod, który chcesz, a następnie utworzyć obiekt w globalnej przestrzeni nazw.

Uwaga: kompilator może zoptymalizować te obiekty pod kątem opóźnionego ładowania (ale zazwyczaj nie), ale dla bezpieczeństwa wystarczy umieścić Globalny w tym samym pliku, co główna funkcja (która może być pusta).

 0
Author: Martin York,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 16:19:33

Function main jest tylko domyślną etykietą dla adresu, na którym program rozpocznie uruchamianie. Więc technicznie tak, jest to możliwe, ale musisz ustawić nazwę funkcji, która rozpocznie wykonywanie w Twoim środowisku.

 0
Author: Dawid Królak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 22:26:43

Napisz klasę i wydrukuj swoją nazwę w konstruktorze tej klasy i zadeklaruj globalny obiekt tej klasy. Tak więc konstruktor klasy jest wykonywany przed main. Możesz więc zostawić główny pusty i wydrukować swoje imię i nazwisko.

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

MyClass gObj; // this will trigger the constructor.

int main()
{
   // nothing here...
}
 0
Author: John Gummadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-19 17:53:56

1) Użycie makra definiującego main

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

Wyjście:

Stackoverflow

2) Używanie Operatora Wklejania Tokenów Powyższe rozwiązanie zawiera słowo "main". Jeśli nie możemy nawet napisać main, używamy operatora wklejania tokenów (zobacz to po szczegóły)

#include<stdio.h>
#define fun m##a##i##n
int fun()
{
printf("stackoverflow");
return 0;
}
 0
Author: anshuman singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-09 15:26:52

Tak możliwe jest napisanie programu bez main ().

Ale używa main() pośrednio.

Następujący program pomoże Ci zrozumieć..

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

Operator '##' nazywany jest operatorem wklejania tokenów lub scalania tokenów. Oznacza to, że możemy połączyć z nim dwa lub więcej znaków.

W drugiej linii programu -

Define decode (s,t,u,m,p,e,d) m##s##u##t

Co tutaj robi preprocesor. Dekodowanie makra (s,t,u,m,p,e,d) jest Rozszerzony jako "msut" (operator ## łączy m,s,u & T w msut). Logika polega na tym, że gdy przekazujesz(s,t,u,m,p,e,d) jako argument,łączy on 4,1, 3 i 2 znaki (tokeny)

Teraz spójrz na trzecią linię programu -

Define begin decode (a,n,i,m,A,r, e)

Tutaj preprocesor zastępuje makro "begin" dekoderem rozszerzenia (A,N,I,m,a,r, e). Zgodnie z definicją makra w poprzednim wierszu argument musi zostać rozszerzony tak,aby 4., 1., 3. i 2. znaki muszą być połączone. W argumencie (A, N, i, m,a,r, e) 4., 1., 3. i 2. znaki to "m", "a", " i " I "n".

Więc zastąpi begin przez main () przez preprocesor zanim program zostanie przekazany kompilatorowi. To jest to ...

 0
Author: Saraswati Katakdhond,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-16 05:59:05

Zgodnie ze standardami, main() jest wymagane i punktem wyjścia środowisk hostowanych. Dlatego musisz użyć sztuczek, aby ukryć oczywisty wygląd głównego, jak sztuczka zamieszczona powyżej.

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

Tutaj main jest napisany przez sztuczki makro. To może nie być jasne od razu, ale ostatecznie prowadzi do głównej. Jeśli jest to prawidłowa odpowiedź na twoje pytanie, można to zrobić bardzo łatwo, jak to.

# include <stdio.h>
# define m main

int m()
{
    printf("Hell0");
}
 -4
Author: Bharat Kul Ratan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 21:59:07