2009-10-07 28 views
5

C++ w MS Visual Studio 2008. Poziom ostrzeżenia 4 plus ładunek dodatkowych ostrzeżeń również włączony. Spodziewam się, że to ostrzeżenie: co najmniej, ale bardziej prawdopodobny błąd kompilatora?Dlaczego/jak to się kompiluje?

deklaracji funkcji jest następująca:

int printfLikeFunction(
    const int    bufferLength, 
    char * const   buffer, 
    const char * const  format, 
    ...); 

wykorzystanie kodu - jest literówka: chociaż ARRAY_SIZE z outputBuffer jest przekazywana sama outputBuffer nie jest - na pewno nie powinno to skompilować:

printfLikeFunction(ARRAY_SIZE(outputBuffer), "Format: %s, %s", arg1, arg2); 

Oczywiście jest to błędne i popełniono błąd. Jednak powinien go złapać kompilator! Parametr buffer powinien być wskaźnikiem znaków i przekazywany jest literał łańcuchowy, który jest const char-pointer. To musi być błąd. (arg1 i arg2 są również (prawdopodobnie const) znakami, tak więc deklaracja jest dopasowywana nawet bez outputBuffer znajdującego się we właściwym miejscu).

W czasie wykonywania ten kod ulega awarii podczas próby zapisu w literale łańcuchowym. Nic dziwnego, po prostu nie rozumiem, w jaki sposób można było je skompilować.

(Chociaż, przypuszczalnie, jest to przypuszczalnie, dlaczego sprintf_s ma parametry bufora i rozmiaru w innej kolejności niż ta funkcja - powoduje, że takie błędy jednoznacznie zawodzą).

Odpowiedz

9

C++ ma specjalną lukę w odniesieniu do literałów łańcuchowych w celu uzyskania zgodności z kodem stylu w stylu pre-const. Chociaż literały łańcuchowe są tablicami const char, można je przekonwertować na wskaźnik na znak inny niż const.

Parafrazowanie 4.2/2 [conv.array]: "wąski" literał literowy można przekonwertować na wartość rytmu wskaźnika typu na non-constchar. Konwersja jest uwzględniana tylko wtedy, gdy istnieje wyraźny typ celu (np. Parametr funkcji), a nie wtedy, gdy wymagana jest ogólna konwersja wartości do rwartości.

Ta konwersja jest przestarzała, ale wciąż dostępna. Zauważ, że podczas konwersji konwersja literału na wskaźnik na typ inny niż const char nadal wywoływałaby niezdefiniowane zachowanie, próbując zmodyfikować dowolny ze znaków w literale łańcuchowym za pomocą tego wskaźnika.

+0

Wow. Cóż za zwariowana decyzja! Ale tak, właśnie sprawdziłem Standard i masz absolutną rację. Nie do wiary! –

+0

Można mieć nadzieję, że Visual Studio wyświetli ostrzeżenie, gdy będzie używać przestarzałych i zwariowanych funkcji standardu. –

+0

Tak ... nawet jeśli "szaleństwo" jest subiektywne, przestarzałe nie jest, więc zadziwia mnie, że nie spowodowało to ostrzeżenia. –

2

Przypominam sobie, że kompilator ma opcję, która kontroluje sposób traktowania literałów łańcuchowych. Domyślnie są one traktowane jako char *, aby nie złamać całego istniejącego kodu, który nie jest stały, ale można go zmienić, aby traktować je jako const char *. Może to pomóc w wyzwoleniu błędu, którego szukasz.

(później) Nie mam obecnie kompilatora Microsoftu, ale patrząc przez odniesienie na MSDN nie mogę znaleźć takiej opcji. Być może myślę o GCC, który ma taką opcję.

+0

Spójrz na odpowiedź Charlesa (http://stackoverflow.com/questions/1530330/why-how-does-this-compile/1530469#1530469). Istnieje nieaktualna konwersja z dowolnego wąskiego ciągu literału na "char *", który pozwala na kompilowanie starego kodu. IMO to PITA, ponieważ pozwala kodowi jak wyżej skompilować bez żadnego błędu, ale tak właśnie jest. – sbi

+0

Interesujące, dzięki! –

1

int printfLikeFunction ( const int bufferLength, char * bufor const, const char * const Format, ...);

parametr bufora jest określony jako char * const, więc chroni tylko adres bufora, który ma zostać zmodyfikowany, a nie zawartość bufora.

Aby uniknąć zapisywania bufora, musisz zadeklarować go jako const char * const, podobnie jak format.

kompilator umożliwia zapisanie do bufora, ponieważ zadeklarowano go jako char *. modyfikator przyrostka stf zapobiega tylko ponownemu przypisaniu wartości bufora do funkcji.

patrz http://jriddell.org/const-in-cpp.html aby const wpływ modyfikatora na zmienne

+1

Myślę, że problem polega na tym, że OP zapomniał przekazać parametr 'buffer', a jaki powinien być parametr' format', który jest przyjmowany jako 'buffer'. – dave4420

+0

Przypuśćmy, że bufor ma być modyfikowany, to tutaj zostanie zapisany ciąg wyjściowy. Problem polega na tym, że literał łańcuchowy traktowany jest jako znak *, podczas gdy powinien być traktowany jako const char *. –

+0

I bufor ma zostać zmodyfikowany, a stała literalna jest przekazywana jako wartość parametru bufora, nastąpi uszkodzenie pamięci, ponieważ kompilator umieści literał w stałym miejscu w pamięci. – dweeves

1

łańcucha znaków w C są const wskaźniki Char (char*const) nie const wskaźniki do const znaków (const char* const).

C++ pierwotnie stosowało C, ale potem standard ANSI C++ został w pewnym momencie zmieniony (nie jestem pewien kiedy), aby je ustawić const char* const.Produkty Microsoft tradycyjnie miały tendencję do porównywania wstecznej zgodności z poprzednimi wersjami pod względem zgodności - może istnieć opcja kompilatora wymuszająca "nowe" zachowanie, ale ponieważ przykłady literałów łańcuchowych w MSDN nie są stałe, wątpię, że tak jest.

+2

Właściwie, w języku C++, wąskie napisy łańcuchowe mają typ 'const char []', który jest niejawnie przekształcony na 'const char *'. Jak wyjaśnił Charles, istnieje jednak nieaktualna konwersja na "char *". – sbi

Powiązane problemy