Est-ce que “sizeof (arr )” peut conduire à un comportement indéfini?

Il existe un schéma bien connu pour déterminer la longueur d’un tableau:

int arr[10]; size_t len = sizeof(arr) / sizeof(arr[0]); assert(len == 10); 

Ce modèle s’applique aux tableaux statiques et aux tableaux automatiques de taille constante. Cela s’applique également aux tableaux de longueur variable dans C99.

Je veux appliquer une idée similaire pour déterminer la taille de tableau dynamic en octets:

 size_t known_len = 10; int *ptr = malloc(known_len * sizeof(int)); size_t size = known_len * sizeof(ptr[0]); assert(size == known_len * sizeof(int)); 

C’est plus joli que ce qui est known_len * sizeof(int) car sizeof(ptr[0]) ne fait pas référence au type d’élément de tableau actuel. Par conséquent, il n’est pas nécessaire que le lecteur du code connaisse le type.

Cependant, il m’est difficile de savoir si l’expression sizeof(ptr[0]) peut entraîner un comportement indéfini. Comme il est étendu:

 sizeof(ptr[0]) -> sizeof(*((ptr) + (0))) -> sizeof(*ptr) 

L’expression du résultat est discutable dans le cas où ptr est 0 :

 sizeof(*((int*) 0)) 

Selon une norme C99:

(C99, 6.3.2.3p3): “Une expression constante entière ayant la valeur 0 , ou une telle expression convertie dans le type void * , est appelée une constante de pointeur nulle.” Déréférencer un pointeur null est un comportement indéfini.

(C99, 6.5.3.2.p4) “Si une valeur non valide a été atsortingbuée au pointeur, le comportement de l’opérateur unaire * n’est pas défini.87)”

87): “Parmi les valeurs non valides permettant de déréférencer un pointeur par l’opérateur unaire * figurent un pointeur nul, une adresse alignée de manière inappropriée pour le type d’object pointé et l’adresse d’un object à la fin de sa durée de vie.”

Mais il n’est jamais précisé si la taille d’une telle expression peut conduire à un comportement indéfini. En fait, cette taille devrait être évaluée au moment de la compilation.

Mes questions sont:

  • L’expression sizeof(ptr[0]) peut-elle être utilisée dans le code lorsque le type de ptr est connu et que la valeur de ptr n’est pas connue?
  • Une telle utilisation peut-elle être justifiée selon la norme C99? Spécification GNU GCC?

L’expression ptr[0] ne sera pas évaluée en sizeof(ptr[0]) . La taille sera déterminée en utilisant simplement le type de ptr[0] au moment de la compilation.

C11: 6.5.3.4:

L’opérateur sizeof donne la taille (en octets) de son opérande, qui peut être une expression ou le nom entre parenthèses d’un type. La taille est déterminée à partir du type de l’opérande . Le résultat est un entier. Si le type de l’opérande est un type de tableau de longueur variable, l’opérande est évalué. sinon, l’opérande n’est pas évalué et le résultat est une constante ininteger.

Cela signifie qu’il n’y a pas de comportement indéfini.

Cela ne causera pas un comportement indéfini.

À l’exception de la taille des tableaux de longueur variable, sizeof est une expression constante à la compilation. Le compilateur traite l’expression pour déterminer son type, sans générer le code permettant d’évaluer l’expression au moment de la compilation. Par conséquent, la valeur sur ptr[0] (qui est un pointeur non initialisé) n’a pas d’importance.

De plus, si vous souhaitez atsortingbuer dix nombres entiers, vous devriez appeler malloc comme malloc :

 int *ptr = malloc(known_len * sizeof(ptr[0])); 

Sinon, vous allouez dix octets, ce qui est trop petit pour stocker dix entiers. Notez que dans l’expression ci-dessus, ptr n’est pas initialisé au moment de l’appel, ce qui convient parfaitement à sizeof .

En général, s’il me manque quelque chose, le déréférencement d’un pointeur null sous sizeof peut conduire à un comportement indéfini. Depuis C99, sizeof n’est pas une construction purement compilable. L’opérande de sizeof est évalué au moment de l’exécution si le type d’opérande est un VLA.

Considérez l’exemple suivant

 unsigned n = 10; int (*a)[n] = NULL; // `a` is a pointer to a VLA unsigned i = 0; sizeof a[i++]; // applying `sizeof` to a VLA 

Selon la norme C99, l’argument de sizeof est censé être évalué (c’est-à-dire que i est censé être incrémenté). Cependant, je ne suis pas tout à fait sûr que la déréférence de point nul dans a[0] soit supposée produire un comportement indéfini ici.

En tant qu’expression (pas une déclaration), ptr[0] est identique à *ptr .

Tant que le type de ptr est connu et qu’il ne pointe pas vers un vide, sizeof est garanti pour fonctionner avec sizeof(*ptr) et sizeof(ptr[0]) .

Le code est diabolique.

 size_t known_len = 10; int *ptr = malloc(known_len); size_t size = known_len * sizeof(ptr[0]); assert(size == known_len * sizeof(int)); 

La taille de l’allocation de mémoire à partir de votre sharepoint vue est de 10 octets. Ce n’est pas 10 pointeurs à int.

  known_len / sizeof(ptr[0]) 

vous donnera le nombre d’entiers que l’allocation peut contenir.

En l’état, vous risquez de perdre la fin de l’allocation, ce qui entraîne un comportement indéfini.

Considérez malloc (unknown_length * sizeof ptr [0]);