Bonsoir,
Ce que tu cherches à faire est erroné.
En théorie
La norme du langage C stipule en effet qu'un objet ne doit avoir sa valeur accédée uniquement par une lvalue qui a un type compatible, qualificateurs et signes omis, avec son type effectif (l'exception étant le type caractère). Dans ton cas, l'objet désigné par la lvalue
a[10] est potentiellement accédé par une lvalue qui a le type
int, ce qui n'est ni une version qualifiée, signée, non signée du type
uint16_t ou un type caractère. Par omission de la description d'un comportement particulier, la norme privilégie donc un comportement indéterminé. Cette règle, communément appelée
règle de strict aliasing est citée ci-dessous. On notera que les différences entre les normes sont minimes, et ne concernent que la définition du type référence. En C89, il s'agit du « type déclaré », ce qui n'est pas très clair, notamment dans un cas particulier (voir plus bas).
Le
type effectif d'un objet est défini par son type déclaré, excepté pour les objets alloués dynamiquement, qui voient leur type fixé par la dernière lvalue les ayant modifiés. Ce sont des petits détails qui peuvent parfois faire une différence dans le comportement observable du programme. Si ça t'intéresse, j'ai co-écrit un cours de vulgarisation à ce sujet.
En pratique
Le principal problème des accès aléatoires de cette manière est qu'ils peuvent conduire à des soucis d'alignement. Par exemple, sur une architecture x86, où chaque type est aligné sur une multiple de sa propre taille,
a sera aligné sur une frontière de 2, tandis que
b est soumis à un alignement de 4. Donc il est probable que
b ne soit pas correctement aligné, si on l'assigne à
a[10]. Or, les
accès non alignés constituent un thème compliqué dans le monde du matériel. En théorie, sur la plupart des architectures Intel, ils sont autorisés, mais cela est au prix de grosses pertes de performance. En outre, d'autres architectures sont moins laxistes et envoient des exceptions lorsque cela se produit.
Il y a donc nécessité de changer le type d'une de tes deux variables. La première solution de
Neckara est correcte, mais la deuxième conduit à une seconde erreur : il n'y a aucune garantie qu'un entier signé (
int) puisse tenir une valeur non signée sur 16 bits, puisque la constante
INT_MAX ne doit être supérieure qu'à +32767, ce qui est insuffisant pour stocker des valeurs allant jusqu'à +65535. Si tu n'as pas de contrôle sur tes données, un code pourrait ressembler à :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| // C99
#include <assert.h>
#include <limits.h>
#include <stdint.h>
uint16_t a[20];
int b[10];
for (int i = 0; i < 10; ++i)
{
uint16_t v = a[i + 10];
assert((unsigned int)v < (unsigned int)INT_MAX);
b[i] = v;
} |
Chose assez intéressante,
gcc (je n'ai que la vieille version 4.4.4 sur ma machine) affiche un avertissement qui me paraît être un faux positif « comparison is always true due to limited range of data type ». Serait-ce un nouveau déboire du typeur non portable imposé par les spécifications du langage C ?
Bonne soirée !

Partager