Czym jest NULL?

2008-04-24 21:42

Gdybym chciał byś złośliwy, to stwierdziłbym, że w C++ nawet ‘nic’ (czyli NULL) nie jest takie, jak być powinno. Ale ponieważ w rzeczywistości jestem wcieleniem łagodności (;]), napiszę raczej o tym, jak można zaradzić na niedogodności obecnej postaci wskaźnika pustego w C++.
Cały problem z NULL-em polega na tym, że nie jest on wartością odpowiedniego typu. Dwie klasyczne definicje tej stałej – jako 0 lub (void*)0 – mają zauważalne mankamenty. Pierwsza definiuje NULL jako liczbę, co sprawia, że dozwolone są bezsensowne podstawienia w rodzaju:

  1. int n = NULL;

Natomiast druga nie da się wprawdzie skonwertować na typ liczbowy, ale nie da się też niejawnie zmienić w wartość żadnego innego typu wskaźnikowego niż void*. A tego oczekiwalibyśmy po wskaźniku pustym. Dlatego z dwojga złego w standardzie przyjęto pierwszą definicję i NULL jest po prostu zerem.

To, czego naprawdę byśmy chcieli po NULL, to wartość 0, która:

  • może być podstawiona za dowolny typ wskaźnikowy
  • nie może być podstawiona za żaden inny typ, zwłaszcza liczbowy

To zaś da się osiągnąć, pisząc odpowiednią… klasę:

  1. #undef NULL
  2. const
  3.     class Null
  4.     {
  5.         public:
  6.             // konwersja na dowolny typ wskaźnikowy (także pointer-to-member)
  7.             template <typename T> operator T* () const { return 0; }
  8.             template <class C, typename T> operator T C::*() { return 0; }
  9.         private:
  10.             // zablokowanie pobierania adresu
  11.             void operator & () const;
  12.     }
  13. NULL;

Sztuczką są tu oczywiście szablony operatorów konwersji. Zapewniają one możliwość traktowania naszego NULL-a jako wartości dowolnego typu wskaźnikowego – łącznie ze wskaźnikami do składowych klas. A ze względu na brak innych konwersji, nie jest możliwe automatycznie przypisanie naszego pustego wskaźnika do zmiennej liczbowej.

Taki NULL jest na tyle dobry, że działa nawet z uchwytami Windows API, gdyż wewnętrznie są one zdefiniowane jako specyficzne wskaźniki. Dziwi więc, dlaczego nadal nie ma go chociażby w bibliotece standardowej C++. Najwyraźniej nawet jeśli chcemy mieć ‘nic’, trzeba się trochę napracować ;P

Be Sociable, Share!
Be Sociable, Share!
Tags: ,
Author: Xion, posted under Programming »


10 comments for post “Czym jest NULL?”.
  1. moriturius:
    April 24th, 2008 o 21:59

    Jednym z powodów dla których uwielbiam C/C++ i nie znoszę Pascala jest ścisła kontrola typów.

    Brak takiej ścisłej kontroli w C/C++ daje programiście ogromne możliwości. Naturalnie razem z możliwościami na barki programisty zrzucana jest także odpowiedzialność za działanie i czytelność.

    Stąd – w Pascalu może pisać każdy, bo jeśli napisze działający program to da się go później przeczytać i zrozumieć bez większych problemów. Jeśli chodzi o kod w C++ to tutaj doświadczony programista stworzy ładny kod, który potrafi odczytać tylko inny doświadczony programista ;) To piękne :)

  2. moriturius:
    April 24th, 2008 o 22:04

    Właściwie nie odniosłem się jawnie do notki więc daję sprostowanie:

    Implementowanie w ten sposób NULL’a to raczej programistyczna ciekawostka, bo przydatność tego jest raczej mierna ;)

  3. io:
    April 25th, 2008 o 0:40

    Sztuka dla sztuki? :)

  4. Xion:
    April 25th, 2008 o 11:13

    Niekoniecznie. Jeśli na przykład ktoś nieopatrznie przeciąży dwie funkcje dla typu liczbowego wskaźnikowego:

    1. void f(int);
    2. void f(void*);

    to przy zwykłym NULL-u wywołania:

    1. f(0);
    2. f(NULL);

    będą dotyczyły tej samej wersji funkcji – pierwszej, chociaż za drugim razem pewnie chodziło nam o wersję wskaźnikową. Podana wyżej definicja NULL-a, chociaż niedoskonała (jest przynajmniej jedna rzecz, którą można w niej poprawić; można zgłaszać pomysły :)), zabezpieczy przed taką sytuacją. Inna sprawa, że nie powinno się robić takich przeciążeń, ale co nie jest zabronione przez kompilator…

    Zasadniczo problem sprowadza się do tego, że o ile C# ma null, Delphi ma nil, a VB Nothing, to C++ nie ma wbudowanego w język wskaźnika pustego. W C++0x będzie aczkolwiek nullptr.

  5. I:
    April 25th, 2008 o 11:29

    Czyli jednak sztuka dla sztuki…

  6. I:
    April 25th, 2008 o 11:31

    tzn. sztuka pie* o niczym ;p
    pozdr. :)

  7. moriturius:
    April 26th, 2008 o 19:18

    @Xion: z ogromną elastycznością łączy się wiele niebezpieczeństw. Jeśli ktoś zrobi taki błąd to znaczy, że powinien zająć się ogrodnictwem :P

    W C/C++ po prostu trzeba umieć programować ;)

  8. agent_J:
    April 27th, 2008 o 17:17

    @moriturius: a co gdy NULL tak na prawdę nie jest NULLem ? Np. przy wielokrotnym dziedziczeniu.

  9. moriturius:
    April 27th, 2008 o 20:29

    @agent_J: a w jaki sposob NULL moze nie byc NULLem? :P nawet przy wielokrotnym dziedziczeniu?

  10. manfred:
    March 21st, 2009 o 21:04

    @agent_J: przy wielokrotnym dziedziczeniu static_cast sprawdza, czy przypadkiem wskaźnik to nie 0. Jeśli nie, to deltę dodaje. Jak tak, to zwraca niezmieniony.

Comments are disabled.
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.