Ponowne uruchamianie programu

0

Witajcie.
Potrzebuję pomocy.

Taki program:

wprowadzam coś
on mi odpowiada
wprowadzam komendę
program się restartuje

Wszystko w konsoli

jak zrobić ten restart?

0

A po co sie ma restartować? Nie wystarczy go zapętlić? o_O

0

Nie wiedziałem że tak się da. . . A mógł byś powiedzieć jak?

0
int main()
{
//program
main();
}
0

Niestety nie działa. Mógł by ktoś coś dokładniej napisać? Albo zaproponowac jak zrobić restart programu?

0

@Mita przepraszam bardzo ale jak ty chcesz napisać JAKIKOLWIEK program bez znajmości tak podstawowej sprawy jak pętle? To co ty umiesz? Stworzyć zmienną i napisać warunek? o_O

#include <iostream>
using namespace std;

int main()
{
  while(1)
  {
    //kod programu który będzie się pętlił
  }
  return 0;
}

A ktoś kto tak sobie woła rekurencyjnego maina to ma chyba za duży stos...

0
aasdasasdasdad napisał(a)
int main()
{
//program
main();
}

Genialne XD

0
int main()
{
 {
  // program
  }
  main();
}

:P juz tak by bylo lepiej :) ale podejrzewam ze mimo dzikiego sposobu zapisu nie dalby rady wprowadzic recznie powyzej chocby 1k wyrazen ;p

0
Gelldur napisał(a)

Genialne XD

Nie genialne tylko zwyczajna rekurencja ogonowa, podrośniesz to zaczniesz z tego korzystać regularnie. W praktyce równoważne pętli.

@krwq, tak by było lepiej? Za przeproszeniem, pomerdało? Co te klamerki do kodu wnoszą? Każdy kolejny zagnieżdżony scope jest sumą nadrzędnych - poza pedalskim wyglądem nie uzyskujesz nic. Źle podejrzewasz, rekurencja ogonowa jest równoważna zwyczajnej pętli, nie zjada miejsca na stosie, kompilatory rozwijają ją do iteracji.

Z tym kodem jest tylko jeden problem - standard C/C++ zlekka negatywnie wypowiada się o ręcznym wołaniu main, generalnie robić tego nie wolno poza przypadkami własnego prologu biblioteki standardowej. Po ludzku - połowa kompilatorów wyrzuci błąd, w wypadku każdej innej funkcji byłoby to jak najbardziej poprawne.

0

deus, ja jeszcze osobiscie nigdy maina nie wywolalem, klamry daja to ze wszystkie zmienne lokalne w srodku beda wyrzucone ze stosu przed wywolaniem maina i na stos pojda tylko niejawne argumenty maina?

0

Nie zostaną wyrzucone wcale, zmienne ze stosu usuwa się fizycznie dopiero na wyjściu z funkcji, nie z bloku. Poza tym jakie niejawne argumenty? Dlaczego w ogóle cokolwiek ma iść na stos, dlaczego ma być jakieś wywołanie funkcji robione skoro to rekurencja ogonowa?

0

nie wiem w jaki sposob kompilator kompiluje i nie wiem czy pozbywa sie rekurencji, wiem ze lepiej unikac rekurencji jak tylko sie da

0

Tja, typowe pseudonaukowe podejście z naszych uczelni 'rekurencja to zuo, unikać za wszelką cenę'. Należy unikać wszystkiego, czego się nie rozumie.

0

nie zawsze zlo ale z reguly, rekurencja z reguly wolniejsza jest od tego samego zapisanego iteracyjnie

0
deus. napisał(a)
Gelldur napisał(a)

Genialne XD
@krwq, tak by było lepiej? Za przeproszeniem, pomerdało? Co te klamerki do kodu wnoszą? Każdy kolejny zagnieżdżony scope jest sumą nadrzędnych - poza pedalskim wyglądem nie uzyskujesz nic. Źle podejrzewasz, rekurencja ogonowa jest równoważna zwyczajnej pętli, nie zjada miejsca na stosie, kompilatory rozwijają ją do iteracji.

jestes absolutnie pewien, ze z klamrami nie jest lepiej?

Te nadmiarowe klamry formalnie wyznaczaja nowy scope, a co za tym idzie, klamra zamykajaca tenze innerscope przed rekurencyjnym main'em powinna wymusic wywolanie destruktorow obiektow przed kolejnym odpaleniem main'a.
Bez tej klamry kompilator powinien zalozyc, ze nie moze z'destruktor'owac tych obiektow az sie caly upper-main zakonczy.

Bez tego innerscope'a, co prawda oczywiscie, inner-main nie pobiera tutaj zadnego argumentu ze scope'a zewnetrznego main, ale to NIE znaczy, ze kompilator ma prawo zniszczyc obiekty przed nim - musialby dysponowac moca udowodnienia, ze wczesniejsza destrukcja obiektow jest rownowazna pozniejszej, co jest poza banalnymi przypadkami niemozliwe. Idac tym torem, glupi auto_ptr w glownym scopie main'a juz powinien blokowac opt. tailcall'a?

IMHO, pedalskie-nadmiarowe-klamry zwalniaja kompilator z odpowiedzialnosci, bo programista WIE ze w momencie } obiekty idą won, i tym samym ulatwiaja analize i zwiekszaja szanse na optymalizacje tailcall'a

Oczywiscie w momencie gdy klasy zmiennych nie maja destruktorow, jedyny zysk z ekstra klamer to usuniecie nazw zmiennych ze scope'a, czyli w tym przypadku bezsens.

0

Rekurencja ogonowa to nic innego niż skok na początek funkcji więc destruktory przed tym i tak polecą, autorzy kompilatorów aż tak głupi nie są - w praktyce rekurencja ogonowa sama tworzy dodatkowy niby-scope, to jedyne logiczne wyjście, które zresztą jest stosowane w co lepszych kompilatorach. Niestety standard nie narzuca optymalizacji. Jest jedno ale - żeby to było pewne to konieczny jest return, którego we wspomnianym kodzie nie ma - dopiero return bezpośrednio oznacza tutaj tailcalla.

0

ano wlasnie! mi chodzi o to, ze destruktory NIE moga sie odpalic PRZED rekursja, chyba ze jakims cudem kompilator da rade udowodnic ze nie zmienia to znaczenia kodu..
zerknij na i wybacz pseudokod:

class singleton_manager
{
    static self* getInstance(){...}

    void register(item* ptr){...} // metody robia dokladnie to co mozna sie domyslac
    void unregister(item* ptr){...} // ot, wstawiaja/usuwaja z listy
    vector<item*>& getall(item* ptr){...} // zwraca liste
    ...
};

int main()
{
    mgr = sing_manager.getInstance();
    
    //// { - potencjalna klamra
    auto_ptr<..> whatever1 = new HeavyBlah(mgr);  //ctor HeavyBlah wywoluje mgr.register(this)
    auto_ptr<..> whatever2 = new HeavyBlah(mgr);  //dtor ~HeavyBlah pozniej wywoluje mgr.unregister(this)
    auto_ptr<..> whatever3 = new HeavyBlah(mgr);

    if(whatever)
    {
        asd = mgr.getall();
        // zrob cos waznego uzywajacego HeavyBlah z mgr. jest ich 3, 3, 3, 3, ... czy 3, 6, 9, 12, .. ?
    }

    //// } - potencjalna klamra

    return main(); // :)
}

w przypadku bez-klamrowym, IMHO, kompilator nie moze zdecydowac sie wywolac destruktorow auto_ptr -> destruktorow HeavyBlah PRZED wywolaniem sub-main, poniewaz obszar zycia obiektow jeszcze sie nie zakonczyl, one powinny zginac dopiero po przeewaluowaniu sie "return main()" a i dzieki RVA/etc moga nawet teoretycznie przezyc return mimo lokalnosci.. za kazda 'iteracja' main bedzie tworzyl 3 obeikty az ich sie w pitu uzbiera

w momencie dodania klamer, 3xBlah beda za kazdym razem regenerowane

IMHO, w przypadku bezklamrowym, kompilator nie moze tez sam sobie wtryniac tych klamer "bo tak mu sie podoba", poniewaz zmienia to znaczenie kodu i zezwolenie kompilatorowi na cos takiego oznaczaloby, ze nigdy nie mozemy bezpiecznie napisac return asdfghj(); bo a nuz nam obiekty lokalne wyparuja przed wywolaniem asdfghj!

szcerze mowiac, mecze ten temat z dwoch powodow: sam czesto uzywam nadmiarowych klamer dla odseparowania zmiennych i wczesniejszego niszczenia, a dwa, ze doprawdy ciekaw jestem czy sie myle w tym co napisalem teraz i czy faktycznie kompilator moze tak mi strzelic w stope - nie spotkalem tego jeszcze.

0
quetzalcoatl napisał(a)

szcerze mowiac, mecze ten temat z dwoch powodow: sam czesto uzywam nadmiarowych klamer dla odseparowania zmiennych i wczesniejszego niszczenia, a dwa, ze doprawdy ciekaw jestem czy sie myle w tym co napisalem teraz i czy faktycznie kompilator moze tak mi strzelic w stope - nie spotkalem tego jeszcze.

Tak, do wymuszania zwalniania zmiennych to owszem, klamry się przydają. W C problemu nie ma, w C++ zależy co sobie kompilator wymyśli, aż mam ochotę kilka przetestować, ze szczególnym uwzględnieniem LLVM. W sumie fakt, w C++ różne rzeczy się dziać potrafią, przyznaję rację, lepiej klamry dawać dla bezpieczeństwa.

Generalnie w wypadku rekurencji ogonowej ciało funkcji jest przekształcane w blok iteracji, logicznie rzecz biorąc dobry kompilator powinien to ładnie wyciągnąć i zwalniać obiekty lokalne co iterację. O samej rekurencji standard nie mówi praktycznie nic, co do mechaniki niszczenia obiektów w podobych sytuacjach tak samo...

Zresztą wtedy miałem na myśli konkretnie C, na taki język całość 'problemu' mi wyglądała.

0

Sprawdzilem jak to jest z ta rekursja ogonowa i musze powiedziec, ze niestety gcc nie zwalnia pamieci zajmowanej przez zmienne lokalne przed ponownym wywolaniem main, tylko caly czas przydziela im nowa pamiec :D
Na ponizszym "zdjeciu" widac kod program w C i kod po dissasemblacji w gdb.
http://img682.imageshack.us/img682/5774/gdb1.png
Dobra, po krotce co tu mamy:

  • pierwsze trzy linijki to wyrownaniu i kanarek ze wzgledu na to iz program byl kompilowany bez flag, czyli mamy SSP, a ja jeszcze mam LSM, ale mniejsza o to bo zadnej roli w tym przykladzie nie odgrywa
  • potem standard, ustawienie ramki
  • ECX na stos, ecx zawiera adres o 4 bajty dalej niz EIP funkcji main
  • rezerwacja pamieci na zmienne lokalne, 4 bajty na zmienna bolek, 8 na control value, 8 na argc, argv
  • PONOWNE WYWOLANIE main
  • dalej niestety program nie dochodzi, a tym czasem wlasnie tam mamy zwolnienie pamieci
  • dalej wiadomo, zdjecie kanarka, sprawdzenie, zdjecie ramki
  • ret na koniec

Teraz dowod:
http://img694.imageshack.us/img694/9131/zrzutekranu1ep.png

Kodu z klamerkami nie sprawdzalem ;)

0

Tak, też sprawdzałem GCC 4.4, efekty są nieciekawe. Przyjmijmy roboczo, że zgodnie z nauczaniem 'rekurencja to zuo!', przynajmniej w C++. Generalnie póki nie ma niczego, co posiada zdefiniowany destruktor to problemu nie ma.

W sumie ciekawa sprawa, durne GCC momentami średnio sobie radzi nawet z wersją z klamerkami, nie zawsze przekształca w iterację... CL wypada lepiej, ale nadal jest za głupi żeby wersję bez klamerek sensownie poskładać. Ciekawe czy doczekam czasów sensownych kompilatorów C++...

0
tomek@noname:~$ gcc be.c -o be -Os -g
tomek@noname:~$ gdb ./be
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) disas main
Dump of assembler code for function main:
0x08048394 <main+0>:	lea    0x4(%esp),%ecx
0x08048398 <main+4>:	and    $0xfffffff0,%esp
0x0804839b <main+7>:	pushl  -0x4(%ecx)
0x0804839e <main+10>:	push   %ebp
0x0804839f <main+11>:	mov    %esp,%ebp
0x080483a1 <main+13>:	push   %ecx
0x080483a2 <main+14>:	jmp    0x80483a2 <main+14>
End of assembler dump.
(gdb) list
1	
2	int main()
3	{
4	
5		int bolek;
6	
7		main();
8	
9	}
(gdb) 

GCC trzeba powiedziec, ze sie chce ladny kod miec ;)

0

Jeszcze taki mały OT dotyczący wyczynów kompilatorów - pracuję sobie nad RE jednej zabawki ewidentnie kompilowanej z opcją faworyzowania wydajności Visualem 2008. Lekko mnie zaskoczyła wielkość kodu implementacji RC4 (bardzo prosty algorytm) - długie jak cholera, rozwinięte do dwóch pętli, z czego pierwsza bierze za jednym zamachem 8 bajtów, część (pierwsze 5) jest nawet wczytywana jako dwordy (wczytanie słowa maszynowego może być szybsze niż pojedynczego bajtu), wszystkie operacje idą na dwordach poza tymi ewidentnie wymagającymi bajtów, znowu z tego samego powodu (rzeczom typu xor nadmiar danych nie przeszkadza)... Mówiąc krótko kompilator zrobił taką sieczkę, że aż mi się wierzyć nie chce.

Znowu mam chęć na kolejny eksperyment - zmieniać opcje kompilacji zwyczajnej implementacji póki czegoś podobnego nie otrzymam.

0

@t0m_k, no dobra, ale to nie ma związku. Pisałem, że z kodem w C/C++ bez destruktorów problemu nie ma - uprości. Poza tym kompilator powinien się przyczepić, że ręcznie wołasz main, do tego rekurencyjnie - to jest jedyna funkcja, która funkcjonuje na specjalnych prawach - takie cyrki w jej wypadku są niedozwolone.

0

Zasady sa proste, jesli da sie cos zrobic bez rekurencji, i nie bedzie to powodowac wyraznego spadku wydajnosci, to czemu tak nie zrobic ? To nie sa uczelniane wymysle, tylko "coding standard". I wtedy lepiej jest uzyc "while" chodzby dla przejrzystosci kodu, niz wolac "maina" i ludzic sie ze panowie i panie z quality przyznaja nam racje...

0

Bez przesady, rekurencja upraszcza kod w części wypadków. C++ jest jakie jest, dlatego lepiej faktycznie z rekurencją uważać. Rekurencja jest po to żeby jej używać, szczególnie ogonowa. Z powoływaniem się na 'coding standard' to ja bym był bardzo ostrożny.

0

@deus: To ma zwiazek z tematem, poniewaz nie ktorzy pisza, ze rekurencja jest zla, a wcale tak nie jest, tylko trzeba odpowiednio przypilnowac kodu, a jak to mozna w gcc zrobic pokazalem uzywajac flagi optymalizacji -Os.
Poza tym Ty znow pisales wczesniej, ze kompilator zamienia na iteracje, wiec postanowilem to sprawdzic i wrzucilem przyklad, pokazujacy, ze gcc sam z siebie nic nie robi w tym kierunku.
Moze sie komus to przyda jak bedzie chcial uzyc rekursji, a bedzie mu zalezalo na malym uzyciu pamieci.

0
t0m_k napisał(a)

Poza tym Ty znow pisales wczesniej, ze kompilator zamienia na iteracje, wiec postanowilem to sprawdzic i wrzucilem przyklad, pokazujacy, ze gcc sam z siebie nic nie robi w tym kierunku.

Brawo, pisałem też, że jest to optymalizacja. Dziwisz się, że bez włączenia optymalizacji kompilator ich sam z siebie nie robi? Poza tym co do takiego przekształcenia nikt nie miał wątpliwości, problemy zaczynają się dopiero kiedy chodzi o czas życia obiektów lokalnych obdarzonych destruktorem.

0

Skoro pisales to ok, umknelo mi gdzies, a co do obiektow to sie nie wypowiem, bo lata minely od mojego ostatniego kontaktu z OO ;)

0

C++ jest tak zakręcone, że przy każdej bardziej skomplikowanej konstrukcji nie wiadomo czego się spodziewać. Lepiej przesiąść się na Javę - tam przynajmniej wszystko jest ustandaryzowane ;]

Zamiana rekurencji ogonowej na while jest bardzo prosta. Wystarczy zamiast:

funkcja(arg1, ...., argn) {
    // ciało funkcji
    if (warunek) {
        funkcja(f1(arg1), ....., fn(argn));
    }
}

napisać:

funkcja(arg1, ...., argn) {
    do {
        // ciało funkcji
        arg1 = f1(arg1);
        ....
        argn = fn(argn);
    } while (warunek);
}

Rekurencji używa się często przy stategii "dziel i zwyciężaj" (np quicksort). W takim przypadku dzielimy input na kilka części i dla kilku z nich zapuszczamy rekurencję. Kompilator zamieni zawsze ostatnie wywołanie na iterację (oczywiście mówię o ostatnim wystąpieniu funkcji w kodzie, które to nie musi zostać wykonane - np jest w ifie). Tymczasem najbardziej opłaca się zostawić do iteracji największą część, bo potencjalnie to ona będzie prowadzić do największej głębokości rekursji. Np w quicksorcie dzielimy tablicę na dwie części. Jeżeli nie zastosujemy zwykłą rekurencję ogonową otrzymamy pesymistyczną złożoność pamięciową na rekurencję rzędu O(n), a jeżeli wywołujemy rekurencję dla mniejszej częsci, a większą kontynuujemy na obecnym poziomie rekursji to złożoność pamięciowa na stos rekursji wynosi już tylko O(lg n).

Nie zawsze jest pewność że kompilator rozwinie rekurencję do iteracji. Wg mnie rozwijanie rekurencji ogonowej sprawdza się tylko w przypadku bardzo krótkich kodów np algorytm Euklidesa:

gcd(a, b) {
  if (b == 0)
    return a;
  else
   return gcd(b, a mod b);
0

Lepiej przesiąść się na Javę - tam przynajmniej wszystko jest ustandaryzowane

Akurat raczej radziłbym Scalę, znacznie nowocześniejszy język o większych możliwościach. Do tego kładzie nacisk na poprawne wsparcie dla programowania funkcyjnego więc z rekurencją problemów nie ma. Zresztą Scala jest genialna pod prawie każdym względem, zaawansowane OOP i traity powinny być normą, niestety jest nią koślawe OOP z C++ i mniej koślawe z Javy i C#.

Kompilator zamieni zawsze ostatnie wywołanie na iterację (oczywiście mówię o ostatnim wystąpieniu funkcji w kodzie, które to nie musi zostać wykonane - np jest w ifie).

W C++ to nie takie pewne - cyrki z destruktorami.

Jako podsumowanie, chcesz sobie poużywać rekurencji bez narażania się na choroby psychiczne - zainwestuj w Haskella.

1 użytkowników online, w tym zalogowanych: 0, gości: 1