[C++] Wann gilt ein Zeiger als dereferenziert?

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

[C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

Hi,

eine Frage, die ich mir immer wieder Stelle, ist: Was genau ist die Dereferenzierung eines Zeigers? Der bloße Aufruf des *ptr-Operators? Oder erst ein Zugriff auf die Adresse, die im Zeiger steht? Genauer:

  void foo(int &) { }

  int * ptr = nullptr;
  foo(*ptr);


Hier wird der Nullzeiger in eine Referenz umgewandelt, die dann aber nicht benutzt wird. Gilt das bereits als Dereferenzierung? Ist der Quelltext standardkonform?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von dot »

Krishty hat geschrieben:Gilt das bereits als Dereferenzierung?
afaik ja
Krishty hat geschrieben:Ist der Quelltext standardkonform?
Ich würde sagen: Undefined Behaviour
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Niki »

http://en.wikipedia.org/wiki/Reference_(C%2B%2B)
in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

Ja; das sind triftige Gründe.

Ich frage, weil ich meine Windows-Handles gern als Referenzen herumreiche, und das alte Lied ist, dass CreateFile() nicht 0 als Fehlerwert zurückgibt, sondern INVALID_HANDLE_VALUE. Meine Überlegung war, dass es ziemlich schmutzig würde, falls mal ein Datei-Handle mit dem Wert 0 zurückkäme.

Ich kann es aber von der anderen Seite lösen: CreateFile() scheint zwar 0 nicht als Fehlerwert zurückzugeben, aber auch nicht als gültiges Handle. Scheinbar war das zuletzt unter Windows 98 der Fall, wenn man COM-Ports geöffnet hat. Ich kann also unter Windows NT annehmen, dass niemals ein gültiges Handle mit dem Wert 0 existiert – das erleichtert Vieles und erledigt auch die Frage.

In jedem Fall danke dafür, dass ihr meine Neugier gestillt habt!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Niki »

Krishty hat geschrieben:Ich kann also unter Windows NT annehmen, dass niemals ein gültiges Handle mit dem Wert 0 existiert – das erleichtert Vieles und erledigt auch die Frage.
Ein wenig riskant ist es ja schon, weil man ja nicht weiß was Windows da intern macht. Aber die Chancen, dass deine hoffentlich harmlose Annahme korrekt ist stehen relativ gut, und zwar wegen CloseHandle(). Wenn du CloseHandle() mit NULL oder INVALID_HANDLE_VALUE aufrufst, so wird GetLastError() dir ERROR_INVALID_HANDLE liefern. Für ein wirklich ungültiges Handle jedoch, schmeißt CloseHandle() eine Exception (kannst du sehen wenn du z.B. mal schnell einen Mutex erzeugst und das Handle zweimal schließt). Dieses Verhalten lässt einen schon vermuten wie der Source-Code von CloseHandle() anfängt... aber es sind halt tatsächlich nur Vermutungen.
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Artificial Mind »

Laut http://msdn.microsoft.com/en-us/library ... s.85).aspx wird folgendes definiert:
A handle to an object.
This type is declared in WinNT.h as follows:

Code: Alles auswählen

typedef PVOID HANDLE;
damit ist ein Handle doch ein void* oder?
Das könnte dann auch implizieren, dass ein HANDLE von 0 nicht gültig ist (sondern nur für spezielle Handles, wie das HWND 0 genutzt wird).

Weitere Indizien dafür finden sich in http://msdn.microsoft.com/de-de/library ... s.90).aspx.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von dot »

Krishty hat geschrieben:Ich kann also unter Windows NT annehmen, dass niemals ein gültiges Handle mit dem Wert 0 existiert
Nein; ob es einen Wert für ungültig gibt und welcher das ist, hängt von der Art des Handle ab...
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

dot hat geschrieben:
Krishty hat geschrieben:Ich kann also unter Windows NT annehmen, dass niemals ein gültiges Handle mit dem Wert 0 existiert
Nein; ob es einen Wert für ungültig gibt und welcher das ist, hängt von der Art des Handle ab...
INVALID_HANDLE_VALUE mag ein ungültiger Wert für CreateFile() sein, aber nicht der Einzige (sondern nur das einzige Signal). CreateFile() wird unter NT niemals 0 zurückgeben, weil alle Handles bei 4 beginnen (die symbolischen bei 3). 0 trat unter Windows 9x auf, aber nicht unter NT. Einige Leute behaupten, dass es in früheren NT-Versionen stdin angesteuert hat (zusätzlich zu GetStdHandle(STDIN)), aber selbst wenn, wird das nie bei einer von mir geöffneten Datei auftreten (selbst wenn ich stdin öffne, bekomme ich ein anderes, nicht-0-Handle zurück).

Direkte Garantien gibt es nicht, aber viele Indirekte:
  • Inside Windows 2000 schreibt
    http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.kernel/2005-05/msg00387.html hat geschrieben:An object handle is an index into a process-specific handle table, pointed to by the executive process (EPROCESS) block (described in Chapter 6). The first handle index is 4, the second 8, and so on.
  • Windows’ Kernel-Debugger kann keine Details zu Handle 0 anzeigen; stattdessen löst es das Anzeigen *aller* Handles aus.
  • Offenbar implementieren die meisten NT-Funktionen 0 als Spezialfall. Das gilt nur für Windows XP, aber auf höheren Versionen hat ebenfalls noch nie jemand ein 0-Handle entdecken können (Process Explorer!):
    http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.kernel/2005-05/msg00444.html hat geschrieben:On any NT-derived platform, 0 (zero) can never be a valid handle value. A zero handle will either result in an error or special behavior. For example, the native ZwCreateProcess() service has a section handle argument, which is always supplied by win32's CreateProcess(). If that handle is zero, ZwCreateProcess() will use the fork() semantics for process creation [which is to copy the address space of the parent]. For another example, ZwTerminateProcess() takes -1, 0 or a valid process handle; and the zero handle will _not_ terminate the process, unlike the other two cases, but again do something very special. This can be repeated for just about any native OS routine that accepts handles; comparison against zero is ubiquitous in the OS code, so if any valid handle somehow had such a value, you would not be able to perform any "regular" operation on this handle.

    The INVALID_HANDLE_VALUE, which is -1, is in fact a valid handle, which means "this process". Of course, ReadFile() will fail with this handle, because the object type is wrong, just like ReadFile() will fail on a perfectly valid handle of a mutex, a thread, etc.

    CloseHandle() does indeed fail when given a bad handle. It will fail on both -1 and 0: -1 cannot be closed, and 0 is just wrong. When CloseHandle() fails, it checks whether a debugger is attached to the process, in which case it raises an exception.
Ich gehe davon aus, dass der INVALID_HANDLE_VALUE-vs.-NULL-Fehler so hinterlistig ist, dass sie diese Möglichkeit mit guten Gründen aus Windows 9x entfernt haben und nicht wieder einführen wollen. Wahrscheinlich ist, dass mit NT das hier geschah:
The Old New Thing — The dialog manager, part 4: The dialog loop hat geschrieben:So many people make this mistake that we had to put this app hack into the core OS. It would be pointless to make a shim for it since that would mean that thousands of apps would need to be shimmed.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Niki »

Hier hast du noch einen Indikator aus dem .NET-Framework, für die sichere Benutzung von Win32 Handles.

http://msdn.microsoft.com/de-de/library ... .110).aspx

Aber selbst die Existenz dieser Klasse (und ihrer Beschreibung) ist keine Garantie. Hast du denn keine anderen Möglichkeiten mit denen du happy wärst?
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

Naja – eben gegen INVALID_HANDLE_VALUE testen, aber nur in den Fällen, wo es auch zurückgegeben wird. Das ist mir zu hässlich und zu unintuitiv. Eine Wrapper-Klasse wäre mir zu viel Code. Ich könnte auch einfach 1 zum Handle addieren wenn ich es rumreiche und 1 subtrahieren bevor ich es benutze – aber das wäre zu viel Overhead.

Eigentlich bin ich happy – CreateFile() wird niemals 0 zurückgegeben, also kann ich diesen Wert als eigene Markierung verwenden, die mit allen anderen Handles und Zeigersemantiken konform ist:

  auto result = CreateFileW(…);
  if(INVALID_HANDLE_VALUE == result) {
    return NULL;
  }
  return result;



  if(NULL != handle) {
    CloseHandle(handle);
  }
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von dot »

Code: Alles auswählen

#pragma once

#include <utility>

#include "platform.h"


namespace Win32
{
  template <typename T, T null_value, void (&close)(T)>
  class handle
  {
  private:
    handle(const handle&);
    handle& operator =(const handle&);

    T h;

  public:
    typedef T handle_type;
    static const T null_value;

    handle(T handle = null_value)
      : h(handle)
    {
    }

    handle(handle&& h)
      : h(h.h)
    {
      h.h = null_value;
    }

    ~handle()
    {
      if (h != null_value)
        close(h);
    }

    operator T() const { return h; }

    handle& operator =(handle&& h)
    {
      swap(*this, h);
      return *this;
    }

    void reset(T handle = null_value)
    {
      if (h != null_value)
        close(h);
      h = handle;
    }

    T release()
    {
      T handle = h;
      h = null_value;
      return handle;
    }

    friend void swap(handle& a, handle& b)
    {
      using std::swap;
      swap(a.h, b.h);
    }
  };

  template <typename T, T null_value, void (&close)(T)>
  const T handle<T, null_value, close>::null_value = null_value;

  inline void closeHandle(HANDLE h)
  {
    CloseHandle(h);
  }
}
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

Siehst du? 70 Zeilen mehr als meins ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
B.G.Michi
Establishment
Beiträge: 163
Registriert: 07.03.2006, 20:38
Alter Benutzername: B.G.Michi
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von B.G.Michi »

Einmal geschrieben (oder STRG-C + STRG+V) für immer aus dem Sinn. Verwendet Handles so wie sie gedacht sind, keine Leistungseinbußen und in jedem fall standardkonform.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

B.G.Michi hat geschrieben:Einmal geschrieben (oder STRG-C + STRG+V) für immer aus dem Sinn.
Wäre das erste Mal ;)
Verwendet Handles so wie sie gedacht sind
Tue ich das nicht?
keine Leistungseinbußen
Bei mir denn?
und in jedem fall standardkonform.
Meins nicht?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von dot »

exception safe :P
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

dot hat geschrieben:exception safe :P
Ausnahmen wirft die WinAPI nicht; darum habe ich da auch keinen Bedarf :-)

Wie sieht es mit symbolischen Handles aus? Die darf man nicht schließen (The Old New Thing – What are the conventions for managing standard handles?); ansonsten verhalten sie sich aber wie jedes andere Datei-Handle auch …

Und da fällt mir ein versteckter Hinweis zur Null-Duldung auf:
If the value is INVALID_HANDLE_VALUE, then there is no active file. (You might also have decided to use NULL as your special value, but INVALID_HANDLE_VALUE works better here because that is the conventional sentinel value for file handles.)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Niki »

Krishty hat geschrieben:Ausnahmen wirft die WinAPI nicht; darum habe ich da auch keinen Bedarf :-)
Nicht ganz korrekt. CloseHandle() kann beispielsweise Exceptions werfen. In diesem speziellen Fall aber nur wenn ein Debugger attached ist.
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

Das sind aber strukturierte Ausnahmen und keine C++-Ausnahmen; Destruktoren würden dabei sowieso nicht aufgerufen werden (von ein paar sehr alten Compiler-Einstellungen, wo catch(...) auch auf SEH reagiert, abgesehen). Halt genau wie keine D’toren ablaufen falls du eine Zugriffsverletzung oder eine Division durch Null auslöst.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Niki »

Krishty hat geschrieben:Das sind aber strukturierte Ausnahmen und keine C++-Ausnahmen;
Ja, da hast du recht.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von dot »

Krishty hat geschrieben:
dot hat geschrieben:exception safe :P
Ausnahmen wirft die WinAPI nicht; darum habe ich da auch keinen Bedarf :-)
Aber das, was du mit "..." symbolisiert hast wirft möglicherweise, oder returned... :P
Benutzeravatar
Krishty
Establishment
Beiträge: 8250
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Wann gilt ein Zeiger als dereferenziert?

Beitrag von Krishty »

Bei mir nicht ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten