2010-07-07 16 views
2
struct Vector 
{ 
    float x, y, z; 
}; 

func(Vector *vectors) {...} 

usage: 
load float *coords = load(file); 
func(coords); 

J'ai une question sur l'alignement des structures en C++. Je vais passer un ensemble de points à la fonction func(). Est-ce que c'est OK de le faire de la manière montrée ci-dessus, ou est-ce que cela dépend d'un comportement dépendant de la plate-forme? (Cela fonctionne au moins avec mon compilateur actuel) Quelqu'un peut-il recommander un bon article sur le sujet?Alignement de structure en C++

Ou, est-il préférable de créer directement un ensemble de points lors du chargement des données à partir du fichier?

Merci

+2

Votre article est très difficile à lire. –

+3

Ce n'est pas C++ comme vous l'avez maintenant. Votre utilisation ne compilera pas et ne montre pas comment vous voulez utiliser le vecteur en question. Veuillez modifier l'utilisation pour montrer ce que load() accepte et retourne. De plus, coords est un pointeur flottant dans votre exemple (sorte de) et func veut un pointeur vectoriel. –

+1

Écrire des données binaires dans un fichier et lire ce retour n'est pas portable. Il y a juste trop de variations entre les machines. Il est beaucoup plus facile de sérialiser les données (écrire en tant que texte), puis sérialiser à l'entrée dans la structure. –

Répondre

3

L'alignement de la structure dépend de la mise en œuvre. Cependant, la plupart des compilateurs vous donnent un moyen de spécifier qu'une structure doit être "empaquetée" (c'est-à-dire, disposée en mémoire sans octets de remplissage entre les champs). Par exemple:

struct Vector { 
    float x; 
    float y; 
    float z; 
} __attribute__((__packed__)); 

Le code ci-dessus provoquera le compilateur gcc pour emballer la structure en mémoire, ce qui rend plus facile à vider un fichier et lire plus tard. La manière exacte de faire ceci peut être différente pour votre compilateur (les détails devraient être dans le manuel de votre compilateur).

Je liste toujours les membres des structures empaquetées sur des lignes séparées afin d'être clair sur l'ordre dans lequel elles doivent apparaître. Pour la plupart des compilateurs cela devrait être équivalent à float x, y, z; mais je ne suis pas certain que ce soit un comportement dépendant de l'implémentation ou pas. Pour être sûr, j'utiliserais une déclaration par ligne.

Si vous lisez les données d'un fichier, vous devez valider les données avant de les transmettre au func. Aucune application de l'alignement des données ne compensera le manque de validation des entrées.

Edit:

Après la lecture de votre code plus, je comprends plus ce que vous essayez de faire. Vous avez une structure qui contient trois valeurs float et vous y accédez avec un float* comme s'il s'agissait d'un tableau de flottants. C'est une très mauvaise pratique. Vous ne connaissez pas le type de remplissage que votre compilateur pourrait utiliser au début ou à la fin de votre structure. Même avec une structure empaquetée, il n'est pas sûr de traiter la structure comme un tableau. Si un tableau est ce que vous voulez, utilisez un tableau. Le moyen le plus sûr est de lire les données à partir du fichier, de le stocker dans un nouvel objet de type struct Vector et de le passer à func. Si func est défini pour prendre un struct Vector* comme argument et que votre compilateur vous permet de passer un float* sans vous agripper, il s'agit en effet d'un comportement dépendant de l'implémentation sur lequel vous ne devez pas compter.

+1

Et en dehors de l'emballage/alignement de la structure, ne pas oublier les problèmes d'endianness! –

0

Tout d'abord, votre code exemple est mauvais:

load float *coords = load(file); 
func(coords); 

vous passez func() un pointeur sur un flotteur var au lieu d'un pointeur vers un objet vectoriel.

Deuxièmement, la taille totale du vecteur est égale à (sizeof (float) * 3), ou en d'autres termes à 12 octets.
Je consulterais le manuel de mon compilateur pour voir comment contrôler l'alignement de la structure, et juste pour avoir la tranquillité d'esprit que je lui donnerais, disons 16 octets.
De cette façon, je saurai que le fichier, s'il contient un vecteur, a toujours une taille de 16 octets et que je n'ai besoin de lire que 16 octets.

Édition:
Vérifiez MSVC9's align capabilities.

+0

Ne supposez pas que Vector est 12 octets. Le compilateur est libre d'ajouter un remplissage à tout moment. Le compilateur définira la taille et l'alignement pour rendre l'accès à un tableau de ces objets aussi efficace que possible. Essayer de combattre le compilateur et définir une taille spécifique à une structure est contre-productif (et non portable). –

+0

@Martin: Vous avez 100% raison, mais ma réponse est pour cette question spécifique et bien sûr que "12" est juste un exemple. – Poni

0

préfèrent passer par référence que passant par pointeur:

void func(Vector& vectors) 
{ /*...*/ } 

La différence ici entre un pointeur et une référence est qu'un pointeur peut être NULL ou d'un point à un endroit étrange dans la mémoire. Une référence fait référence à un objet existant.

En ce qui concerne l'alignement, ne vous inquiétez pas. Les compilateurs gèrent cela automagiquement (au moins l'alignement en mémoire).

Si vous parlez de l'alignement des données binaires dans un fichier, recherchez le terme «sérialisation».

2

Utiliser une opérateur >> surcharge d'extraction.

std::istream& operator>>(std::istream& stream, Vector& vec) { 
    stream >> vec.x; 
    stream >> vec.y; 
    stream >> vec.z; 
    return stream; 
} 

Maintenant, vous pouvez faire:

std::ifstream MyFile("My Filepath", std::ios::openmodes); 
Vector vec; 
MyFile >> vec; 
func(&vec); 
+0

Vous avez oublié de renvoyer une valeur à votre opérateur de flux. –

+2

Vous devriez probablement faire en sorte que votre opérateur de flux renvoie un flux. Cela permet d'enchaîner les opérations de flux. –

0

données binaires L'écriture est non portable entre les machines. A propos de la seule chose portable est le texte (même alors ne peut pas être invoqué car tous les systèmes utilisent le même format texte (heureusement, la plupart acceptent les 127 caractères ASCII et espérons bientôt nous standardiser sur quelque chose comme Unicode (il dit avec un sourire)

Si vous voulez écrire des données dans un fichier, vous devez décider du format exact du fichier, puis écrire du code qui lira les données de ce format et les convertira en la représentation de votre matériel spécifique pour ce type. le format pourrait être binaire ou il pourrait s'agir d'un format de texte sérialisé peu importe les performances (la vitesse d'E/S du disque sera probablement votre facteur limitant) .En termes de compacité, le format binaire sera probablement plus efficace. de l'écriture des fonctions de décodage sur chaque plate-forme le format texte est nettement plus facile t de celui-ci est déjà intégré dans les cours d'eau.

Solution si simple:
Lecture/écriture dans un format de texte sérialisé.
Aucun problème d'alignement également.

#include <algorithm> 
#include <fstream> 
#include <vector> 
#include <iterator> 

struct Vector 
{ 
    float x, y, z; 
}; 
std::ostream& operator<<(std::ostream& stream, Vector const& data) 
{ 
    return stream << data.x << " " << data.y << " " << data.z << " "; 
} 
std::istream& operator>>(std::istream& stream, Vector& data) 
{ 
    return stream >> data.x >> data.y >> data.z; 
} 


int main() 
{ 
    // Copy an array to a file 
    Vector data[] = {{1.0,2.0,3.0}, {2.0,3.0,4.0}, { 3.0,4.0,5.0}}; 
    std::ofstream file("plop"); 
    std::copy(data, data+3, std::ostream_iterator<Vector>(file)); 



    // Read data from a file. 
    std::vector<Vector> newData; // use a vector as we don't know how big the file is. 
    std::ifstream input("inputFile"); 
    std::copy(std::istream_iterator<Vector>(input), 
       std::istream_iterator<Vector>(), 
       std::back_inserter(newData) 
      ); 
}