Czy istnieje sposób na ustalenie (oczywiście programowo), czy dany wskaźnik jest „prawidłowy”? Sprawdzanie wartości NULL jest łatwe, ale co z takimi rzeczami, jak 0x00001234? Podczas próby wyłuskiwania tego rodzaju wskaźnika następuje wyjątek / awaria.
Preferowana jest metoda wieloplatformowa, ale specyficzna dla platformy (dla Windows i Linux) jest również w porządku.
Aktualizacja w celu wyjaśnienia: problem nie dotyczy nieaktualnych / zwolnionych / niezainicjowanych wskaźników; zamiast tego implementuję API, które pobiera wskaźniki od wywołującego (jak wskaźnik do ciągu, uchwyt pliku itp.). Wzywający może wysłać (celowo lub przez pomyłkę) nieprawidłową wartość jako wskaźnik. Jak zapobiec awariom?
Odpowiedzi:
Nie możesz tego sprawdzić. Po prostu nie ma możliwości sprawdzenia, czy wskaźnik jest „prawidłowy”. Musisz ufać, że kiedy ludzie używają funkcji, która pobiera wskaźnik, ci ludzie wiedzą, co robią. Jeśli przekażą ci 0x4211 jako wartość wskaźnika, musisz zaufać, że wskazuje adres 0x4211. A jeśli „przypadkowo” uderzą w obiekt, to nawet jeśli użyjesz jakiejś przerażającej funkcji systemu operacyjnego (IsValidPtr lub cokolwiek innego), nadal wpadniesz w błąd i szybko nie zawiedziesz.
Zacznij używać pustych wskaźników do sygnalizowania tego rodzaju rzeczy i powiedz użytkownikowi swojej biblioteki, że nie powinni używać wskaźników, jeśli mają tendencję do przypadkowego przekazywania nieprawidłowych wskaźników, poważnie :)
źródło
Oto trzy proste sposoby, dzięki którym program w C pod Linuksem może introspektywnie ocenić stan pamięci, w której jest uruchomiony, i dlaczego w niektórych kontekstach pytanie ma odpowiednio wyrafinowane odpowiedzi.
W systemie Microsoft Windows istnieje funkcja QueryWorkingSetEx, która jest udokumentowana w interfejsie API stanu procesu (również w interfejsie API NUMA). Jako następstwo wyrafinowanego programowania NUMA API, ta funkcja pozwoli ci również wykonywać proste prace „testowania wskaźników poprawności (C / C ++)”, jako że jest mało prawdopodobne, aby była przestarzała przez co najmniej 15 lat.
źródło
Zapobieganie awariom spowodowanym przez wysyłanie przez dzwoniącego nieprawidłowego wskaźnika to dobry sposób na tworzenie cichych błędów, które są trudne do znalezienia.
Czy nie jest lepiej dla programisty używającego twojego API, aby uzyskać jasny komunikat, że jego kod jest fałszywy, przez awarię go, a nie ukrywanie?
źródło
W Win32 / 64 jest na to sposób. Spróbuj odczytać wskaźnik i przechwycić wynikowe wykonanie SEH, które zostanie wyrzucone w przypadku niepowodzenia. Jeśli nie rzuca, jest to prawidłowy wskaźnik.
Problem z tą metodą polega jednak na tym, że zwraca ona tylko informację, czy można odczytać dane ze wskaźnika. Nie gwarantuje bezpieczeństwa typu ani żadnej liczby innych niezmienników. Ogólnie rzecz biorąc, ta metoda jest dobra tylko do powiedzenia „tak, potrafię odczytać to konkretne miejsce w pamięci w czasie, który właśnie minął”.
Krótko mówiąc, nie rób tego;)
Raymond Chen zamieścił post na blogu na ten temat: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx
źródło
AFAIK nie ma sposobu. Powinieneś spróbować uniknąć tej sytuacji, zawsze ustawiając wskaźniki na NULL po zwolnieniu pamięci.
źródło
Spójrz na to i to pytanie. Spójrz także na inteligentne wskazówki .
źródło
Odnośnie odpowiedzi nieco wyżej w tym wątku:
Radzę trzymać się od nich z daleka, ktoś już to opublikował: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx
Innym postem na ten sam temat i tego samego autora (chyba) jest ten: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx („IsBadXxxPtr powinno naprawdę nazywać się CrashProgramRandomly ”).
Jeśli użytkownicy twojego API wysyłają złe dane, pozwól mu się zawiesić. Jeśli problem polega na tym, że przekazane dane nie są używane do później (co utrudnia znalezienie przyczyny), dodaj tryb debugowania, w którym łańcuchy znaków itp. Są rejestrowane na wejściu. Jeśli są złe, będzie to oczywiste (i prawdopodobnie ulegnie awarii). Jeśli zdarza się to zbyt często, warto przenieść interfejs API poza proces i pozwolić mu na awarię procesu API zamiast głównego procesu.
źródło
Po pierwsze, nie widzę sensu w próbach ochrony przed dzwoniącym celowo próbującym spowodować awarię. Mogliby to łatwo zrobić, próbując samodzielnie uzyskać dostęp przez nieprawidłowy wskaźnik. Jest wiele innych sposobów - mogą one po prostu nadpisać pamięć lub stos. Jeśli chcesz zabezpieczyć się przed tego rodzaju rzeczami, musisz uruchomić oddzielny proces, używając gniazd lub innego IPC do komunikacji.
Piszemy sporo oprogramowania, które umożliwia partnerom / klientom / użytkownikom rozszerzenie funkcjonalności. Nieuchronnie każdy błąd jest najpierw zgłaszany do nas, więc warto mieć możliwość łatwego pokazania, że problem tkwi w kodzie wtyczki. Ponadto istnieją obawy dotyczące bezpieczeństwa, a niektórzy użytkownicy są bardziej zaufani niż inni.
Używamy wielu różnych metod w zależności od wymagań dotyczących wydajności / przepustowości i wiarygodności. Od najbardziej preferowanych:
oddzielne procesy wykorzystujące gniazda (często przekazujące dane jako tekst).
oddzielne procesy korzystające z pamięci współdzielonej (w przypadku przesyłania dużych ilości danych).
ten sam proces oddzielne wątki za pośrednictwem kolejki komunikatów (w przypadku częstych krótkich komunikatów).
ten sam proces rozdziela wątki wszystkie przekazywane dane przydzielone z puli pamięci.
ten sam proces przez bezpośrednie wywołanie procedury - wszystkie przekazane dane przydzielone z puli pamięci.
Staramy się nigdy nie uciekać się do tego, co próbujesz zrobić, mając do czynienia z oprogramowaniem stron trzecich - zwłaszcza gdy wtyczki / bibliotekę otrzymujemy jako kod binarny, a nie źródłowy.
Korzystanie z puli pamięci jest dość łatwe w większości przypadków i nie musi być nieefektywne. Jeśli TY przydzielasz dane w pierwszej kolejności, sprawdzenie wskaźników względem przydzielonych wartości jest banalne. Można również zapisać przydzieloną długość i dodać „magiczne” wartości przed i po danych, aby sprawdzić prawidłowy typ danych i przepełnienia danych.
źródło
W systemie Unix powinieneś być w stanie wykorzystać wywołanie systemowe jądra, które sprawdza wskaźnik i zwraca EFAULT, takie jak:
#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <stdbool.h> bool isPointerBad( void * p ) { int fh = open( p, 0, 0 ); int e = errno; if ( -1 == fh && e == EFAULT ) { printf( "bad pointer: %p\n", p ); return true; } else if ( fh != -1 ) { close( fh ); } printf( "good pointer: %p\n", p ); return false; } int main() { int good = 4; isPointerBad( (void *)3 ); isPointerBad( &good ); isPointerBad( "/tmp/blah" ); return 0; }
powracający:
bad pointer: 0x3 good pointer: 0x7fff375fd49c good pointer: 0x400793
Prawdopodobnie istnieje lepsze wywołanie systemowe niż open () [być może dostęp], ponieważ istnieje szansa, że może to doprowadzić do rzeczywistej ścieżki kodowej tworzenia pliku i późniejszego zamknięcia wymagania.
źródło
Mam wiele zrozumienia dla twojego pytania, ponieważ sam jestem w prawie identycznej sytuacji. Doceniam to, co mówi wiele odpowiedzi, i są one poprawne - procedura dostarczająca wskaźnik powinna zapewniać prawidłowy wskaźnik. W moim przypadku jest prawie nie do pomyślenia, że mogli uszkodzić wskaźnik - ale gdyby im się to udało, to MOJE oprogramowanie uległo awarii, a JA byłby winny :-(
Nie wymagam, abym kontynuował działanie po błędzie segmentacji - to byłoby niebezpieczne - po prostu chcę zgłosić klientowi, co się stało przed zakończeniem, aby mógł naprawić swój kod, zamiast obwiniać mnie!
Oto jak to zrobiłem (w systemie Windows): http://www.cplusplus.com/reference/clibrary/csignal/signal/
Aby podać streszczenie:
#include <signal.h> using namespace std; void terminate(int param) /// Function executed if a segmentation fault is encountered during the cast to an instance. { cerr << "\nThe function received a corrupted reference - please check the user-supplied dll.\n"; cerr << "Terminating program...\n"; exit(1); } ... void MyFunction() { void (*previous_sigsegv_function)(int); previous_sigsegv_function = signal(SIGSEGV, terminate); <-- insert risky stuff here --> signal(SIGSEGV, previous_sigsegv_function); }
Teraz wygląda na to, że zachowuje się tak, jak bym chciał (wyświetla komunikat o błędzie, a następnie zamyka program) - ale jeśli ktoś zauważy usterkę, daj mi znać!
źródło
exit()
, omija RAII, a tym samym może powodować wycieki zasobów.exit()
i zaczął dzwonić mój Portable C ++ Alarm Bell. Powinno być w porządku w tej specyficznej dla Linuksa sytuacji, w której program i tak by się zakończył, przepraszam za hałas.man 2 signal
w Linuksie zawiera akapit wyjaśniający dlaczego.W C ++ nie ma przepisów do testowania poprawności wskaźnika jako przypadku ogólnego. Można oczywiście założyć, że NULL (0x00000000) jest zły, a różne kompilatory i biblioteki lubią używać tu i tam "specjalnych wartości", aby ułatwić debugowanie (na przykład, jeśli kiedykolwiek zobaczę wskaźnik pokazujący się jako 0xCECECECE w Visual Studio, wiem Zrobiłem coś źle), ale prawda jest taka, że skoro wskaźnik jest tylko indeksem w pamięci, prawie nie da się stwierdzić, patrząc na wskaźnik, czy jest to „właściwy” indeks.
Istnieją różne sztuczki, które możesz zrobić za pomocą dynamic_cast i RTTI, aby upewnić się, że wskazywany obiekt jest odpowiedniego typu, ale wszystkie one wymagają przede wszystkim wskazania czegoś ważnego.
Jeśli chcesz mieć pewność, że program może wykryć „nieprawidłowe” wskaźniki, moja rada jest następująca: Ustaw każdy deklarowany wskaźnik na NULL lub prawidłowy adres natychmiast po utworzeniu i ustaw go na NULL natychmiast po zwolnieniu pamięci, na którą wskazuje. Jeśli pilnie podchodzisz do tej praktyki, sprawdzenie NULL jest wszystkim, czego kiedykolwiek potrzebujesz.
źródło
Nie ma na to żadnego przenośnego sposobu, a zrobienie tego na określonych platformach może być trudne lub niemożliwe. W każdym razie nigdy nie powinieneś pisać kodu, który zależy od takiej kontroli - nie pozwól, aby wskaźniki w pierwszej kolejności przyjmowały nieprawidłowe wartości.
źródło
Ustawienie wskaźnika na NULL przed i po użyciu jest dobrą techniką. Jest to łatwe do zrobienia w C ++, jeśli zarządzasz wskaźnikami w klasie, na przykład (ciąg znaków):
class SomeClass { public: SomeClass(); ~SomeClass(); void SetText( const char *text); char *GetText() const { return MyText; } void Clear(); private: char * MyText; }; SomeClass::SomeClass() { MyText = NULL; } SomeClass::~SomeClass() { Clear(); } void SomeClass::Clear() { if (MyText) free( MyText); MyText = NULL; } void SomeClass::Settext( const char *text) { Clear(); MyText = malloc( strlen(text)); if (MyText) strcpy( MyText, text); }
źródło
Akceptowanie dowolnych wskaźników jako parametrów wejściowych w publicznym interfejsie API nie jest dobrą zasadą. Lepiej jest mieć typy „zwykłych danych”, takie jak liczba całkowita, ciąg znaków lub struktura (mam na myśli oczywiście klasyczną strukturę z czystymi danymi; oficjalnie strukturą może być wszystko).
Czemu? Cóż, ponieważ jak mówią inni, nie ma standardowego sposobu sprawdzenia, czy otrzymałeś prawidłowy wskaźnik, czy taki, który wskazuje na śmieci.
Ale czasami nie masz wyboru - twoje API musi akceptować wskaźnik.
W takich przypadkach obowiązkiem dzwoniącego jest przekazanie dobrego wskaźnika. NULL może być akceptowana jako wartość, ale nie jako wskaźnik do śmieci.
Czy możesz w jakikolwiek sposób sprawdzić dwukrotnie? Cóż, w takim przypadku zdefiniowałem niezmiennik typu, na który wskazuje wskaźnik, i wywołałem go, gdy go otrzymasz (w trybie debugowania). Przynajmniej jeśli niezmiennik zawiedzie (lub ulegnie awarii), wiesz, że przekazano Ci złą wartość.
// API that does not allow NULL void PublicApiFunction1(Person* in_person) { assert(in_person != NULL); assert(in_person->Invariant()); // Actual code... } // API that allows NULL void PublicApiFunction2(Person* in_person) { assert(in_person == NULL || in_person->Invariant()); // Actual code (must keep in mind that in_person may be NULL) }
źródło
Jak powiedzieli inni, nie można niezawodnie wykryć nieprawidłowego wskaźnika. Rozważ kilka form, jakie może przybierać nieprawidłowy wskaźnik:
Możesz mieć pusty wskaźnik. Można to łatwo sprawdzić i coś z tym zrobić.
Możesz mieć wskaźnik do miejsca poza ważną pamięcią. To, co składa się na prawidłową pamięć, różni się w zależności od tego, jak środowisko wykonawcze systemu konfiguruje przestrzeń adresową. W systemach uniksowych jest to zwykle wirtualna przestrzeń adresowa zaczynająca się od 0 i sięgająca dużej liczby megabajtów. W systemach wbudowanych może być dość mały. W każdym razie może nie zaczynać się od 0. Jeśli zdarzy się, że Twoja aplikacja działa w trybie nadzorcy lub równoważnym, wskaźnik może odnosić się do rzeczywistego adresu, który może, ale nie musi, być zarchiwizowany z rzeczywistą pamięcią.
Możesz mieć wskaźnik do jakiegoś miejsca w twojej prawidłowej pamięci, nawet wewnątrz segmentu danych, bss, stosu lub sterty, ale nie wskazujący na prawidłowy obiekt. Wariantem tego jest wskaźnik, który wskazywał na prawidłowy obiekt, zanim coś złego stało się z obiektem. Złe rzeczy w tym kontekście obejmują zwolnienie alokacji, uszkodzenie pamięci lub uszkodzenie wskaźnika.
Możesz mieć płaski, niedozwolony wskaźnik, taki jak wskaźnik z niedozwolonym wyrównaniem dla elementu, do którego się odwołujesz.
Problem staje się jeszcze gorszy, gdy weźmie się pod uwagę architektury oparte na segmentach / przesunięciu i inne dziwne implementacje wskaźników. Takie rzeczy są zwykle ukrywane przed programistą przez dobre kompilatory i rozsądne użycie typów, ale jeśli chcesz przebić zasłonę i spróbować przechytrzyć system operacyjny i programistów kompilatorów, możesz, ale nie ma jednego ogólnego sposobu zrobić to, co rozwiąże wszystkie problemy, z którymi możesz się spotkać.
Najlepsze, co możesz zrobić, to zezwolić na awarię i podać dobre informacje diagnostyczne.
źródło
Ten artykuł MEM10-C. Zdefiniuj i użyj funkcji sprawdzania poprawności wskaźnika, która mówi, że można do pewnego stopnia sprawdzić, szczególnie w systemie operacyjnym Linux.
źródło
Ogólnie rzecz biorąc, jest to niemożliwe. Oto jeden szczególnie nieprzyjemny przypadek:
struct Point2d { int x; int y; }; struct Point3d { int x; int y; int z; }; void dump(Point3 *p) { printf("[%d %d %d]\n", p->x, p->y, p->z); } Point2d points[2] = { {0, 1}, {2, 3} }; Point3d *p3 = reinterpret_cast<Point3d *>(&points[0]); dump(p3);
Na wielu platformach wydrukuje się to:
[0 1 2]
Zmuszasz system wykonawczy do nieprawidłowej interpretacji bitów pamięci, ale w tym przypadku nie nastąpi awaria, ponieważ wszystkie bity mają sens. Jest częścią projektu języka (spojrzenie w stylu C polimorfizmu z
struct inaddr
,inaddr_in
,inaddr_in6
), więc nie można niezawodnie chroni przed nią na każdej platformie.źródło
To niewiarygodne, ile mylących informacji można przeczytać w artykułach powyżej ...
Nawet w dokumentacji Microsoft MSDN twierdzi się, że IsBadPtr został zbanowany. No cóż - wolę działającą aplikację niż zawieszanie się. Nawet jeśli praca terminowa może działać nieprawidłowo (o ile użytkownik końcowy może kontynuować aplikację).
Googlując nie znalazłem żadnego przydatnego przykładu dla Windows - znalazłem rozwiązanie dla aplikacji 32-bitowych,
http://www.codeproject.com/script/Content/ViewAssociatedFile.aspx?rzp=%2FKB%2Fsystem%2Fdetect-driver%2F%2FDetectDriverSrc.zip&zep=DetectDriverSrc%2FDetectDriver%2Fdetect-driver%2F%2FDetectDriverSrc.zip&zep=DetectDriverSrc%2FDetectDriver%2Fsrc%2Fdrv = 2
ale muszę również obsługiwać aplikacje 64-bitowe, więc to rozwiązanie nie działa dla mnie.
Ale zebrałem kody źródłowe wina i udało mi się ugotować podobny rodzaj kodu, który działałby również dla 64-bitowych aplikacji - załączam kod tutaj:
#include <typeinfo.h> typedef void (*v_table_ptr)(); typedef struct _cpp_object { v_table_ptr* vtable; } cpp_object; #ifndef _WIN64 typedef struct _rtti_object_locator { unsigned int signature; int base_class_offset; unsigned int flags; const type_info *type_descriptor; //const rtti_object_hierarchy *type_hierarchy; } rtti_object_locator; #else typedef struct { unsigned int signature; int base_class_offset; unsigned int flags; unsigned int type_descriptor; unsigned int type_hierarchy; unsigned int object_locator; } rtti_object_locator; #endif /* Get type info from an object (internal) */ static const rtti_object_locator* RTTI_GetObjectLocator(void* inptr) { cpp_object* cppobj = (cpp_object*) inptr; const rtti_object_locator* obj_locator = 0; if (!IsBadReadPtr(cppobj, sizeof(void*)) && !IsBadReadPtr(cppobj->vtable - 1, sizeof(void*)) && !IsBadReadPtr((void*)cppobj->vtable[-1], sizeof(rtti_object_locator))) { obj_locator = (rtti_object_locator*) cppobj->vtable[-1]; } return obj_locator; }
Poniższy kod może wykryć, czy wskaźnik jest prawidłowy, czy nie, prawdopodobnie musisz dodać sprawdzanie NULL:
CTest* t = new CTest(); //t = (CTest*) 0; //t = (CTest*) 0x12345678; const rtti_object_locator* ptr = RTTI_GetObjectLocator(t); #ifdef _WIN64 char *base = ptr->signature == 0 ? (char*)RtlPcToFileHeader((void*)ptr, (void**)&base) : (char*)ptr - ptr->object_locator; const type_info *td = (const type_info*)(base + ptr->type_descriptor); #else const type_info *td = ptr->type_descriptor; #endif const char* n =td->name();
To pobiera nazwę klasy ze wskaźnika - myślę, że powinno wystarczyć do twoich potrzeb.
Jedną rzeczą, której wciąż się obawiam, jest wydajność sprawdzania wskaźników - we fragmencie kodu powyżej są już wykonywane 3-4 wywołania API - może to być przesada dla aplikacji, które mają krytyczne znaczenie czasowe.
Byłoby dobrze, gdyby ktoś mógł zmierzyć narzut sprawdzania wskaźników w porównaniu na przykład z wywołaniami C # / zarządzanymi C ++.
źródło
Rzeczywiście, coś można zrobić w określonej sytuacji: na przykład, jeśli chcesz sprawdzić, czy łańcuch będący wskaźnikiem łańcucha jest prawidłowy, użycie funkcji write (fd, buf, szie) syscall może pomóc ci zrobić magię: niech fd będzie deskryptorem pliku tymczasowego plik, który tworzysz do testu, a buf wskazuje na łańcuch, który sprawdzasz, jeśli wskaźnik jest nieprawidłowy, metoda write () zwróciłaby -1, a errno ustawione na EFAULT, co wskazuje, że buf znajduje się poza dostępną przestrzenią adresową.
źródło
Następujące działa w systemie Windows (ktoś wcześniej to zasugerował):
static void copy(void * target, const void* source, int size) { __try { CopyMemory(target, source, size); } __except(EXCEPTION_EXECUTE_HANDLER) { doSomething(--whatever--); } }
Funkcja musi być statyczną, samodzielną lub statyczną metodą jakiejś klasy. Aby przetestować tylko do odczytu, skopiuj dane do bufora lokalnego. Aby przetestować zapis bez modyfikowania treści, napisz je. Możesz testować tylko pierwszy / ostatni adres. Jeśli wskaźnik jest nieprawidłowy, kontrola zostanie przekazana do „doSomething”, a następnie poza nawiasy. Po prostu nie używaj niczego wymagającego destruktorów, takich jak CString.
źródło
W systemie Windows używam tego kodu:
void * G_pPointer = NULL; const char * G_szPointerName = NULL; void CheckPointerIternal() { char cTest = *((char *)G_pPointer); } bool CheckPointerIternalExt() { bool bRet = false; __try { CheckPointerIternal(); bRet = true; } __except (EXCEPTION_EXECUTE_HANDLER) { } return bRet; } void CheckPointer(void * A_pPointer, const char * A_szPointerName) { G_pPointer = A_pPointer; G_szPointerName = A_szPointerName; if (!CheckPointerIternalExt()) throw std::runtime_error("Invalid pointer " + std::string(G_szPointerName) + "!"); }
Stosowanie:
unsigned long * pTest = (unsigned long *) 0x12345; CheckPointer(pTest, "pTest"); //throws exception
źródło
IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () dla systemu Windows.
Zajmują one czas proporcjonalny do długości bloku, więc w celu sprawdzenia poprawności sprawdzam tylko adres początkowy.
źródło
Widziałem różne biblioteki używające jakiejś metody do sprawdzania pamięci bez odwołań i tym podobnych. Uważam, że po prostu „zastępują” metody alokacji i zwalniania pamięci (malloc / free), które mają pewną logikę, która śledzi wskaźniki. Przypuszczam, że to przesada w twoim przypadku użycia, ale byłby to jeden ze sposobów, aby to zrobić.
źródło
Z technicznego punktu widzenia można zastąpić operator new (i usunąć ) i zebrać informacje o całej przydzielonej pamięci, dzięki czemu można mieć metodę sprawdzania, czy pamięć sterty jest poprawna. ale:
nadal potrzebujesz sposobu, aby sprawdzić, czy wskaźnik jest przydzielony na stosie ()
będziesz musiał zdefiniować, co jest `` prawidłowym '' wskaźnikiem:
a) pamięć pod tym adresem jest przydzielona
b) pamięć pod tym adresem to adres początkowy obiektu (np. adres nie jest pośrodku dużej tablicy)
c) pamięć pod tym adresem jest adresem początkowym obiektu oczekiwanego typu
Konkluzja : podejście, o którym mowa, nie jest sposobem C ++, musisz zdefiniować pewne reguły, które zapewnią, że funkcja otrzyma prawidłowe wskaźniki.
źródło
Nie ma sposobu, aby to sprawdzić w C ++. Co powinieneś zrobić, jeśli inny kod przekazuje Ci nieprawidłowy wskaźnik? Powinieneś się rozbić. Czemu? Sprawdź ten link: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx
źródło
Dodatek do gotowych odpowiedzi:
Załóżmy, że twój wskaźnik może zawierać tylko trzy wartości - 0, 1 i -1, gdzie 1 oznacza prawidłowy wskaźnik, -1 nieprawidłową, a 0 inną nieprawidłową. Jakie jest prawdopodobieństwo, że wskaźnik ma wartość NULL, a wszystkie wartości są jednakowo prawdopodobne? 1/3. Teraz wyjmij poprawną wielkość przypadku, więc dla każdego nieprawidłowego przypadku masz stosunek 50:50, aby wyłapać wszystkie błędy. Wygląda dobrze, prawda? Skaluj to dla 4-bajtowego wskaźnika. Istnieją 2 ^ 32 lub 4294967294 możliwych wartości. Spośród nich tylko JEDNA wartość jest poprawna, jedna to NULL, a nadal pozostaje 4294967292 innych nieprawidłowych przypadków. Oblicz ponownie: masz test dla 1 z (4294967292+ 1) nieprawidłowych przypadków. Prawdopodobieństwo 2 x e-10 lub 0 dla większości praktycznych celów. Taka jest daremność czeku NULL.
źródło
Wiesz, nowy sterownik (przynajmniej w Linuksie), który jest w stanie to zrobić, prawdopodobnie nie byłby taki trudny do napisania.
Z drugiej strony tworzenie programów w ten sposób byłoby głupotą. Jeśli nie masz naprawdę konkretnego i jednorazowego użytku do czegoś takiego, nie polecałbym tego. Jeśli zbudowałeś dużą aplikację ładowaną ze stałymi sprawdzeniami poprawności wskaźnika, prawdopodobnie będzie to horrendalnie powolne.
źródło
Jeśli nie działają - następna aktualizacja systemu Windows to naprawi? Jeśli nie działają na poziomie koncepcyjnym - funkcja zostanie prawdopodobnie całkowicie usunięta z api systemu Windows.
Dokumentacja MSDN twierdzi, że są one zbanowane, a przyczyną tego jest prawdopodobnie błąd dalszego projektowania aplikacji (np. Generalnie nie powinieneś jeść po cichu nieprawidłowych wskaźników - oczywiście jeśli jesteś odpowiedzialny za projekt całej aplikacji) oraz wydajność / czas sprawdzania wskaźnika.
Ale nie powinieneś twierdzić, że nie działają z powodu jakiegoś bloga. W mojej aplikacji testowej zweryfikowałem, że działają.
źródło
te linki mogą być pomocne
_CrtIsValidPointer Sprawdza, czy określony zakres pamięci jest prawidłowy do odczytu i zapisu (tylko wersja debugowania). http://msdn.microsoft.com/en-us/library/0w1ekd5e.aspx
_CrtCheckMemory Potwierdza integralność bloków pamięci alokowanych w stercie debugowania (tylko wersja debugowania). http://msdn.microsoft.com/en-us/library/e73x0s4b.aspx
źródło