Czy jest jakikolwiek przypadek, w którym pominięcie a #include
spowodowałoby uszkodzenie oprogramowania w czasie wykonywania, podczas gdy kompilacja wciąż trwa?
Innymi słowy, czy to możliwe?
#include "some/code.h"
complexLogic();
cleverAlgorithms();
i
complexLogic();
cleverAlgorithms();
czy oba będą się pomyślnie budować, ale zachowają się inaczej?
#include
d.#include <vld.h>
w strategicznej pozycji w kodzie. Usunięcie lub dodanie tego nagłówka VLD nie „łamie” programu, ale znacząco wpływa na zachowanie środowiska wykonawczego. Widziałem, jak VLD spowalnia program do tego stopnia, że stał się bezużyteczny.Odpowiedzi:
Tak, jest to całkowicie możliwe. Jestem pewien, że jest wiele sposobów, ale załóżmy, że plik dołączania zawierał definicję zmiennej globalnej, która nazywała się konstruktorem. W pierwszym przypadku konstruktor wykonałby, a w drugim nie.
Umieszczenie definicji zmiennej globalnej w pliku nagłówkowym jest złym stylem, ale jest możliwe.
źródło
<iostream>
robi to właśnie w standardowej bibliotece; jeśli jakakolwiek jednostka tłumaczeniowa zawiera,<iostream>
wówczasstd::ios_base::Init
obiekt statyczny zostanie skonstruowany na początku programu, inicjując strumienie znakówstd::cout
itp., w przeciwnym razie nie.Tak, to możliwe.
Wszystko, co dotyczy
#include
s, dzieje się w czasie kompilacji. Ale czas kompilacji może oczywiście zmienić zachowanie w czasie wykonywania:some/code.h
:następnie
Dzięki rozwiązaniu
#include
przeciążanie jest bardziej odpowiedniefoo(int)
i dlatego drukuje1
zamiast2
. Ponadto, ponieważFOO
jest zdefiniowany, dodatkowo drukujeFOO
.To tylko dwa (niepowiązane) przykłady, które od razu przyszły mi do głowy i jestem pewien, że jest o wiele więcej.
źródło
Aby wskazać trywialny przypadek, dyrektywy prekompilatora:
I wtedy
Być może jest to patologiczne, ale zdarzył się podobny przypadek:
Wygląda niewinnie. Próbuje zadzwonić
std::max
. Jednak windows.h definiuje maxGdyby tak było
std::max
, byłoby to normalne wywołanie funkcji, które ocenia f () raz g () raz. Ale z Windows.h tam teraz ocenia dwa razy f () lub g (): raz podczas porównania i raz, aby uzyskać wartość zwracaną. Jeśli f () lub g () nie były idempotentne, może to powodować problemy. Na przykład, jeśli jeden z nich jest licznikiem, który za każdym razem zwraca inną liczbę ...źródło
using namespace std;
i użyjeszstd::max(f(),g());
, kompilator złapie problem (z niejasnym komunikatem, ale przynajmniej wskazującym na stronę wywoływania).Możliwe jest brak specjalizacji szablonu.
źródło
Niekompatybilność binarna, dostęp do członka lub jeszcze gorzej, wywołanie funkcji niewłaściwej klasy:
Funkcja go używa i jest w porządku:
Wprowadzanie innej wersji klasy:
Używając funkcji main, druga definicja zmienia definicję klasy. Prowadzi to do binarnej niezgodności i po prostu ulega awarii w czasie wykonywania. I napraw ten problem, usuwając pierwszy plik include w main.cpp:
Żaden z wariantów nie generuje błędu czasu kompilacji lub łącza.
Odwrotna sytuacja, dodanie parametru include naprawia awarię:
Sytuacje te są jeszcze trudniejsze, gdy naprawia się błąd w starej wersji programu lub używa zewnętrznej biblioteki / dll / współdzielonego obiektu. Dlatego czasami należy przestrzegać zasad binarnej kompatybilności wstecznej.
źródło
Chcę podkreślić, że problem istnieje również w C.
Możesz powiedzieć kompilatorowi, że funkcja korzysta z konwencji wywoływania. Jeśli tego nie zrobisz, kompilator będzie musiał zgadywać, że używa domyślnego, inaczej niż w C ++, gdzie kompilator może odmówić jego skompilowania.
Na przykład,
main.c
foo.c
W systemie Linux na platformie x86-64 moje dane wyjściowe to
Jeśli pominiesz tutaj prototyp, kompilator zakłada, że masz
Konwencja dotycząca nieokreślonych list argumentów wymaga
float
przekonwertowania nadouble
przesłaną. Więc chociaż dałem1.0f
, kompilator konwertuje go,1.0d
aby przekazaćfoo
. I zgodnie z Suplementem do Procesora architektury B64 interfejsu systemu V w aplikacji V,double
dostaje się w 64 najmniej znaczących bitachxmm0
. Alefoo
oczekuje liczby zmiennoprzecinkowej i odczytuje ją z 32 najmniej znaczących bitówxmm0
, i otrzymuje 0.źródło