hej
czy zmienna z bedzie tworzona za kazdym przejsciem petli od nowa czy kompilatory to optymalizuja i bedzie tworzona tylko raz?
while (true)
{
int z ;
}
vixen03 napisał(a)
[...] kazda iteracja od nowa tworzy ta zmienna
Tego nie byłbym taki pewny ;) Przykład (BCB/VC6):
for(int i=0;i<100;i++)
{
int x;
x+=1;
}
zmienna
x
przy każdej iteracji leży pod tym samym adresem, a jej wartość nie jest "kasowana" - czyli na koniec ostatniej iteracji zmienna x
będzie powiększona o 100. Pomijając kwestię zasięgu zmiennych można to potraktować jak:
```cpp
int x;
for(int i=0;i<100;i++)
{
x+=1;
}
0x666 napisał(a)
vixen03 napisał(a)
[...] kazda iteracja od nowa tworzy ta zmienna
Tego nie byłbym taki pewny ;) Przykład (BCB/VC6):
for(int i=0;i<100;i++)
{
int x;
x+=1;
}
zmienna
x
przy każdej iteracji leży pod tym samym adresem, a jej wartość nie jest "kasowana" - czyli na koniec ostatniej iteracji zmienna x
będzie powiększona o 100. Pomijając kwestię zasięgu zmiennych można to potraktować jak:
>
>
```cpp
int x;
for(int i=0;i<100;i++)
{
x+=1;
}
wydaje mi sie ze tu zaszla troche inna sytuacja... przy pierwszym przebiegu petli tworzona jest zmienna (zauzmy ze ma adres 'adres') inkrementujemy ja i mamy pod adresem 'adres' wartosc 1, zmienna jest kasowana ale pamiec nie jest czyszczona. Drugi przebieg petli, znowu jest tworzona zmienna x tyle ze pod tym samym adresem pod ktorym znajduje sie juz wartosc 1 itd. Wydaje mi sie ze zachodzi wlasnie taka sytuacja gdyz podczas wykonywania tej petli nie jest rezerwowana zadna inna pamiec...
aha i sprawdzilem w VC6 to, jest dokladnie tak jak mowisz tyle ze zmienna x przy tworzeniu nie zawiera wartosci 0 tylko przypadkowa liczbe znajdujaca sie akurat pod tym adresem w pamieci, tak wiec po stu przebiegach petli nie bedzie tam wartosci 100 tylko o 100 wieksza niz na poczatku
i jeszcze jedna rzecz, taki zapisz juz nie daje takiego samego wyniku
for(i=0;i<10;i++)
{
int * x = new int;
*x++;
}
tylko nie wiem czy to ma cos z tym wspolnego ale chyba odbywa sie to podobnie jak w pierwszym przypadku :P
przy pierwszym przebiegu petli tworzona jest zmienna (zauzmy ze ma adres 'adres') inkrementujemy ja i mamy pod adresem 'adres' wartosc 1, zmienna jest kasowana ale pamiec nie jest czyszczona. Drugi przebieg petli, znowu jest tworzona zmienna x tyle ze pod tym samym adresem pod ktorym znajduje sie juz wartosc 1 itd.
Mylisz się ;) Ta zmienna jest "tworzona" podczas wejścia w funkcję, w której się znajduje - przydzielana jest odpowiednia ilość miejsca na stosie. Od strony assemblera zmienna
x
, nawet przed wejściem w pętle, po prostu już jest. Mimo wszystko poprawny zapis powinien wyglądać tak:
for(int i=0,x=0;i<100;i++)
{
x+=1;
}
ha i sprawdzilem w VC6 to, jest dokladnie tak jak mowisz tyle ze zmienna x przy tworzeniu nie zawiera wartosci 0 tylko przypadkowa liczbe znajdujaca sie akurat pod tym adresem w pamieci, tak wiec po stu przebiegach petli nie bedzie tam wartosci 100 tylko o 100 wieksza niz na poczatku
W rzeczy samej i tak też napisałem ;)
i jeszcze jedna rzecz, taki zapisz juz nie daje takiego samego wyniku
for(i=0;i<10;i++)
{
int * x = new int;
*x++;
}
tylko nie wiem czy to ma cos z tym wspolnego ale chyba odbywa sie to podobnie jak w pierwszym przypadku
Podobnie tyle, że zmienna
x
jest inicjowana przy każdej iteracji i po wyjściu z pętli 40 bajtów będzie niepotrzebnie zalegać w pamięci :P.
Wszystko zalezy od kompilatora. W g++ na 100% zmienna ta jest tworzona za kazdym razem, Przeciez jezeli umiesciles jej deklaracje wewnatrz petli, to tego chciales. I jest to bardzo dobre podejscie, poniewaz mogles umiescic jakies obliczenia w konstruktorze.
vixen03 napisał(a)
ale zrob sobie takie cos:
class klasa {
public:
klasa() {cout << "tworzenia klasy";}
}
for(int i = 0; i < 10; i++)
klasa k;
A to spróbuj tego :D
class klasa {
private:
int x;
public:
klasa() {std::cout << "tworzenia klasy x="<<x<<std::endl;}
~klasa() {x++;std::cout << "usuwanie klasy"<<std::endl;}
};
int main(int argc, char* argv[])
{
for(int i = 0; i < 10; i++)
klasa k;
}
Zauważ, że mowa jest o typach wbudowanych (
int
).
greco napisał(a)
Wszystko zalezy od kompilatora. W g++ na 100% zmienna ta jest tworzona za kazdym razem.
Otóż to!!! Między innymi dlatego warto trzymać się zasad języka C/C++ i nie wariować tak jak ja to zrobiłem w pętli z pierwszego mojego postu. Tylko pytanie brzmi: co to znaczy, że zmienna jest tworzona i jaki wpływ ma to tworzenie na wydajność??? Chyba w pewien sposób odpowiedziałem na te pytania ;)
Jest tak jak napisał vixen03, ale to jest bardziej skomplikowane.
Miejsce na zmienną automatyczną przydziela kompilator na początku funkcji, natomiast przy deklaracji może(!) nastąpić wywołanie konstruktora, a przy końcu bloku - wywołanie destruktora.
Oczywiście int() jest pusty :)
Dodatkowo kompilator potrafi powtórnie użyć miejsca na inny obiekt danego typu, jeśli te dwa obiekty nie istnieją w tym samym czasie.
Natomiast jest jeszcze jedna rzecz: inicjalizacja. W bloku ZAWSZE, w każdej iteracji, jest inicjalizowany obiekt automatyczny [C++ 6.7.2], np.:
for (...)
{
int i = 5;
}
A static POD(?) tylko raz [6.7.4].
Sprawdziłem na kompilatorze Watcomu przy agresywnej optymalizacji:
int main(void)
{
while(1)
{
long int a;
a = 1;
}
long int b = 3;
}
najpierw powinna zostać utworzona zmienna a, a potem b a więc po zakończeniu while zmienna b powinna zajmować miejsce a. Ale jest inaczej:
adres a: -04[bp]
adres b: -08[bp]
Wynika z tego, że wszystkie zmienne w funkcji mają ten sam zakres a miejsce na stosie jest robione przy wejściu do funkcji i rzeczywiście tak jest:
mov bp,sp
sub sp, 08
Pętla while wygląda tak:
mov word ptr -08[bp],01
mov word ptr -06[bp],0
jmp 0318
Zmienna więc nie jest tworzona przy każdym wejściu do pętli. Prawdopodobnie jednak, każdy inny kompilator bedzie stosował odmienną taktykę optymalizacji więc lepiej przyjąć, że zmienna jest fizycznie tworzona i usuwana każdorazowo i nie stosować takich trików ;P.
no wlasnie lepiej nie stosowac takich trikow i przybrac rozwiazanie najlepiej do wybranej sytuacji... wlasciwie chyba i tak wiedza o tym do niczego sie nie przyda... bo zawsze mozna utworzyc wskaznik przed petla i go inkrementowac i wykorzystanie pamieci bedzie minimalne
Heimdall napisał(a)
Sprawdziłem na kompilatorze Watcomu przy agresywnej optymalizacji:
(...)
To ma być optymalizacja?! :D
To JEST optymalizacja [gcc 3.4.2 -Os -fomit-frame-pointer]:
_main:
call ___main
L2:
jmp L2
Nieskończona pętla... i nic więcej.
Natomiast jeśli chodzi o kompilację czegoś takiego:
#include <stdio.h>
#include <time.h>
int main(void)
{
int a,b;
while(a>1)
{
int b1 = a+66;
printf("%d",b1);
}
while(b>1)
{
int b2 = b+66;
printf("%d",b2);
}
}
To b1 i b2 mogą być w tym samym miejscu.
Albo:
#include <stdio.h>
#include <time.h>
int main(void)
{
int a;
while(a>1)
{
int b1 = a+66;
printf("%d",b1);
a++;
}
int b;
while(b>1)
{
int b1 = b+66;
printf("%d",b1);
b++;
}
// mogę tu chcieć b=a;
}
Mamy: a,b są w tym samym bloku, więc NIE MOGĄ zostać połączone.
I kolejny, a'la Heimdall:
#include <stdio.h>
#include <time.h>
int a;
int main(void)
{
while(a>1)
{
int b1 = a+66; // [ebp-4]
printf("%d",b1);
a++;
}
int b; // b dostaje miejsce b1 [ebp-4]
while(b>1)
{
int b1 = b+66; // to b1 jest juz w innym miejscu [ebp-8]
printf("%d",b1);
b++;
}
}
A jeszcze jak się dorzuci optymalizacje do tego to w ogóle może kompilator zrobić cuda :)
W C++ jest tak:
Implementacja kompilatora dla x86 używa stosu procesora i dlatego przy wejściu w funkcję mamy już miejsce dla wszystkich zmiennych lokalnych (auto + temporary!), bo oczywiście kompilator potrafi obliczyć zajętość miejsca.
Sam standard mówi:
"The lifetime of an object of type T begins when:
? storage with the proper alignment and size for type T is obtained, and
? if T is a class type with a non-trivial constructor (12.1), the constructor call has completed.
The lifetime of an object of type T ends when:
? if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
? the storage which the object occupies is reused or released."
I nawet bardziej się kompilukuje, bo można w pewnych warunkach uzyskać dostęp do obiektu po jego znieszczeniu, a przed ponownym użyciem, itp. [hmm, np. do zmiennej statycznej w klasie, cały punkt 3.8 Object Lifetime dla zainteresowanych]
Najlepiej nie patrzeć na to, niech kompilator sam się tym zajmuje.
[jeśli gdzieś się walnąłem to proszę mnie poprawić, ale jeśli nie wiesz o czym w ogóle to jest to wolę nie czytać twojego postu...]
Zarejestruj się i dołącz do największej społeczności programistów w Polsce.
Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.