Seite 1 von 1

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

Verfasst: 25.01.2014, 14:54
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?

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

Verfasst: 25.01.2014, 15:57
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

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

Verfasst: 25.01.2014, 16:20
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.

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

Verfasst: 25.01.2014, 17:02
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!

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

Verfasst: 25.01.2014, 17:45
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.

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

Verfasst: 25.01.2014, 18:07
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.

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

Verfasst: 25.01.2014, 18:09
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...

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

Verfasst: 25.01.2014, 18:28
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.

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

Verfasst: 25.01.2014, 20:00
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?

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

Verfasst: 25.01.2014, 20:10
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);
  }

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

Verfasst: 25.01.2014, 20:16
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);
  }
}

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

Verfasst: 25.01.2014, 20:19
von Krishty
Siehst du? 70 Zeilen mehr als meins ;)

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

Verfasst: 25.01.2014, 22:56
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.

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

Verfasst: 26.01.2014, 06:55
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?

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

Verfasst: 26.01.2014, 09:35
von dot
exception safe :P

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

Verfasst: 26.01.2014, 09:49
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.)

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

Verfasst: 26.01.2014, 10:40
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.

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

Verfasst: 26.01.2014, 10:41
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.

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

Verfasst: 26.01.2014, 10:46
von Niki
Krishty hat geschrieben:Das sind aber strukturierte Ausnahmen und keine C++-Ausnahmen;
Ja, da hast du recht.

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

Verfasst: 26.01.2014, 11:52
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

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

Verfasst: 26.01.2014, 11:55
von Krishty
Bei mir nicht ;)