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

func(Vector *vectors) {...} 

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

我有一個關於C++中結構對齊的問題。我將通過一組點來函數func()。可以按照上面所示的方式來完成它,還是依靠平臺相關的行爲? (它至少可以和我目前的編譯器一起工作)有人可以推薦關於這個主題的好文章嗎?C++中的結構比對

或者,從文件加載數據時直接創建一組點是否更好?

謝謝

+2

您的帖子非常難以閱讀。 – 2010-07-07 16:54:18

+3

這不是C++現在的方式。您的使用情況不會編譯,也不會說明您希望如何使用該矢量。請修改用法以顯示什麼load()正在接受和返回。另外,coords是你的例子中的一個浮點指針(有點),func需要一個矢量指針。 – 2010-07-07 17:02:37

+1

將二進制數據寫入文件並讀取該文件是不可移植的。機器之間的差異太多了。序列化數據(將其寫爲文本)然後將輸入序列化回結構要容易得多。 – 2010-07-07 17:39:56

回答

3

結構比對依賴於實現。但是,大多數編譯器都提供了一種指定結構應該「打包」的方式(即,在內存中排列,字段之間沒有填充字節)。例如:

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

上面的代碼將導致gcc編譯器收拾存儲器中的結構,使其更容易傾倒到一個文件,讀回在後面。編譯器的確切做法可能會有所不同(細節應該在編譯器手冊中)。

我總是在單獨的行中列出打包結構的成員,以便明確它們應該出現的順序。對於大多數編譯器來說,這應該等於float x, y, z;,但我不確定這是否與實現有關。爲了安全起見,我會每行使用一個聲明。

如果您正在讀取文件中的數據,則需要在將數據傳遞到func之前對其進行驗證。沒有大量的數據對齊實施將彌補缺乏輸入驗證。

編輯:

進一步閱讀你的代碼後,我更加明白什麼你正在嘗試做的。您有一個包含三個float值的結構,並且您正在使用float*訪問它,就好像它是一個浮點數組。這是非常糟糕的做法。你不知道你的編譯器在結構的開始或結束時可能使用哪種填充。即使採用緊湊結構,將結構視爲陣列也是不安全的。如果一個數組是你想要的,然後使用一個數組。最安全的方法是從文件中讀取數據,將其存儲到類型爲struct Vector的新對象中,並將其傳遞給func。如果func被定義爲以struct Vector*作爲參數,並且您的編譯器允許您傳遞float*而不會產生懷疑,那麼這實際上就是您不應該依賴的依賴於實現的行爲。

+1

除了包裝/結構對齊之外,別忘了排序問題! – 2010-07-07 17:02:18

0

首先,您的示例代碼是壞:

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

你傳遞FUNC()一個指向一個浮動變種,而不是一個指針到矢量對象。其次,Vector的總大小如果等於(sizeof(float)* 3),換句話說就是12個字節。
我會查閱我的編譯器的手冊,看看如何控制結構的對齊,並只是爲了讓我安心,比如說16個字節。
這樣我就知道該文件如果包含一個向量,總是隻有16個字節,我只需要讀取16個字節。

編輯:
檢查MSVC9's align capabilities

+0

不要以爲Vector是12字節。編譯器可以隨時添加填充。編譯器將設置大小和對齊以儘可能高效地訪問這些對象的數組。試圖對抗編譯器並將特定大小設置爲結構是反生產的(並且不可移植)。 – 2010-07-07 17:35:48

+0

@Martin:你100%正確,但我的答案是根據這個具體問題,當然,「12」只是一個例子。 – Poni 2010-07-07 18:12:59

0

通過引用不想傳遞大於由指針傳遞:

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

一個指針和一個參考值之間這裏的區別在於:指針可以是NULL或指向在存儲器一些陌生的地方。引用指的是一個現有的對象。

只要對齊,不要關心自己。編譯器自動處理它(至少在內存中對齊)。

如果您正在討論二進制數據在文件中的對齊方式,請搜索術語「序列化」。

2

使用操作符>>提取過載。

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

現在你可以這樣做:

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

你忘了從你的流操作符中返回一個值。 – 2010-07-07 18:00:27

+2

你應該讓你的流操作符返回一個流。這允許鏈操作。 – 2010-07-07 18:01:02

0

寫入二進制數據是機器之間的非便攜。
關於唯一的便攜式的東西是文本(即使不能依靠,因爲並非所有的系統都使用相同的文本格式(幸運的是大多數接受127個ASCII字符,希望很快我們會標準化爲像Unicode這樣的東西(他笑着說) )

如果要將數據寫入文件,您必須確定文件的確切格式,然後編寫代碼讀取該格式的數據,並將其轉換爲您的特定硬件的表示形式。格式可以是二進制的,也可以是序列化的文本格式,它在性能上並不重要(因爲磁盤IO速度可能會成爲你的限制因素)在緊湊性方面,二進制格式可能會更加高效。在每個平臺上編寫解碼函數的文本格式肯定比較容易它已經建成了河流。

如此簡單的解決方案:
讀取/寫入序列化的文本格式。
也沒有對齊的問題。

#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) 
      ); 
}