Quel est le type dynamic de l’object alloué par malloc?

La norme C ++ fait référence au terme “type dynamic” (et la norme C, au “type effectif” dans le même contexte), par exemple

Si un programme tente d’accéder à la valeur stockée d’un object via une valeur glval autre que l’un des types suivants, le comportement est indéfini:

  • le type dynamic de l’object,

Mais comment le type dynamic de l’object alloué avec malloc déterminé?

Par exemple:

 void *p = malloc(sizeof(int)); int *pi = (int*)p; 

Le type dynamic de l’object pointé par pi sera-t-il int ?

Selon la spécification C ++:

Type dynamic:

type de l’object le plus dérivé (1.8) auquel fait référence la valeur glvalue indiquée par une expression glvalue

La valeur de retour de malloc est un bloc de stockage non initialisé. Aucun object n’a été construit dans cette mémoire. Et donc il n’a pas de type dynamic.

Le void* ne pointe pas vers un object et seuls les objects ont un type dynamic.

Vous pouvez créer un object dans ce stockage en commençant sa vie. Mais jusqu’à ce que vous le fassiez, il ne s’agit que de stockage.

En C, le type effectif n’est pertinent que lorsque vous accédez à un object. Puis en est déterminé par

  • le type de déclaration, s’il en a un
  • le type d’un autre object dont il s’agit d’une copie (par exemple, memcpy )
  • le type de la lvalue par laquelle elle est accédée, par exemple si un void* est converti en un autre type de pointeur (par exemple int* ), qui est ensuite déréférencé.

C’est généralement ce qui se passe avec les objects malloc ed, si vous affectez la valeur de retour de malloc à un type de pointeur.

Le type dynamic est un terme formel pour décrire des objects essentiellement polymorphes, c’est-à-dire ayant au moins une fonction virtual . Il s’agit donc d’un terme C ++ car C n’a pas de concept de virtual , par exemple.

Mais comment le type dynamic de l’object alloué avec malloc est-il déterminé?

Ce n’est pas. malloc alloue N octets bruts de mémoire et la renvoie sous forme de void* – vous devez en déduire le bon type. De plus, cette mémoire représente simplement la zone où l’object est placé, mais cet object ne sera pas vivant jusqu’à ce que vous appeliez explicitement son constructeur. (encore une fois, dans une perspective C ++)

Le type dynamic de l’object pointé par pi sera-t-il int?

Non, car le terme type dynamic est significatif lors de la description d’un object avec des types de classe. int n’est ni ne peut être.

 class Foo { //virtual ~Foo() = default; virtual void f() {} }; class Bar : public Foo { virtual void f() {} }; // ... Foo *ptr = new Bar(); 

Ici Foo est le type statique de ptr tandis que Bar est son type dynamic.

Le statu quo est que malloc ne crée pas d’objects. Les seules constructions possibles sont les new expressions, définitions, transformations et assignations aux membres variantes. Voir P0137R0 pour une formulation correcte à ce sujet.

Si vous souhaitez utiliser le stockage généré par malloc , en supposant qu’il soit correctement aligné (ce qui est le cas sauf si vous utilisez des alignements étendus), utilisez un appel au placement new:

 auto p = malloc(sizeof(int)); int* i = new (p) int{0}; // i points to an object of type int, initialized to zero 

Par conséquent, utiliser malloc en C ++ est totalement inutile, car bog-standard new combine efficacement ces étapes en une seule.

Voir aussi la réponse de @ TC dans la question connexe du demandeur.

Selon 1.3.7 en norme C ++ 11,

type dynamic type de glvalue de l’object le plus dérivé (1.8) auquel se réfère la glvalue désignée par une expression glvalue [Exemple: si un pointeur (8.3.1) p dont le type statique est «pointeur sur la classe B» pointe vers un object de classe D, dérivée de B (clause 10), le type dynamic de l’expression * p est «D.» Les références (8.3.2) sont traitées de la même manière. – fin exemple]

à titre d’exemple

 class A {} class B : public A {} A *a = new B; 

le type “statique” de a est A * alors que son type dynamic est B * .

L’idée de ne pas référencer le même type vient à l’abri de quelque chose comme

 class A{} class B : public A {int x;} class C : public A {int y;} A *a = new B; reinterpret_cast(a)->x; 

ce qui peut conduire à un comportement indéfini.

void * ne pointe pas vers un object, mais la distinction entre type dynamic et type de déclaration n’a de sens que pour les objects.