2010-03-23 9 views
13

Jeśli spróbujesz zmienić wskaźnik na typ ulotny, nawet zmienny wskaźnik znaku, w którym normalnie spodziewałbyś się wydrukować ciąg, zamiast tego po prostu otrzymasz "1" (zakładając, że wskaźnik nie jest pusty). Zakładam, że operator strumienia wyjściowego < < jest szablonem specjalizującym się w zmiennych wskaźnikach, ale moje pytanie brzmi: dlaczego? Jaki przypadek użycia motywuje to zachowanie?Dlaczego std :: cout konwertuje zmienne wskaźniki na bool?

Przykładowy kod:

#include <iostream> 
#include <cstring> 

int main() 
{ 
    char x[500]; 
    std::strcpy(x, "Hello world"); 

    int y; 
    int *z = &y; 

    std::cout << x << std::endl; 
    std::cout << (char volatile*)x << std::endl; 

    std::cout << z << std::endl; 
    std::cout << (int volatile*)z << std::endl; 

    return 0; 
} 

wyjściowa:

Hello world 
1 
0x8046b6c 
1 

Odpowiedz

24

ostream::operator<< ma następujące przeciążeń, między innymi:

ostream& operator<< (bool val); 
ostream& operator<< (const void* val); 

Po przejechaniu w lotnym wskaźnik, drugi przeciążenie nie można zastosować, ponieważ zmienne wskaźniki nie mogą zostać przekonwertowane na nielotne bez wyraźna obsada. Jednak każdy wskaźnik można przekonwertować na wartość bool, więc wybrane zostaje pierwsze przeciążenie, a wynik, który widzisz, wynosi 1 lub 0.

Tak więc prawdziwym powodem tego nie jest celowa decyzja w imieniu komisji normalizacyjnej, ale po prostu, że standard nie określa przeciążenia, które przyjmuje zmienny wskaźnik.

+0

+1 dokładniej wskaźnik do ulotnej pamięci, lotny wskaźnik byłby 'char * volatile' zamiast' char volatile * ' –

+0

Oczywiście możesz dodać przeciążenie funkcji' operator << (ostream & os, volatile void * p) {return os << const_cast (p);} 'i bądź z nim przez cały czas .. – Potatoswatter

+0

@Potatoswatter Odrzucanie zmienności wskazanego obiektu, aby uzyskać dostęp do obiektu, jakby był nieulotny [jest nieokreślonym zachowaniem] (https://stackoverflow.com/a/24555400/1038860), niestety. (Chyba, że ​​wskazany obiekt [nie był pierwotnie] (https://stackoverflow.com/a/7368038/1038860) lotny.) –

5
Myślę, że powodem jest to, że zmienne wskaźniki nie mogą być konwertowane pośrednio, aby anulować *. Jest to w dodatku C do standardu, a uzasadnieniem jest bezpieczeństwo typu.

Zmiana: Tylko wskaźniki do const i nielotnych obiektów mogą być niejawnie konwertowane na void * Uzasadnienie: Ten typ poprawia bezpieczeństwo.

Zamiast konwersji do pustki * (która zostanie wydrukowana w języku hex), otrzymasz "domyślną" konwersję na wartość bool.

+0

+1, pierwsze zdanie powinno brzmieć "wskaźniki na lotność" zamiast "lotny wskaźnik" :) –

+0

Ten dodatek do dodatku zawsze mnie zastanawiał. Ponieważ w C nie można przekształcić 'T const *' na 'void *'. Kiedy ostatnio to sprawdziłem, potrzebna jest też obsada. –

1

Myślę, że problem nie jest wyraźnym przeciążeniem dla wskaźników dla typów lotnych, ale BRAK przeciążenia dla wskaźników na typy zmienne. Kompilator nie może niejawnie usunąć niestabilnego kwalifikatora ze wskaźników, więc sprawdza dostępne przeciążenia, wybiera wersję operatora bool < < i konwertuje wskaźnik na zmienny na bool.

+0

Powtórzę, to powinno brzmieć "wskaźniki dla niestabilnych typów obiektów". Różnica jest kluczowa: 'void f (int * p); int main() {int x = 5; int * volatile p = & x; f (p); } 'lotność wskaźnika nie wpływa w rzeczywistości na wywołanie: będzie on leniwy, będzie odczytywany i kopiowany do argumentu funkcji i będzie w ten sposób przekazywany do' f' (w całej uczciwości będę głosować, ponieważ wszystkie pozostałe odpowiedzi mają ten sam problem - nawet jeśli dodają coś innego ...) –

2

Nie odpowiedzią

To jest właśnie problem z brzmieniem pytania i odpowiedzi. Problem powstaje z powodu niemożności konwersji wskaźników na obiekty zmienne na wskaźniki pustych przestrzeni, a nie na zmienne wskaźniki .

Różnica, która jest raczej ważna, to element pamięci, który jest niestabilny. W odpowiedzi na pytanie, wskaźnik nie jest lotny (może to być buforowane, a to nie musi być przepłukany w pamięci, gdy jest on zmieniony), ale raczej szpiczaste pamięci:

int volatile * p = f(); 
p++;  // this does not affect the perceived state of the c++ memory model 
++p; 
*p = 5; // this changes the perceived state 

Powodem jest to ważne jest to, że z lotną wskazówką do pamięci, sam wskaźnik jest tym, który ma specjalne traktowanie.

void foo(int *); 

int * volatile p = f(); // 1 
foo(p);     // 2 
int volatile * q = f(); 
//foo(q); // error, cannot convert pointer to volatile to pointer to non-volatile 
*q = 5;     // 3 
q = 0;     // 4 

W powyższym kodzie operacje oznaczone jako 1 i 2 powodują przejście do pamięci. Przypisanie w [1] musi być porzucone do pamięci. Nawet jeśli wartość p jest w rejestrze, zostanie załadowana z pamięci na [2]. Operacja oznaczona [3] modyfikuje wartość wskazaną przez q, która jest volatile i spowoduje przejście do pamięci głównej, podczas gdy operacja [4] wpływa tylko na wskaźnik, który sam nie jest obiektem, i jako taki nie jest częścią C++ postrzegany przez model pamięciowy i może być wykonywany w rejestrach (zwróć uwagę, że kompilator może zoptymalizować od siebie q i wykonać operacje w rejestrze, podczas gdy p nie może być zoptymalizowany

+0

Masz rację, przepraszam za moje słabe frazowanie, naprawię mój post. –

Powiązane problemy