2013-07-02 24 views
7

Wszystkie standardy C i C++ zawierają tekst mówiący o tym, że jeśli operacja łańcuchowa nie może wygenerować prawidłowego tokenu literowego, zachowanie jest niezdefiniowane. W C++ 11 jest to faktycznie możliwe, poprzez dodanie znaku nowego wiersza w surowym słowie kluczowym. Ale w standardach zawsze tkwił haczyk.Niepoprawna operacja ciągów znaków

Czy istnieje inny sposób, w jaki sznurka może produkować UB, gdzie UB lub źle sformułowany program jeszcze się nie stało?

Chciałbym usłyszeć o dowolnym dialekcie C lub C++ w ogóle. Jestem writing preprocesorem.

+3

Większość ludzi zmaga się, aby zmusić ich do pracy, a nie nie zdać. –

+0

Hah ... Chciałbym sprawdzić, czy błąd działa, tj. Dostać test. Sztuczka z nowymi liniami nie pomaga, ponieważ przechwytuję pułapkę i dodam '\ n'. (Cóż, to jest '' \\\\ n "' jeśli liczą się backsliski.) – Potatoswatter

+0

OK, wtedy nie dostałem tego, o co prosisz, potrzebujesz preprocesorów testowych. mcpp ma pakiet sprawdzania poprawności. –

Odpowiedz

4

Operator stringify (#) wymyka się tylko \ ze stałymi łańcuchowymi. Rzeczywiście, \ nie ma szczególnego znaczenia poza stałą łańcuchową, z wyjątkiem na końcu linii. Jest to zatem token przetwarzania wstępnego (sekcja C 6.4, C++ sekcja 2.5).

konsekwencji, jeśli mamy

#define Q(X) #X 

następnie

Q(\) 

jest uzasadniony wezwanie: the \ jest wyrazem przerób, która nigdy nie jest konwertowany do tokena, więc jest to ważne. Ale nie możesz stringify \; to dałoby ci "\", co nie jest poprawnym dosłownym ciągiem znaków. Dlatego zachowanie powyższego jest niezdefiniowane.

Oto bardziej zabawny przypadek testowy:

#define Q(A) #A 
#define ESCAPE(c) Q(\c) 
const char* new_line=ESCAPE(n); 
const char* undefined_behaviour=ESCAPE(x); 

Mniej ciekawy przypadek nieokreślonej stringify gdzie parametr stringified byłby zbyt długi, aby być ciągiem dosłowne. (Standardy zalecają, aby maksymalny rozmiar literału ciągu znaków wynosił co najmniej 65536 znaków, ale nie mówił nic o maksymalnym rozmiarze makropolecenia, który prawdopodobnie mógłby być większy.)

+0

Dzięki! Powinieneś o tym pomyśleć. Ciąg bez znaku jest już czymś, co przetestowałem w surowej ciągówce, a to zostanie uwięzione w ten sam sposób :). Twój bardziej zabawny przypadek nie wydaje się być UB w preprocesorze; to jest dokładnie tak samo jak pisanie '\ x" lub czy czegoś brakuje? (Sekwencje ucieczki są później tłumaczone.) – Potatoswatter

+0

@Potatoswatter: Literał łańcuchowy zawiera s-znaki, sekwencje escape i nazwy uniwersalnych znaków. '\ x' nie jest żadnym z powyższych. Zatem '" \ x "' nie jest poprawnym dosłownym ciągiem znaków, sposób, w jaki go widzę, a zatem sposób, w jaki preprocesor radzi sobie z 'ESCAPE (x)' (lub, jeśli o to chodzi, 'ESCAPE (*)') jest niezdefiniowany. Tak więc preprocesor mógłby, jeśli zdecydował, zastąpić oba z nich buźką. – rici

+0

Przynajmniej w C++: "Sekwencje Escape, w których znak następujący po ukośniku odwrotnym nie jest wymieniony w tabeli 7, są warunkowo obsługiwane, z semantyką zdefiniowaną przez implementację." Tak więc dla dyskretnego preprocesora, myślę, że pułapkowanie byłoby nieco restrykcyjne. Ale masz rację, to jest gramatyka :) Jeszcze raz dziękuję! – Potatoswatter