web-dev-qa-db-fra.com

Question C: (const void *) vs (void *)

Quelle est la différence entre const void * et void *? Dans quelles circonstances un pointeur vide peut-il être converti en const void pointeur?

31
snkherv

Un const void * Pointe vers une mémoire qui ne doit pas être modifiée.

Un void * (Non-const) pointe vers une mémoire qui pourrait être modifiée (mais pas via le void *; Vous devez d'abord le caster).

Lorsque vous utilisez memmove(), l'adresse source est convertie en const void *:

void *memmove(void *dst, const void *src, size_t nbytes);

C'est une illustration quand un pointeur vide peut être converti en un pointeur vide constant. Fondamentalement, vous pouvez le faire (convertir en constante) à tout moment lorsque vous savez que vous n'allez pas modifier la mémoire vers laquelle le pointeur pointe. Cela s'applique à tout pointeur - pas seulement aux pointeurs vides.

La conversion dans l'autre sens (d'un pointeur constant à un pointeur non constant) est un exercice beaucoup plus dangereux. Il n'y a aucune garantie que la mémoire pointée soit réellement modifiable; par exemple, un littéral de chaîne peut être stocké en mémoire morte (constante), et si vous perdez la constance avec un transtypage et essayez de modifier la chaîne, vous obtiendrez probablement une erreur de segmentation ou son équivalent - votre programme s'arrêtera soudainement et non sous votre contrôle. Ce n'est pas une bonne chose. Donc, ne changez pas les pointeurs de constants en non constants sans être très sûr qu'il est en fait OK de mentir à votre compilateur. Sachez que les compilateurs n'aiment pas qu'on leur mente et peuvent récupérer le leur, généralement au moment le plus gênant (comme lors de la démonstration de votre programme à un client potentiel important devant votre patron, le patron de votre patron et le patron de votre patron) ).

31
Jonathan Leffler

Il est parfaitement raisonnable de lancer un void * à un const void * et le compilateur devrait le faire implicitement en coulisse sans aucune réflexion de votre part, mais l'inverse est dangereux et doit être évité.

N'oubliez pas que si une fonction prend un pointeur const, vous êtes libre de lui passer soit une valeur const soit une valeur non - const. Dire que vous prenez un pointeur const revient simplement à déclarer que la mémoire ne sera pas modifiée par votre fonction.

Un exemple: (notez que les lignes marquées [~ # ~] danger [~ # ~] devraient lancer une erreur de compilation)

const void *p_const;
void *p_buffer;

// const pointers are allowed to hold static memory
p_const = "Foo"; // allowed
p_buffer = "Foo"; // DANGER!!!

// casting to const is implicit
p_const = malloc(LEN); // allowed - implicit cast
p_buffer = malloc(LEN); // allowed

// casting to const implicit again
write(STDOUT, p_const, LEN); // allowed
write(STDOUT, p_buffer, LEN); // also allowed - implicit cast

// const memory cannot be used where mutable memory is expected
read(0, p_buffer, LEN); // allowed
read(0, p_const, LEN); // DANGER!!

// To make the above more obivous, we'll skip the intermediate variable
// and place instead what it holds
read(0, malloc(LEN), LEN); // allowed - useless but at least no crashes
read(0, "foo", 4); // DANGER!!!

En règle générale, si une fonction que vous écrivez prend un pointeur sur une valeur que vous n'allez pas modifier, la signature de la fonction doit utiliser un pointeur const. L'utilisation d'un pointeur non déclaré const signifie que la mémoire vers laquelle vous pointez peut être modifiée.

Un autre exemple:

void do_something(const void* ptr, int length);

// Since the signature is a const pointer, I know I can call it like this:
do_something("foo",4);

Inversement, la fonction appelle un pointeur non constant, alors je dois le permettre:

void do_something(void* ptr, int length);

// This tells me that the function may overwrite my value.
// The safe solution therefore looks more like this:

char *myptr = char[4];
memcpy(myptr,"foo",4);    
do_something(myptr,4);

De même, si vous vous retrouvez dans une situation où vous devez convertir un pointeur const en un pointeur non - const, vous devez dupliquer la valeur pointée vers une partie modifiable de la mémoire, et passez votre doublon à la fonction plutôt qu'à l'original. Si cela ressemble à un mal de tête, c'est parce que c'est le cas. Si vous vous trouvez dans cette situation, vous avez probablement fait quelque chose de mal.

Conceptuellement, si la variable contient une "valeur", il s'agit probablement d'un pointeur const. Si, à la place, il contient un "tampon", alors c'est un pointeur non - const.

Les pointeurs dans vos signatures de fonction doivent toujours être déclarés const sauf si vous avez l'intention d'écrire dans cette mémoire. Suivre cette règle vous aidera à éviter des problèmes désastreux dans la logique de votre programme.

Je n'ai pas compris cette règle simple avant d'avoir programmé pendant 6 ans.

7
tylerl