2012-02-06 9 views
12

byłem orzeźwiający moje rozumienie wartości inicjalizacji kontra default-inicjalizacji, i natknąłem this:Dlaczego to proste niezdefiniowane zachowanie?

struct C { 
    int x; 
    int y; 
    C() { } 
}; 

int main() { 
    C c = C(); 
} 

Najwyraźniej jest UB ponieważ

W przypadku C(), istnieje konstruktor, który może inicjować elementy x i y, więc inicjalizacja nie ma miejsca. Próba skopiowania C() do c powoduje więc niezdefiniowane zachowanie.

Myślę, że rozumiem, dlaczego, ale nie jestem pewien. Czy ktoś może rozwinąć?

Czy to oznacza, że ​​jest to również UB?

int x; x = x; 

Nawiasem mówiąc, w odniesieniu do inicjalizacji wartości, czy zagwarantowane jest zero?

int x = int(); 
+0

Nie jesteś pewien co masz na myśli. Jeśli masz na myśli wartości x i y, które nie zostaną zainicjalizowane, to tak, ale odpowiedziałeś na własne pytanie (ponieważ konstruktor tego nie robi). Co do Cc = C(); Myślę, że to jest całkowicie poprawne. – Sid

+0

Myślałem, że to też było całkowicie ważne, dopóki ktoś nie twierdził inaczej. Kiedy go czytam, pierwszy fragment może tylko UB, jeśli drugi też jest, w przeciwnym razie jest to prosta niezainicjowana wartość, bez deamonów nosowych. – spraff

+2

@Sid: Nie, użycie wartości niezainicjowanego obiektu daje niezdefiniowane zachowanie. –

Odpowiedz

13

Pierwszym przykładem jest niezdefiniowane zachowanie ponieważ domyślna, kompilator wygenerowany konstruktor kopia zrobi kopię memberwise, int s może mieć wartości odłowu i odczytywania wartości wychwytywania skopiować może spowodować, że program awarię.

W praktyce nie mogę sobie wyobrazić, żeby kiedykolwiek faktycznie zawaliło się; kompilator prawie na pewno zoptymalizuje kopiowanie, a nawet gdyby nie, to prawdopodobnie użyłby specjalnej bitowej kopii, która skopiowałaby bez sprawdzania wartości pułapkowania. (W C++ masz gwarancję, że będziesz w stanie uzyskać bajtów kopiowania.)

Dla drugiego przypadku ponownie nieokreślone zachowanie. Chociaż w tym przypadku, masz przypisanie zamiast kopiowania konstrukcji, a kompilator jest mniej prawdopodobny, aby go zoptymalizować. (W pierwszym przykładzie nie ma zadnego zadania, tylko kopia.)

Po trzecie, tak. Inicjator o pustym nawiasie (i bez domyślnego inicjalizatora zdefiniowanego przez użytkownika, który ma go zastąpić) najpierw wykonuje zerowanie inicjalizacji (dokładnie tak, jak ma to miejsce w przypadku zmiennych ze statycznym czasem życia).

+0

+1, niektóre narzędzia do debugowania uruchamiają aplikację na maszynie wirtualnej, która pułapkę (w niektórych przypadkach) określa to jako potencjalny problem. Zgadzam się jednak, że w prawdziwym przypadku istnieje szansa, że ​​skopiujesz niezainicjowaną pamięć i przeniesiesz ją. –

+0

W C++ 14 odniesienia to [dcl.init]/12 "] Jeśli ocena jest nieokreśloną wartością, zachowanie jest niezdefiniowane, z wyjątkiem następujących przypadków:" - i żaden przypadek nie obejmuje tego kodu –

-1

Nr Co się stanie, to większość kompilatory zoptymalizuje ustawienia zmiennej na zewnątrz, ale te, które nie będą nie tylko zmienić wartość x. Jest taki sam, jak poniższy kod:

int x = 0; 
x = 0; 

Nie chodzi o to, że druga linia nie zostanie wykonana, po prostu nic nie da.

+0

Chodzi o to, że jeśli moje opublikowane fragmenty są UB, optymalizacja kompilatora może spowodować * dowolny * wynik. – spraff

+3

Nie zgadzam się, technicznie odczytywanie wartości nieokreślonej jest niezdefiniowanym zachowaniem. To, czy kompilator optymalizuje odczyt, jest * jeden * możliwego wyniku niezdefiniowanego zachowania. –

1

Nie sądzę, że tak naprawdę jest to undefined behavior, chociaż wartości w c mają unspecified values. Oznacza to, że zachowanie programu jest dobrze zdefiniowane, o ile nie używasz tych nieokreślonych wartości. Jeśli ich używasz, np. w stanie lub je wydrukować, wyniki nie są zdefiniowane. Jednak nie sądzę, że program może robić coś dziwnego.

W odniesieniu do korzystania z domyślnego konstruktora na wbudowanych typów, to jest gwarantowana w celu uzyskania wartości typu Zero, czyli 0 dla liczb całkowitych, 0.0 dla pływających rodzajów punktów itd to rozciąga się również do członków typów bez konstruktor. Gdy pojawi się jakiś konstruktor, musisz sam zająć się budowaniem swoich członków bez konstruktora.

+1

Ooh, Ty kontra James Kanze w prostej strzelaninie! Mówi, że konstruktor kopiowania "używa" niezainicjowanych wartości, a to czyni UB. Mówisz inaczej. –

+0

@SteveJessop, ale ta odpowiedź wydaje się bardziej poprawna, prawda? :), +1 –

+0

@ Mr.Anubis: wydaje się, że na najwyższych poziomach nie ma zgody co możesz zrobić w C++ z niezainicjowaną wartością. W standardzie znajduje się tekst, który mówi, że nie można wykonać konwersji wartości l-wartości, ale można by pomyśleć, że (podobnie jak inne reprezentacje obiektów) można uzyskać do niego dostęp jako "unsigned char". W praktyce jest mało prawdopodobne, że wygenerowany ctor robi coś poza ślepym kopiowaniem danych, ale żeby grać bezpiecznie, uważam, że James ma rację co do standardu. np. kopiowanie może przypadkowo sprawdzić bity parzystości, jeśli "int" ma jakieś, z UB, jeśli są błędne. –