Wann wird ein Casting-Void-Zeiger in C benötigt?

Lesezeit: 5 Minuten

Benutzer-Avatar
Amöbe

Ich habe mir angesehen Fortgeschrittene Linux-Programmierung von Mitchell, Oldham und Samuel. Ich habe im Abschnitt über pthreads etwas über void-Zeiger und Casting gesehen, das mich verwirrt.

Wenn sie ein Argument an pthread_create() übergeben, wandeln sie den Zeiger nicht in einen void-Zeiger um, obwohl die Funktion dies erwartet.

pthread_create( &thread, NULL, &compute_prime, &which_prime );

Hier, which_prime ist vom Typ int.

Aber wenn sie einen Wert nehmen, der vom Thread mit pthread_join zurückgegeben wird, wandeln sie die Variable in einen void-Zeiger um.

pthread_join( thread, (void*) &prime );

Hier, prim ist vom Typ int wieder.

Warum wird in zweiter Instanz gegossen und nicht in erster Instanz?

  • pthread_joinDas zweite Argument von ist eine Leere**. Dieser Code sieht falsch aus.

    – Johannes Zwinck

    9. Dezember 2013 um 11:54 Uhr

  • Betrachtet man den vollständigen Code (advancedlinuxprogramming.com/alp-folder/alp-ch04-threads.pdf) zeigt, dass das Beispiel die void* von der Thread-Funktion zurückgegeben und von empfangen pthread_join() wie int. Dies ist die relevante Zeile von compute_prime(): int candidate; ... return (void*) candidate; Also verwenden &prime als 2. argument zu int prime; ... pthread_join(..., &prime) macht absolut Sinn. Aber casr es zu void* ist einfach falsch. Wenn ein Gips angelegt wird void** gültig gewesen wäre, wie durch die Erklärung von pthread_join(pthread_t, void **).

    – alk

    4. Januar 2014 um 13:53 Uhr


Benutzer-Avatar
alk

Es ist nicht erforderlich, von oder zu einem Zeiger auf umzuwandeln void in C:

6.3.2.3 Zeiger

1 Ein Zeiger auf Leere kann in oder von einem Zeiger auf einen beliebigen unvollständigen oder Objekttyp konvertiert werden. Ein Zeiger auf einen beliebigen unvollständigen oder Objekttyp kann in einen Zeiger auf umgewandelt werden Leere
und wieder zurück; das Ergebnis soll mit dem ursprünglichen Zeiger verglichen werden.


Die einzigen Ausnahmen hiervon sind

  • beim Drucken eines Zeigers mit der "%p" Konvertierungsbezeichner, da er nur für definiert ist void *.
  • beim Kopieren des Werts eines Zeigers aus einer intptr_t oder uintptr_t zurück zu a void *.

  • “beim Drucken eines Zeigers mit dem Konvertierungsbezeichner “%p”, da er nur für void * definiert ist” – Ich drucke oft nicht void* Zeiger verwenden %p und es scheint zu funktionieren. Ist es einfach nicht tragbar? Soll ich die Zeiger auf werfen void*?

    – Aviv Cohn

    24. November 2019 um 12:40 Uhr

  • @AvivCohn: “Soll ich die Zeiger werfen” Um kompatibel zu bleiben, ja, das sollten Sie, sonst wird ein undefiniertes Verhalten aufgerufen, das dazu führen kann, dass (scheinbar) funktioniert oder abstürzt oder ….

    – alk

    24. November 2019 um 13:14 Uhr


Benutzer-Avatar
Fred Fu

Das zweite Beispiel ist ein gutes Beispiel dafür, warum Casting auf void* ist meistens ein fehler. Es sollte sein

void *primep = ′  // no cast needed
pthread_join(thread, &primep);

Weil pthread_join nimmt ein void** als zweites Argument. Das void* stellt nur sicher, dass der Fehler den Compiler passiert, weil die void* umgewandelt wird void** automatisch.

Also, wann tun Sie müssen zu werfen void* oder zurück:

  • beim Arbeiten mit Zeigern, die als ganze Zahlen gespeichert sind ((u)intptr_t);
  • beim Übergeben von Zeigern auf Funktionen, die einen unvollständigen Prototyp und einen unvollständigen Take haben void* (Oder nehmen Sie einen anderen Zeigertyp und Sie haben void*); das bedeutet normalerweise Funktionen, die eine variable Anzahl von Argumenten annehmen, wie z printf.

  • Das void** Parameter von pthread_join ist ein Out-Parameter. Es macht keinen Sinn, primep zu initialisieren. Sein Wert wird ignoriert und überschrieben.

    – n. 1.8e9-wo-ist-meine-Aktie m.

    23. September 2018 um 6:15 Uhr

In C, Umwandlung in void* von jedem Zeigertyp und umgekehrt erfolgt implizit. Die Besetzung im zweiten Beispiel ist nicht erforderlich.

(Beachten Sie, dass beim C++-Casting jeder Zeiger auf void* ebenfalls implizit erfolgt (mit Ausnahme von Funktionszeigern und Funktionsmember-/Methodenzeigern, die nicht auf void* gecastet werden können), aber das Casting erfordert ein explizites Casting.)

Benutzer-Avatar
Abhinet

Gemäß der Dokumentation,

int pthread_join(pthread_t thread, void **retval);

Also, die pthread_join nimmt ein pointer to void* als zweites Argument. Das ist weil,

In pthread_join erhalten Sie die Adresse zurück, die der fertige Thread an pthread_exit übergeben hat. Wenn Sie nur einen einfachen Zeiger übergeben, wird er als Wert übergeben, sodass Sie nicht ändern können, wohin er zeigt. Um den Wert des an pthread_join übergebenen Zeigers ändern zu können, muss dieser selbst als Zeiger übergeben werden, also als Zeiger auf einen Zeiger.

Nun zu deiner Frage: „Warum wird in zweiter Instanz gegossen und nicht in erster Instanz?„In erster Linie, dh pthread_createes erwartet a void* als viertes Argument. Also vorbei &which_prime würde implizit konvertiert werden void*.

Im zweiten Fall, dh pthread_joines erwartet a void** und wir gehen vorbei &prime dort. Der Compiler wird sich also beschweren. Um den Fehler zu umgehen, übergibt der Autor also eine Besetzung von void* die automatisch konvertiert werden void**.

Aber das ist keine gute Lösung.

Die Lösung::

void* prime ; // make prime as void*
pthread_join( thread, &prime );
printf( "%" PRIxPTR "\n", (intptr_t)prime ) ; 
// intptr_t instead of int to get an integer type 
// that's the same size as a pointer

Ich glaube, auf den gleichen Code wurde in anderen Fragen verwiesen.

Die Antwort im zweiten Link erklärt:

Es ist nicht gültig. Es funktioniert einfach, wenn sizeof(int) == sizeof(void *), was auf vielen Systemen vorkommt.

Ein void * kann nur garantiert Zeiger auf Datenobjekte enthalten.

Hier ist eine C-FAQ zu diesem Thema.

Und der zitierte Text:

Wie werden Ganzzahlen in und von Zeigern konvertiert? Kann ich vorübergehend eine Ganzzahl in einen Zeiger stecken oder umgekehrt?

Zeiger-zu-Ganzzahl- und Ganzzahl-zu-Zeiger-Konvertierungen sind implementierungsdefiniert (siehe Frage 11.33), und es gibt keine Garantie mehr, dass Zeiger ohne Änderung in Ganzzahlen und zurück konvertiert werden können

Zeiger in ganze Zahlen oder ganze Zahlen in Zeiger zu zwingen, war noch nie eine gute Praxis

  • In C99 können Zeiger sicher konvertiert werden intptr_t/uintptr_t und zurück.

    – Fred Foo

    9. Dezember 2013 um 12:30 Uhr

  • @FredFoo falls vorhanden. Sie sind im Standard optional

    – osven

    4. April 2018 um 19:33 Uhr

  • In C99 können Zeiger sicher konvertiert werden intptr_t/uintptr_t und zurück.

    – Fred Foo

    9. Dezember 2013 um 12:30 Uhr

  • @FredFoo falls vorhanden. Sie sind im Standard optional

    – osven

    4. April 2018 um 19:33 Uhr

1367630cookie-checkWann wird ein Casting-Void-Zeiger in C benötigt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy