web-dev-qa-db-fra.com

Quand faut-il lancer un pointeur vide en C?

J'ai regardé Programmation Linux avancée par Mitchell, Oldham et Samuel. J'ai vu dans la section sur pthreads quelque chose sur les pointeurs vides et le casting qui me déroute.

En passant un argument à pthread_create (), ils ne convertissent pas le pointeur en un pointeur vide même si c'est ce que la fonction attend.

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

Ici, which_prime est de type int.

Mais en prenant une valeur renvoyée par le thread en utilisant pthread_join, ils transtendent la variable pour annuler le pointeur.

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

Ici, prime est de nouveau de type int.

Pourquoi le casting est-il effectué en deuxième instance et non en premier?

20
Amoeba

Le deuxième exemple est un bon exemple des raisons pour lesquelles la conversion en void* est généralement une erreur. Ça devrait être

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

car pthread_join prend un void** comme deuxième argument. Le void* s'assure seulement que le bogue passe le compilateur parce que le void* est converti en void** automatiquement.

Ainsi, lorsque do vous devez convertir en void* ou retour:

  • lorsque vous travaillez avec des pointeurs stockés sous forme d'entiers ((u)intptr_t);
  • lors du passage de pointeurs vers des fonctions dont le prototype est incomplet et qui prennent void* (ou prenez un autre type de pointeur et vous avez void*); cela signifie généralement que les fonctions prennent un nombre variable d'arguments tels que printf.
10
Fred Foo

Pas besoin de transtyper depuis ou vers un pointeur vers void en C:

6.3.2.3 Pointeurs

1 Un pointeur vers void peut être converti vers ou depuis un pointeur vers tout type d'objet ou incomplet. Un pointeur vers un type d'objet incomplet ou peut être converti en pointeur vers void et inversement; le résultat doit être égal au pointeur d'origine.


Les seules exceptions à cette règle sont

  • lors de l'impression d'un pointeur à l'aide de "%p" spécificateur de conversion car il n'est défini que pour void *.
  • lors de la copie de la valeur d'un pointeur à partir d'un intptr_t ou uintptr_t retour à un void *.
9
alk

En C, le transtypage vers void * à partir de n'importe quel type de pointeur et vice-versa se fait implicitement. Il n'y a pas besoin de cast dans le deuxième exemple.

(Notez qu'en C++, la conversion de tout pointeur vers void * est également implicite (sauf pour les pointeurs de fonction et les pointeurs de fonction/membre qui ne peuvent pas être convertis en void *), mais la conversion en arrière nécessite une conversion explicite.)

4
Asaf

Selon la documentation,

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

Alors le pthread_join prend un pointer to void* comme deuxième argument. Ceci est dû au fait,

Dans pthread_join, vous récupérez l'adresse transmise à pthread_exit par le thread fini. Si vous passez juste un pointeur simple, il est passé par valeur afin que vous ne puissiez pas changer où il pointe. Pour pouvoir modifier la valeur du pointeur passé à pthread_join, il doit être passé comme pointeur lui-même, c'est-à-dire comme pointeur vers un pointeur.

Maintenant, à votre question, " Pourquoi le cast est-il fait dans la deuxième instance et pas dans la première?" Dans la première instance, c'est-à-dire pthread_create, il attend un void* comme quatrième argument. Donc en passant &which_prime serait implicitement converti en void*.

Dans le deuxième cas, c'est-à-dire pthread_join, il attend un void** et nous passons &prime Là. Ainsi, le compilateur se plaindra. Donc, pour contourner le bogue, l'auteur passe un casting de void* qui sera automatiquement converti en void**.

Mais ce n'est pas une bonne solution.

La solution ::

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
2
Abhineet

Je crois que le même code a été référencé dans d'autres questions .

La réponse dans le deuxième lien explique:

Ce n'est pas valable. Cela fonctionne simplement si sizeof (int) == sizeof (void *), ce qui se produit sur de nombreux systèmes.

Un vide * est uniquement garanti pour pouvoir contenir des pointeurs vers des objets de données.

Voici un C FAQ sur le sujet.

Et le texte cité:

Comment les entiers sont-ils convertis vers et depuis des pointeurs? Puis-je placer temporairement un entier dans un pointeur, ou vice versa?

Les conversions pointeur-entier et entier-pointeur sont définies par l'implémentation (voir question 11.33), et il n'y a plus aucune garantie que les pointeurs peuvent être convertis en entiers et inversement, sans changement

Forcer des pointeurs en entiers, ou des entiers en pointeurs, n'a jamais été une bonne pratique

1
user1508519