web-dev-qa-db-de.com

Ist es eine gute Idee, Zeiger mit dem Zeigefinger zu tippen?

Ich habe etwas Code durchgesehen und festgestellt, dass die Konvention darin bestand, Zeigertypen zu drehen

SomeStruct* 

in

typedef SomeStruct* pSomeStruct;

Gibt es einen Verdienst dafür?

66
Unknown

Dies kann sinnvoll sein, wenn der Zeiger selbst als "Black Box" betrachtet werden kann, dh als Datenelement, dessen interne Darstellung für den Code irrelevant sein sollte.

Wenn der Code den Zeiger mit never dereferenziert und Sie ihn einfach um API-Funktionen übergeben (manchmal durch Referenz), dann reduziert der Typedef nicht nur die Anzahl der *s in Ihrem Code, sondern schlägt dem Programmierer auch vor dass der Zeiger nicht wirklich mit eingemischt werden sollte.

Dies macht es auch einfacher, die API in der Zukunft zu ändern, wenn dies erforderlich ist. Wenn Sie beispielsweise zur Verwendung einer ID anstelle eines Zeigers wechseln (oder umgekehrt), wird vorhandener Code nicht beschädigt, da der Zeiger nie zuvor dereferenziert werden sollte.

94
Artelius

Meiner Erfahrung nach nicht. Das Ausblenden des '*' erschwert das Lesen des Codes.

67
sigjuice

Das einzige Mal, dass ich einen Zeiger innerhalb des typedef verwende, ist der Umgang mit Zeigern auf Funktionen:

typedef void (*SigCatcher(int, void (*)(int)))(int);
typedef void (*SigCatcher)(int);

SigCatcher old = signal(SIGINT, SIG_IGN);

Ansonsten finde ich sie eher verwirrend als hilfreich.


Die durchgestrichene Deklaration ist der richtige Typ für einen Zeiger auf die Funktion signal(), nicht auf den Signalfänger. Es könnte klarer gemacht werden (unter Verwendung des oben korrigierten Typs SigCatcher), indem Folgendes geschrieben wird:

 typedef SigCatcher (*SignalFunction)(int, SigCatcher);

Oder deklarieren Sie die Funktion signal():

 extern SigCatcher signal(int, SigCatcher);

Das heißt, ein SignalFunction ist ein Zeiger auf eine Funktion, die zwei Argumente akzeptiert (ein int und ein SigCatcher) und ein SigCatcher zurückgibt. Und signal() selbst ist eine Funktion, die zwei Argumente akzeptiert (ein int und ein SigCatcher) und ein SigCatcher zurückgibt.

28

Dies kann Ihnen helfen, einige Fehler zu vermeiden. Zum Beispiel im folgenden Code:

int* pointer1, pointer2;

pointer2 ist kein int * , es ist einfach int . Bei Typedefs wird dies jedoch nicht passieren:

typedef int* pInt;
pInt pointer1, pointer2;

Sie sind beide int * now.

16
Sad Developer

Meine Antwort ist ein klares "Nein".

Warum?

Zunächst tauschen Sie einfach ein einzelnes Zeichen * Gegen ein anderes einzelnes Zeichen p aus. Das ist Null Gewinn. Dies allein sollte Sie davon abhalten, da es immer schlecht ist, zusätzliche Dinge zu tun, die sinnlos sind.

Zweitens, und das ist der wichtige Grund, der * Hat eine Bedeutung, die nicht gut zu verbergen ist . Wenn ich so etwas an eine Funktion übergebe

void foo(SomeType bar);

void baz() {
    SomeType myBar = getSomeType();
    foo(myBar);
}

Ich erwarte nicht, dass die Bedeutung von myBar durch Übergabe an foo() geändert wird. Schließlich übergebe ich einen Wert, sodass foo() immer nur eine Kopie von myBar sieht, oder? Nicht, wenn SomeType eine Art Zeiger bedeutet!

Dies gilt sowohl für C-Zeiger als auch für C++ - Smart-Zeiger: Wenn Sie die Tatsache verbergen, dass es sich um Zeiger auf Ihre Benutzer handelt, entsteht eine völlig unnötige Verwirrung. Also, bitte, verfälschen Sie nicht Ihre Zeiger.

(Ich glaube, die Gewohnheit, Zeigertypen zu tippen, ist nur ein irrtümlicher Versuch, zu verbergen, wie viele Sterne man als Programmierer hat http://wiki.c2.com/?ThreeStarProgrammer .)

12
cmaster

Es hängt (wie so viele Antworten) davon ab.

In C ist dies sehr üblich, wenn Sie versuchen zu verschleiern, dass ein Objekt ein Zeiger ist. Sie versuchen zu implizieren, dass dies das Objekt ist, das alle Ihre Funktionen manipulieren (wir wissen, dass sich darunter ein Zeiger befindet, der jedoch das Objekt darstellt, das Sie bearbeiten).

MYDB   db = MYDBcreateDB("Plop://djdjdjjdjd");

MYDBDoSomthingWithDB(db,5,6,7);
CallLocalFuc(db); // if db is not a pointer things could be complicated.
MYDBdestroyDB(db);

Unter MYDB befindet sich wahrscheinlich ein Zeiger auf ein Objekt.

In C++ ist dies nicht mehr erforderlich.
Hauptsächlich deshalb, weil wir Dinge als Referenz übergeben können und die Methoden in die Klassendeklaration integriert werden.

MyDB   db("Plop://djdjdjjdjd");

db.DoSomthingWithDB(5,6,7);
CallLocalFuc(db);   // This time we can call be reference.
db.destroyDB();     // Or let the destructor handle it.
5
Martin York

Das ist eine Frage des Stils. Sie sehen diese Art von Code sehr häufig in den Windows-Headerdateien. Sie neigen jedoch dazu, die gesamte Großbuchstaben-Version zu bevorzugen, anstatt mit einem Kleinbuchstaben p vorzugehen. 

Ich persönlich vermeide diese Verwendung von Typedef. Es ist viel klarer, wenn der Benutzer explizit sagt, dass er ein Foo * als PFoo will. Typedefs sind heutzutage am besten geeignet, um STL lesbar zu machen :)

typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
5
JaredPar

Die Diskussion wurde mit der Annahme geführt, dass die Sprache von Interesse C ist. Die Auswirkungen auf C++ wurden nicht berücksichtigt.

Verwenden eines Zeigertyps für eine Struktur ohne Tags

Die Frage Größe einer als Zeiger definierten Struktur wirft ein interessantes Licht auf die Verwendung von typedef für (Struktur-) Zeiger.

Betrachten Sie die Definition des Strukturtyps tagless concrete (nicht opak):

typedef struct { int field1; double field2; } *Information;

Die Einzelheiten der Mitglieder sind für diese Diskussion völlig tangential. Alles, was zählt, ist, dass dies kein undurchsichtiger Typ wie typedef struct tag *tag; ist (und Sie können solche undurchsichtigen Typen nicht über ein typedef ohne ein Tag definieren).

Die Frage ist, wie man die Größe dieser Struktur findet.

Die kurze Antwort lautet "nur über eine Variable des Typs". Es gibt kein Tag, das mit sizeof(struct tag) verwendet werden kann. Sie können nicht sinnvoll schreiben sizeof(*Information)Beispiel: und sizeof(Information *) ist die Größe eines Zeigers auf den Zeigertyp und nicht die Größe des Strukturtyps.

Wenn Sie eine solche Struktur zuweisen möchten, können Sie sie nur über die dynamische Zuweisung (oder Ersatztechniken, die die dynamische Zuweisung imitieren) erstellen. Es gibt keine Möglichkeit, eine lokale Variable des Strukturtyps zu erstellen, deren Zeiger Information heißen, und es gibt keine Möglichkeit, eine Dateibereichsvariable (global oder static) des Strukturtyps zu erstellen. Es gibt auch keine Möglichkeit, eine solche Struktur (im Gegensatz zu einem Zeiger auf eine solche Struktur) in eine andere Struktur oder einen anderen Vereinigungstyp einzubetten.

Sie können - müssen - schreiben:

Information info = malloc(sizeof(*info));

Abgesehen von der Tatsache, dass der Zeiger in typedef verborgen ist, ist dies eine gute Praxis - wenn sich der Typ von info ändert, bleibt die Größenzuweisung korrekt. In diesem Fall ist es jedoch auch die einzige Möglichkeit, die Größe der Struktur zu ermitteln und die Struktur zuzuweisen. Und es gibt keine andere Möglichkeit, eine Instanz der Struktur zu erstellen.

Ist das schädlich?

Es hängt von Ihren Zielen ab.

Dies ist kein undurchsichtiger Typ - die Details der Struktur müssen definiert werden, wenn der Zeigertyp typedef 'd ist.

Dieser Typ kann nur mit dynamischer Speicherzuweisung verwendet werden.

Es ist ein Typ, der namenlos ist. Der Zeiger auf den Strukturtyp hat einen Namen, der Strukturtyp selbst jedoch nicht.

Wenn Sie die dynamische Zuordnung erzwingen möchten, ist dies anscheinend eine Möglichkeit, dies zu tun.

Insgesamt ist es jedoch wahrscheinlicher, dass es Verwirrung und Angst hervorruft als Erleuchtung.

Zusammenfassung

Es ist im Allgemeinen eine schlechte Idee, typedef zu verwenden, um einen Zeiger auf einen taglosen Strukturtyp zu definieren.

4

Typedef wird verwendet, um Code lesbarer zu machen. Wenn Sie jedoch den Zeiger als Typedef verwenden, erhöht sich die Verwirrung. Vermeiden Sie besser Typedef-Zeiger.

4
Chand

Wenn Sie dies tun, können Sie keine STL-Container von const pSomeStruct erstellen, da der Compiler Folgendes liest:

list<const pSomeStruct> structs;

wie

list<SomeStruct * const> structs;

dies ist kein zulässiger STL-Container, da die Elemente nicht zuweisbar sind.

Siehe diese Frage .

3
Dan Hook

Win32-API macht dies mit fast jeder Struktur (wenn nicht allen)

POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2

Es ist schön, wie es konsistent ist, aber meiner Meinung nach fügt es keine Eleganz hinzu.

2
dreamlax

Vor einiger Zeit hätte ich diese Frage mit "Nein" beantwortet. Mit dem Aufkommen von intelligenten Zeigern werden Zeiger nicht mehr immer mit einem Stern '*' definiert. Es ist also nichts offensichtlich, dass ein Typ ein Zeiger ist oder nicht.

Nun würde ich sagen: Es ist gut für Zeiger mit Typedef, solange deutlich gemacht wird, dass es sich um einen Zeigertyp handelt. Das heißt, Sie müssen dafür ein Präfix/Suffix verwenden. Nein, "p" ist zum Beispiel kein ausreichendes Präfix. Ich würde wahrscheinlich mit "ptr" gehen.

1
Benoît

Der Zweck von typedef besteht darin, die Implementierungsdetails auszublenden, aber die Zeiger-Eigenschaft wird durch das Eingeben der Zeiger-Eigenschaft zu sehr verborgen und macht es schwieriger, den Code zu lesen/zu verstehen.


Wenn Sie Implementierungsdetails ausblenden möchten (was häufig sinnvoll ist), verbergen Sie den Zeigerteil nicht. Nehmen Sie zum Beispiel den Prototyp für die Standardschnittstelle FILE:

FILE *fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, FILE *stream);

hier gibt fopen ein pointer an eine Struktur FILE zurück (für die Sie die Implementierungsdetails nicht kennen). Vielleicht ist FILE kein so gutes Beispiel, denn in diesem Fall hätte es mit einem pFILE-Typ funktionieren können, der die Tatsache verdeckte, dass es sich um einen Zeiger handelt.

pFILE fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, pFILE stream);

Das funktioniert jedoch nur, weil Sie sich nie mit dem Inhalt herumschlagen, auf den direkt verwiesen wird. In dem Moment, in dem Sie einen Zeiger eingeben, an dem Sie den Code an einigen Stellen ändern, wird es nach meiner Erfahrung sehr schwer zu lesen.

0
hlovdal

Nein.

In dem Moment, in dem Sie es mit const mischen, wird Ihr Leben unglücklich.

typedef foo *fooptr;
const fooptr bar1;
const foo *bar2

Sind bar1 und bar2 vom gleichen Typ?

Und ja, ich zitiere nur Herb Sutters Guru. Viel Wahrheit hat sie gesagt. ;)

- Bearbeiten -

Link zu zitiertem Artikel hinzufügen.

http://www.drdobbs.com/conversationsa-midsummernights-madness/184403835

0
dgnuff