Dlaczego inicjalizacja wartości „int * ptr = int ()” nie jest niedozwolona?

86

Poniższy kod (pobrany stąd ):

int* ptr = int();

kompiluje w języku Visual C ++ i inicjalizuje wskaźnik.

Jak to możliwe? Mam na myśli, że int()daje obiekt typu inti nie mogę przypisać wskaźnika intdo wskaźnika.

Dlaczego powyższy kod nie jest nielegalny?

ostry
źródło
Nie jest to odpowiedź, ale świetne pytanie! Nigdy czegoś takiego nie widziałem.
Josh
6
Ponieważ prymitywy mają „konstruktor” w C ++, int()zwraca skonstruowaną wartość int(która jest według mnie określoną rzeczą w C ++ 03), a wartość domyślna intto 0. Jest to odpowiednikint *ptr = 0;
wkl
7
@EmanuelEy: Nie, każda stała całkowita o wartości zero może być używana jako stała wskaźnika zerowego, niezależnie od tego, jak wskaźniki są faktycznie zaimplementowane.
Mike Seymour,
1
@MooingDuck: Nie powiedziałem, że NULLmoże to być wartość niezerowa. Powiedziałem, że może to być dowolna stała całkowita o wartości zero (co obejmuje int()).
Mike Seymour,
5
@DanielPryden To jest użycie słowa „obiekt”, o którym wcześniej nie wiedziałem.
puszysty

Odpowiedzi:

110

int()jest wyrażeniem stałym o wartości 0, więc jest to poprawny sposób tworzenia stałej wskaźnika zerowego. Ostatecznie to tylko trochę inny sposób powiedzeniaint *ptr = NULL;

Jerry Coffin
źródło
3
+1, stały bit wyrażenia jest ważny i brakuje go w 2 najczęściej głosowanych odpowiedziach.
David Rodríguez - dribeas
czy to zniknie z C ++ 0x?
Neil G
@NeilG: Pozostaje to samo w C ++ 11, chociaż jest teraz również znak nullptr, którego możesz użyć zamiast 0lub NULLw nowym kodzie.
Jerry Coffin
2
@Nils: Kod jasności i deklarowanie swoich zamiarów za pomocą kodu. Oczywiście w C ++ 11 chcesz teraz użyć nullptr, ponieważ dodaje on również korzyści z dodatkowych kontroli czasu kompilacji do miksu.
Jamin Grey,
3
@Nils, ponieważ całkiem oczywiście 0może oznaczać stałą zerowego wskaźnika lub liczbę 0, podczas gdy nullptrjest oczywiste , że jest to stała pustego wskaźnika. Ponadto, jak powiedział Jamin, ma również „dodatkowe kontrole w czasie kompilacji”. Spróbuj pomyśleć, zanim zaczniesz pisać.
Przebieg Miles
35

Ponieważ int()plony 0, które są wymienne z NULL. NULLjest definiowany jako 0, w przeciwieństwie do C, NULLktóre jest (void *) 0.

Zauważ, że byłby to błąd:

int* ptr = int(5);

i to nadal będzie działać:

int* ptr = int(0);

0jest specjalną wartością stałą i jako taka może być traktowana jako wartość wskaźnika. Wyrażenia stałe, które dają 0, takie jak 1 - 1są również dozwolone jako stałe o wartości null.

Blagovest Buyukliev
źródło
1
Zwróć też uwagę, że wartość NULL w C. niekoniecznie (void *)0też. Jest to po prostu zdefiniowane w ramach implementacji „wyrażenie stałej liczby całkowitej z wartością 0 lub takie wyrażenie rzutowane na typ void *”.
Jerry Coffin
@JerryCoffin Nigdy nie korzystałem z kompilatora C, który zdefiniowałby NULLjako (void*)0; tak było zawsze 0(a może 0L). (Ale potem, zanim C90 stało się (void*)0legalne w C,
używałem
1
@JamesKanze: W ubuntu 11.04 i naszej własnej wersji Linuksa, libio.h zawiera: #if !defined(__cplusplus) \n #define NULL ((void*)0) \n #else \n #define NULL (0)aktualna wersja gcc w Ubuntu to 4.5, w naszym systemie to 4.0.
David Rodríguez - drybling
5
0jest specjalnym literałem” - tylko dlatego, że jest wyrażeniem stałym i ma specjalną wartość 0. (1-1)jest równie wyjątkowe, jest również stałą zerową wskaźnika i tak jest int(). Fakt 0bycia dosłownym jest warunkiem wystarczającym, ale niekoniecznym, aby być wyrażeniem stałym. Coś takiego strlen(""), chociaż ma również specjalną wartość 0, nie jest wyrażeniem stałym, a zatem nie jest stałą zerowego wskaźnika.
Steve Jessop
@SteveJessop: Zgadzam się z poprawką, tak naprawdę chodzi o stałą wartość 0, a nie o 0dosłowność.
Blagovest Buyukliev
18

Wyrażenie jest int()obliczane na stałą, zainicjowaną domyślnie liczbę całkowitą, która jest wartością 0. Ta wartość jest specjalna: jest używana do inicjowania wskaźnika do stanu NULL.

Mark Okup
źródło
2
Brakuje tu bardzo ważnego szczegółu obecnego w odpowiedzi Jerry'ego: nie wystarczy, że wyrażenie daje wartość 0, ale musi też być wyrażeniem stałym . W przypadku kontrprzykładu with int f() { return 0; }wyrażenie f()zwraca wartość 0, ale nie można go użyć do zainicjowania wskaźnika.
David Rodríguez - dribeas
@ DavidRodríguez-dribeas, w moim pośpiechu, by przedstawić odpowiedź w możliwie najprostszy sposób, pominąłem tę część. Mam nadzieję, że teraz jest to do przyjęcia.
Mark Okup
13

Od n3290 (C ++ 03 używa podobnego tekstu), 4.10 Konwersje wskaźnika [conv.ptr] akapit 1 (nacisk jest mój):

1 Stała wskaźnika o wartości null jest całkowitym wyrażeniem stałym (5.19) prvalue typu integer, którego wynikiem jest zero lub prvalue typu std :: nullptr_t. Stała wskaźnika o wartości null może zostać przekonwertowana na typ wskaźnika; wynik jest zerową wartością wskaźnika tego typu i można go odróżnić od każdej innej wartości wskaźnika obiektu lub typu wskaźnika funkcji. Taka konwersja jest nazywana konwersją wskaźnika zerowego. […]

int()jest takim całkowitym wyrażeniem stałym prvalue typu całkowitego, którego wynikiem jest zero (to jest kęs!), a zatem może być użyte do zainicjowania typu wskaźnika. Jak widać, 0nie jest to jedyne wyrażenie całkowite, które ma specjalną wielkość liter.

Luc Danton
źródło
4

Cóż, int nie jest przedmiotem.

Wierzę, że to, co się tutaj dzieje, to mówienie int *, aby wskazywał na jakiś adres pamięci określony przez int ()

więc jeśli int () utworzy 0, int * wskaże adres pamięci 0

Megatron
źródło
1
int()z całą pewnością jest przedmiotem.
Wyścigi lekkości na orbicie
@Tomalak: Myślę, że tak nie jest. Jest to tymczasowy typ nieklasowy i myślę, że mam rację, mówiąc, że nieto obiekty, jeśli chodzi o standard C ++. To trochę dziwne, chociaż sekcja o „obiektach tymczasowych” zaczyna się ostrożnie i mówi tylko o tymczasowych typach klas, ale później mówi o wiążących referencjach i oczywiście możesz powiązać referencje int(). Zdefiniuj int i;, więc bez wątpienia, ijest przedmiotem.
Steve Jessop,
@Steve: Spodziewałbym się debaty na ten temat tylko w tym, że „obiekty” są obszarem przechowywania w C ++, a tymczasowe tak naprawdę nie mają pamięci, prawda? 1.8 / 1 nie wymienia wyraźnie tymczasowych, ale wydaje się, że istnieje intencja ich uwzględnienia.
Wyścigi lekkości na orbicie
1
@Tomalak: tak, rzeczywiście, tymczasowe typy nieklasowe nie wymagają przechowywania, chyba że weźmiesz referencję. Nieważne, to nie ma większego znaczenia. Stwierdzenie „well int nie jest obiektem” jest prawdziwe tylko dlatego, że intjest typem, a nie obiektem. To, czy int()daje obiekt, czy tylko wartość r, nie wpływa na nic, co ktoś powiedział gdzie indziej.
Steve Jessop
@Steve: To nie podlega dyskusji :)
Lightness Races in Orbit