web-dev-qa-db-fra.com

Concept de pointeur vide dans la programmation en C

Est-il possible de déréférencer un pointeur vide sans transtypage dans le langage de programmation C?

Aussi, y a-t-il un moyen de généraliser une fonction qui peut recevoir un pointeur et le stocker dans un pointeur vide et en utilisant ce pointeur vide, pouvons-nous créer une fonction généralisée?

pour exemple:

void abc(void *a, int b)
{
   if(b==1)
      printf("%d",*(int*)a);     // If integer pointer is received
   else if(b==2)
      printf("%c",*(char*)a);     // If character pointer is received
   else if(b==3)
      printf("%f",*(float*)a);     // If float pointer is received
}

Je veux rendre cette fonction générique sans utiliser les instructions if-else - est-ce possible?

De même, s'il existe de bons articles sur Internet expliquant le concept d'un pointeur vide, il serait alors bénéfique de fournir les URL.

En outre, l'arithmétique de pointeur avec des pointeurs vides est-elle possible?

125
AGeek

Est-il possible de déréférencer le pointeur vide sans transtypage en langage de programmation C ...

Non, void indique l'absence de type, ce n'est pas quelque chose que vous pouvez déréférencer ou affecter.

existe-t-il un moyen de généraliser une fonction qui peut recevoir un pointeur et le stocker dans un pointeur vide et en utilisant ce pointeur vide nous pouvons créer une fonction généralisée ..

Vous ne pouvez pas simplement le déréférencer de manière portable, car il peut ne pas être correctement aligné. Cela peut poser problème sur certaines architectures comme ARM, où le pointeur sur un type de données doit être aligné sur la limite de la taille du type de données (par exemple, un pointeur sur un entier 32 bits doit être aligné sur une limite de 4 octets pour pouvoir être déréférencé).

Par exemple, en lisant uint16_t de void*:

/* may receive wrong value if ptr is not 2-byte aligned */
uint16_t value = *(uint16_t*)ptr;
/* portable way of reading a little-endian value */
uint16_t value = *(uint8_t*)ptr
                | ((*((uint8_t*)ptr+1))<<8);

De plus, l'arithmétique de pointeur est-elle possible avec des pointeurs vides ...

L'arithmétique de pointeur n'est pas possible sur les pointeurs de void en raison de l'absence de valeur concrète sous le pointeur et donc de sa taille.

void* p = ...
void *p2 = p + 1; /* what exactly is the size of void?? */
95
Alex B

En C, un void * Peut être converti en un pointeur sur un objet de type différent sans transtypage explicite:

void abc(void *a, int b)
{
    int *test = a;
    /* ... */

Cela n'aide cependant pas à écrire votre fonction de manière plus générique.

Vous ne pouvez pas déréférencer un void * En le convertissant en un type de pointeur différent, car le déréférencement d'un pointeur permet d'obtenir la valeur de l'objet pointé. Un nu void n'est pas un type valide, il est donc impossible de déréférencer un void *.

L'arithmétique de pointeur consiste à modifier les valeurs de pointeur par des multiples des objets sizeof pointés. Encore une fois, parce que void n'est pas un type vrai, sizeof(void) n'a aucune signification, l'arithmétique de pointeur n'est donc pas valide pour void *. (Certaines implémentations le permettent, en utilisant l'arithmétique de pointeur équivalente pour char *.)

29
CB Bailey

Vous devez savoir que dans C, contrairement à Java ou C #, il n’est absolument pas possible de "deviner" avec succès le type d’objet pointé par un pointeur vide *. Quelque chose de similaire à "getClass ()" "n'existe tout simplement pas, puisque ces informations sont introuvables. Pour cette raison, le type de" générique "que vous recherchez est toujours accompagné de métainformations explicites, comme le" int b "dans votre exemple ou la chaîne de format dans la famille de fonctions printf.

15

Un pointeur vide est appelé pointeur générique, qui peut faire référence à des variables de tout type de données.

6
Sneha

Jusqu'ici, mon indicateur de sous-estimation sur le vide est le suivant.

Lorsqu'une variable de pointeur est déclarée à l'aide du mot clé void, elle devient une variable de pointeur à usage général. L'adresse de toute variable de tout type de données (char, int, float, etc.) peut être assignée à une variable de pointeur vide.

main()
{
    int *p;

    void *vp;

    vp=p;
} 

Étant donné que d'autres types de données peuvent être affectés à un pointeur vide, je l'ai donc utilisé dans la fonction absolut_value (code ci-dessous). Faire une fonction générale.

J'ai essayé d'écrire un code C simple qui prend un argument entier ou flottant et tente de le rendre + ve, s'il est négatif. J'ai écrit le code suivant,

#include<stdio.h>

void absolute_value ( void *j) // works if used float, obviously it must work but thats not my interest here.
{
    if ( *j < 0 )
        *j = *j * (-1);

}

int main()
{
    int i = 40;
    float f = -40;
    printf("print intiger i = %d \n",i);
    printf("print float f = %f \n",f);
    absolute_value(&i);
    absolute_value(&f);
    printf("print intiger i = %d \n",i);
    printf("print float f = %f \n",f);
    return 0;
}   

Mais je commençais à avoir une erreur, alors j'ai compris que ma compréhension du pointeur vide n'était pas correcte :(. Alors, je vais maintenant chercher à collecter des points, pourquoi?.

Les choses que j'ai besoin de mieux comprendre sur les pointeurs vides, c'est ça.

Nous devons dactylographier la variable de pointeur vide pour la déréférencer. En effet, aucun type de données n'est associé à un pointeur vide. Il est impossible que le compilateur sache (ou devine?) Le type de données pointé par le pointeur vide. Donc, pour prendre les données pointées par un pointeur vide, nous les transtypons avec le type correct des données conservées à l’emplacement des pointeurs vides.

void main()

{

    int a=10;

    float b=35.75;

    void *ptr; // Declaring a void pointer

    ptr=&a; // Assigning address of integer to void pointer.

    printf("The value of integer variable is= %d",*( (int*) ptr) );// (int*)ptr - is used for type casting. Where as *((int*)ptr) dereferences the typecasted void pointer variable.

    ptr=&b; // Assigning address of float to void pointer.

    printf("The value of float variable is= %f",*( (float*) ptr) );

}

Un pointeur vide peut être vraiment utile si le programmeur n’est pas sûr du type de données saisies par l’utilisateur final. Dans un tel cas, le programmeur peut utiliser un pointeur vide pour pointer vers l'emplacement du type de données inconnu. Le programme peut être configuré de manière à demander à l'utilisateur d'informer le type de données et le transtypage peut être effectué en fonction des informations saisies par l'utilisateur. Un extrait de code est donné ci-dessous.

void funct(void *a, int z)
{
    if(z==1)
    printf("%d",*(int*)a); // If user inputs 1, then he means the data is an integer and type casting is done accordingly.
    else if(z==2)
    printf("%c",*(char*)a); // Typecasting for character pointer.
    else if(z==3)
    printf("%f",*(float*)a); // Typecasting for float pointer
}

Un autre point important que vous devez garder à l'esprit à propos des pointeurs vides est que - l'arithmétique de pointeur ne peut pas être effectuée dans un pointeur vide.

void *ptr;

int a;

ptr=&a;

ptr++; // This statement is invalid and will result in an error because 'ptr' is a void pointer variable.

Alors maintenant, j'ai compris quelle était mon erreur. Je corrige la même chose.

Références :

http://www.antoarts.com/void-pointers-in-c/

http://www.circuitstoday.com/void-pointers-in-c .

Le nouveau code est comme indiqué ci-dessous.


#include<stdio.h>
#define INT 1
#define FLOAT 2

void absolute_value ( void *j, int *n)
{
    if ( *n == INT) {
        if ( *((int*)j) < 0 )
            *((int*)j) = *((int*)j) * (-1);
    }
    if ( *n == FLOAT ) {
        if ( *((float*)j) < 0 )
            *((float*)j) = *((float*)j) * (-1);
    }
}


int main()
{
    int i = 0,n=0;
    float f = 0;
    printf("Press 1 to enter integer or 2 got float then enter the value to get absolute value\n");
    scanf("%d",&n);
    printf("\n");
    if( n == 1) {
        scanf("%d",&i);
        printf("value entered before absolute function exec = %d \n",i);
        absolute_value(&i,&n);
        printf("value entered after absolute function exec = %d \n",i);
    }
    if( n == 2) {
        scanf("%f",&f);
        printf("value entered before absolute function exec = %f \n",f);
        absolute_value(&f,&n);
        printf("value entered after absolute function exec = %f \n",f);
    }
    else
    printf("unknown entry try again\n");
    return 0;
}   

Merci,

6
Megharaj

Non ce n'est pas possible. Quel type doit avoir la valeur déréférencée?

2
zvrba
void abc(void *a, int b) {
  char *format[] = {"%d", "%c", "%f"};
  printf(format[b-1], a);
}
2
Asdf

Je veux rendre cette fonction générique, sans utiliser ifs; c'est possible?

La seule façon simple que je vois est d’utiliser la surcharge .. qui n’est pas disponible dans le langage de programmation C AFAIK.

Avez-vous envisagé le langage de programmation C++ pour votre programme? Ou y a-t-il une contrainte qui en interdit l'utilisation?

1
yves Baumes

C étant un langage fortement typé de type statique, vous devez choisir le type de variable avant la compilation. Lorsque vous essayez d'émuler des génériques en C, vous finissez par essayer de réécrire C++ à nouveau. Il serait donc préférable d'utiliser C++ à la place.

0
adem

Vous pouvez facilement imprimer une imprimante vide

int p=15;
void *q;
q=&p;
printf("%d",*((int*)q));
0
Ankit Bansal

Voici un bref pointeur sur void pointeurs: https://www.learncpp.com/cpp-tutorial/613-void-pointers/

6.13 - Void pointeurs

Parce que le pointeur vide ne sait pas quel type d'objet il pointe, il ne peut pas être déréférencé directement! Au lieu de cela, le pointeur vide doit d’abord être explicitement converti en un autre type de pointeur avant d’être déréférencé.

Si un pointeur vide ne sait pas sur quoi il pointe, comment savons-nous à quoi le lancer? En fin de compte, c'est à vous de suivre.

Divers pointeur pointeur

Il n'est pas possible de faire de l'arithmétique de pointeur sur un pointeur vide. En effet, l'arithmétique de pointeur nécessite que le pointeur sache à quel objet de taille il pointe, de sorte qu'il puisse incrémenter ou décrémenter le pointeur de manière appropriée.

En supposant que la mémoire de la machine soit adressable sur des octets et ne nécessite pas d'accès alignés, la manière la plus générique et atomique (la plus proche de la représentation au niveau de la machine) d'interpréter un void* est comme un pointeur sur un octet, uint8_t*. Lancer un void* à un uint8_t* vous permettrait, par exemple, d’imprimer les premiers octets 1/2/4/8/quelle que soit votre volonté, à partir de cette adresse, mais vous ne pouvez pas faire grand chose d’autre.

uint8_t* byte_p = (uint8_t*)p;
for (uint8_t* i = byte_p; i < byte_p + 8; i++) {
  printf("%x ",*i);
}
0
Winfield Chen

Vous ne pouvez pas déréférencer un pointeur sans spécifier son type, car différents types de données auront des tailles de mémoire différentes, c’est-à-dire qu’un entier correspond à 4 octets et un caractère à 1 octet.

0
gotoat

le pointeur void est un pointeur générique. L'adresse de tout type de données de toute variable peut être assignée à un pointeur void.

int a = 10;
float b = 3.14;
void *ptr;
ptr = &a;
printf( "data is %d " , *((int *)ptr)); 
//(int *)ptr used for typecasting dereferencing as int
ptr = &b;
printf( "data is %f " , *((float *)ptr));
//(float *)ptr used for typecasting dereferencing as float
0
N.Annamalai Raj