2014-07-03 10 views
16

Rozważmy następujący (sztuczny) Przykład:Dlaczego ostream wypisuje `1` dla łańcucha zdefiniowanego jako` volatile char [] `?

#include <cstdio> 
#include <iostream> 

int main() { 
    volatile char test[] = "abc"; 
    std::printf("%s\n", test); 
    std::cout << test << "\n"; 
} 

Kompilacja ją GCC i biegania daje następujący wynik:

$ g++ test.cc 
$ ./a.out 
abc 
1 

Jak widać printf wydruki ciąg poprawnie podczas cout drukuje 1. Dlaczego pisanie do cout powoduje w tym przypadku 1?

+3

'lotny char [N]' pasuje 'bool' lepiej niż n 'const char *'. Właściwie to nie pasuje do 'const char *' w ogóle. – chris

+0

@sharth świetny połów, nawet nie pomyślałem, aby szukać dup. Są na tyle blisko, że mogą się dobrze scalić. –

Odpowiedz

14

jedynym odpowiednim przeciążeniu operator<< że dla bool, tak więc matryca jest przekształcany (poprzez wskaźnik) do bool, dając true od jego adres nie jest zerowy. Ten wynik jest wyświetlany jako 1, chyba że używasz manipulatora std::boolalpha.

Nie można użyć przeciążenie dla const char * które wyjście ciąg, albo że za const void * które wyjściowa wartość wskaźnika, ponieważ te konwersje wymagałoby usunięcie volatile kwalifikator. Niejawne konwersje wskaźników mogą dodawać kwalifikatory, ale nie mogą ich usuwać.

Aby wyjściu ciąg, trzeba by wyrzucać kwalifikator:

std::cout << const_cast<const char*>(test) << "\n"; 

ale pamiętaj, że to daje niezdefiniowane zachowanie, ponieważ będzie dostępna tablica się tak, jakby nie były niestabilne.

printf to staroszkolna funkcja variadyczna, nie dająca żadnego rodzaju bezpieczeństwa. Specyfikator %s powoduje, że interpretuje on argument jako const char *, czymkolwiek on jest.

+2

Czy to jest technicznie legalne? Czy operator '' optymalizuje się przy założeniu, że ciąg nie jest niestabilny? Na przykład może to zrobić 'strlen (s)', aby zdecydować, jak duży bufor do przydzielenia, a następnie użyj 'strcpy' do skopiowania do tego bufora. –

+2

Odpowiedzi za pomocą rzutów mają niezdefiniowane zachowanie: C++ 14 [dcl.type.cv] p6 mówi: "Jeśli podjęto próbę odniesienia się do obiektu zdefiniowanego za pomocą typu o zmiennym lotnictwie poprzez użycie glvalue z typ o charakterze lotnym, zachowanie programu jest niezdefiniowane. " Zasadniczo można oddziaływać jedynie z niestabilnymi tablicami poprzez ręcznie napisane pętle nad elementami. –

+0

@Jeffrey Yasskin Zastanawiam się nad tym, ale 'const_cast' w tym przypadku daje' prvalue' o ile mogę powiedzieć, ale jeszcze nie przemyślałem szczegółów. –

5

std::basic_ostream::operator<< ma tylko przeciążenie dla const char* lub const void* który nie pasuje w tym przypadku, ponieważ nie można odrzucić lotny kwalifikator bez obsady, jest to ujęte w draft C++ standard sekcji 4.4Kwalifikacyjnych konwersji który mówi:

prvalue typu „wskaźnik CV1 T” może być przekształcony w prvalue z typu „wskaźnik do CV2 T”, gdy „CV2 T” jest bardziej obiecujące niż CV CV1 „T”.

więc jest używana wersja bool a ponieważ nie jest nullptr wynik jest true.

Po usunięciu kwalifikatora zmiennego z test zapewni to oczekiwany wynik. Kilka odpowiedzi sugeruje użycie const_cast do usunięcia lotnych kwalifikatorów, ale jest to niezdefiniowane zachowanie. Możemy zobaczyć, przechodząc do sekcji 7.1.6.1CV-kwalifikatorów pkt który mówi:

Jeśli próbuje się odnosić do obiektu określonym z lotny wykwalifikowanej typu poprzez zastosowanie a glvalue z typem nielotnym, zachowanie programu jest niezdefiniowane.

const_cast w tym przypadku daje prvalue ale dereferencing że wskaźnik daje się lwartość które powoływania niezdefiniowanej zachowanie.

2

Odpowiedź znaleziono here przez minimalną ilością poszukiwań internetowej:

Krótka odpowiedź: cout interpretuje obiekt jako bool powodu volatile kwalifikacjach. To dziwne przeciążenie dla operatora <<.

Długa odpowiedź: Lotny wskaźnik nie może być przekształcony w nieulotnej wskaźnik bez wyraźnego obsady, więc ani char* ani przeciążenie void* mogą być stosowane, gdy operator << nazywa. Nie ma lotnego, kwalifikowanego przeciążenia, a najbliższym przeciążeniem jest przeciążenie bool, dlatego tablica jest interpretowana jako wartość boolowska, a nie jako adres lub ciąg.

Można go naprawić na kilka sposobów, ale wyraźne obsada jest chyba to, czego chciał: (. Osobiście chciałbym rzucić do const char*)

std::cout<< (char*)test <<std::endl; 

0

To volatile kwalifikator że rzuca to na bool, spróbuj zamiast:

std::cout << const_cast<char*>(test) << "\n"; 
+0

Ta odpowiedź ma niezdefiniowane zachowanie: C++ 14 [dcl.type.cv] p6 mówi: "Jeśli podjęta zostanie próba odniesienia się do obiektu zdefiniowanego za pomocą typu o zmiennej lotności poprzez użycie wartości glownej z typem nieulotnym, zachowanie programu jest niezdefiniowane." –

+0

@JeffreyYasskin odgadnąć, że zmienna jest dość ... 'lotny'! ;) –

Powiązane problemy