32 votes

Comment vérifier si un pointeur pointe vers un emplacement mémoire correctement aligné ?

Étant donné un void * vers un espace de stockage, comment vérifier s'il pointe vers un espace de stockage correctement aligné sans aucun comportement défini par l'implémentation ?

Bien sûr, nous avons std::align Mais existe-t-il un moyen plus efficace d'y parvenir ?

template <std::size_t alignment>
inline bool is_aligned(void * ptr) noexcept {
    std::size_t max = 1u;
    return std::align(alignment, 1u, ptr, max);
}

PS : Je dois faire cela d'une manière compatible avec les normes C++, sans dépendre d'aucun hacks spécifiques à la plate-forme (définis par l'implémentation).

PPS : Je m'excuse pour mon anglais (incompréhensible), qui n'est pas ma langue maternelle.


ÉDITER (2018.08.24) : J'ai supprimé le terme "efficace" du titre, et j'ai ajouté des termes supplémentaires pour souligner que je ne veux pas de comportement défini par l'implémentation ou spécifique à la plate-forme.

0 votes

L'important à propos des pointeurs (dans un cas comme celui-ci) est de se rappeler qu'il ne s'agit en fait que de valeurs entières correspondant à une adresse. C'est le traitement qu'en font les compilateurs qui les rend spéciaux. Et puisqu'un pointeur n'est qu'un entier, vous pouvez utiliser les opérateurs bitwise normaux sur eux (avec le casting adéquat bien sûr), std::intptr_t est utile ici). Par exemple, pour vérifier si un pointeur est sur une adresse paire, vous pouvez faire reinterpret_cast<intptr_t>(some_pointer) & ~1 .

0 votes

Ceci étant dit, étant donné que ptr (dans votre exemple) n'est pas lui-même un constexpr alors le résultat de tout calcul l'utilisant ne peut pas non plus être constexpr .

2 votes

Notez également que l'alignement est par définition spécifique à une plate-forme et que l'exploitation des valeurs numériques renvoyées par un reinterpret_cast, comme le suggère @Some, est un comportement intrinsèquement non défini ou défini par l'implémentation.

25voto

user2079303 Points 4916

Si le reste n'est pas nul lors de la division de l'adresse avec l'alignement souhaité, l'adresse n'est pas alignée.

inline bool
is_aligned(const void * ptr, std::uintptr_t alignment) noexcept {
    auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
    return !(iptr % alignment);
}

Cela ne peut cependant pas être constexpr, à cause du cast.

En outre, cela repose sur le fait, défini par l'implémentation, que la conversion d'un pointeur en entier doit préserver la représentation numérique de l'adresse. Comme le soulignent les commentaires, cela n'est pas garanti par la norme, de sorte que cette fonction n'est pas nécessairement portable sur toutes les plates-formes. C'est également vrai, car il est facultatif pour l'implémentation de fournir la fonction std::uintptr_t .


Je m'attendrais à ce que cela ne soit nécessaire que pour l'alignement d'un type, ce qui serait plus pratique :

template<class T>
bool
is_aligned(const void * ptr) noexcept {
    auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
    return !(iptr % alignof(T));
}

3 votes

Tout pointeur peut être converti en un entier non signé d'une taille au moins égale à celle de uintptr_t . Toutefois, le résultat est défini par la mise en œuvre. Vous supposez qu'une implémentation commune est utilisée et que le modèle de mémoire est plat, ce qui est très probable de nos jours, mais pas garanti.

3 votes

@ArneVogel oui, il semble que cela ne soit pas garanti d'être strictement portable sur des plateformes obscures, où la conversion pourrait changer l'alignement.

0 votes

Vous pouvez rendre is_aligned constexpr comme suit : template<typename T> inline constexpr bool is_aligned(const void *p) { return (reinterpret_cast<const uintptr_t>(p) % alignof(T)) == 0; }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X