2013-08-27 54 views
2

我確定這是一個基本問題,但我一直無法找到這是否是合法的內存分配策略。我正在讀取文件中的數據,並且正在填充結構。成員的大小是可變的每個看了那麼我的結構元素,像這樣爲一個malloc中的元素分配結構和內存

struct data_channel{ 
    char *chan_name; 
    char *chan_type; 
    char *chan_units; 
}; 

指針所以讀我弄清楚每個字符串的大小,所以我可以分配內存爲他們在我的問題是,我可以爲一個malloc分配結構和字符串的內存,然後將指針填入?

假設chan_name的大小是9,chan_type 10和chan_units 5.所以我會分配和做這樣的事情。

struct data_channel *chan; 

chan = malloc(sizeof(struct data_channel) + 9 + 10 + 5); 
chan->chan_name = chan[1]; 
chan->chan_type = chan->chan_name + 9; 
chan->chan_units = chan->chan_type + 10; 

所以我讀了幾個關於內存對齊的文章,但我不知道如果這樣做上面是一個問題或沒有,也可能有什麼樣的意想不到的後果。我已經在我的代碼中實現了它,它似乎工作正常。我只是不想跟蹤所有這些指針,因爲實際上我的每個結構都有7個元素,我可以有100個以上的通道。這當然意味着700個指針加上每個結構的指針總共800個。我還必須設計一種方法來釋放它們。我也想把這個策略應用於其中的字符串數組,然後我需要有一個指向數組的指針。我現在沒有任何結構可以混合使用數據類型,但這可能會成爲一個問題,但我可能會遇到問題?

+0

有一個明顯的錯誤,那會導致代碼不能編譯。 –

+0

@JoachimPileborg - 你的意思是陳不是一個數組? –

+0

看起來非常危險且不便攜。我只是建立一個分配例程(用於結構)和一個釋放例程(用於結構)並完成它。無論如何你必須分配和釋放它,所以只需封裝你不想處理的額外指針即可。 – Jiminion

回答

5

如果chan_name是8字符串,chan_type是9字符串和chan_units是4字符串,那麼是的,它會在你修復你必須分配給chan_name當編譯錯誤,做工精細。

如果爲結構和所有字符串(包括它們的字符串終止符)分配足夠的內存,那麼可以使用這樣的方法。也許不被所有人推薦,但它會起作用。

+1

+1。如果你有'char'以外的其他類型,那麼可能會有內存對齊問題,這就是大多數人會避免使用這種方法的原因。 –

1

我知道在結構的末尾有一個數組時,有一個確定的方法可以做到這一點,但由於所有的數組都具有相同的類型,因此您可能很幸運。肯定的方法是:

#include <stddef.h> 
#include <stdlib.h> 

struct StWithArray 
{ 
    int blahblah; 
    float arr[1]; 
}; 
struct StWithArray * AllocWithArray(size_t nb) 
{ 
    size_t size = nb*sizeof(float) + offsetof(structStWithArray, arr); 
    return malloc(size); 
} 

在結構中使用實際的數組可以保證對齊。

現在將它應用到你的情況:

#include <stddef.h> 
#include <stdlib.h> 

struct data_channel 
{ 
    char *chan_name; 
    char *chan_type; 
    char *chan_units; 

    char actualCharArray[1]; 
}; 

struct data_channel * AllocDataChannel(size_t nb) 
{ 
    size_t size = nb*sizeof(char) + offsetof(data_channel, actualCharArray); 
    return malloc(size); 
} 
struct data_channel * CreateDataChannel(size_t length1, size_t length2, size_t length3) 
{ 
    struct data_channel * pt = AllocDataChannel(length1 + length2 + length3); 
    if(pt != NULL) 
    { 
     pt->chan_name = &pt->actualCharArray[0]; 
     pt->chan_type = &pt->actualCharArray[length1]; 
     pt->chan_name = &pt->actualCharArray[length1+length2]; 
    } 
    return pt; 
} 
+0

+1,因爲它可以工作,不管對齊。 –

+0

這是使用C99之前的'struct hack'。它在實踐中仍然有效。 –

3

這部分取決於元素類型。你一定可以用字符串來做到這一點;與其他一些類型一樣,你不得不擔心對齊和填充問題。

struct data_channel 
{ 
    char *chan_name; 
    char *chan_type; 
    char *chan_units; 
}; 

struct data_channel *chan; 
size_t name_size = 9; 
size_t type_size = 10; 
size_t unit_size = 5; 

chan = malloc(sizeof(struct data_channel) + name_size + type_size + unit_size); 
if (chan != 0) 
{ 
    chan->chan_name = (char *)chan + sizeof(*chan); 
    chan->chan_type = chan->chan_name + name_size; 
    chan->chan_units = chan->chan_type + type_size; 
} 

這將在實踐中運行良好 - 在標準標準化之前它已經過了很長時間。我不能立即明白爲什麼標準會禁止這一點。

更棘手的是,如果你需要分配一個數組int,以及兩個字符串。那麼你不得不擔心對齊問題。

struct data_info 
{ 
    char *info_name; 
    int *info_freq; 
    char *info_unit; 
}; 

size_t name_size = 9; 
size_t freq_size = 10; 
size_t unit_size = 5; 
size_t nbytes = sizeof(struct data_info) + name_size + freq_size * sizeof(int) + unit_size; 
struct data_info *info = malloc(nbytes); 

if (info != 0) 
{ 
    info->info_freq = (int *)((char *)info + sizeof(*info)); 
    info->info_name = (char *)info->info_freq + freq_size * sizeof(int); 
    info->info_unit = info->info_name + name_size; 
} 

這已通過分配最嚴格對齊類型(int陣列),然後再後來分配串的簡單的權宜之計。然而,這部分是你必須對可移植性作出判斷的地方。我相信代碼在實踐中是可移植的。

C11具有定位設備(_Alignof_Alignas<stdalign.h>,加上max_align_t<stddef.h>),可以改變這個答案(但我沒有研究過足夠的,所以我不知道如何,還),但在這裏列出的技術將在任何版本的C中工作,只要您注意數據的對齊。請注意,如果在結構中有單個陣列,那麼C99提供了一種替代舊的'struct hack'(稱爲)的靈活陣列成員(FAM)。這使您可以顯式地將數組作爲結構的最後一個元素。

struct data_info 
    { 
     char *info_name; 
     char *info_units; 
     int info_freq[]; 
    }; 

    size_t name_size = 9; 
    size_t freq_size = 10; 
    size_t unit_size = 5; 
    size_t nbytes = sizeof(struct data_info) + name_size + freq_size * sizeof(int) + unit_size; 
    struct data_info *info = malloc(nbytes); 

    if (info != 0) 
    { 
     info->info_name = ((char *)info + sizeof(*info) + freq_size * sizeof(int)); 
     info->info_units = info->info_name + name_size; 
    } 

請注意,本例中沒有初始化FAM的步驟,info_freq。你不能有這樣的多個數組。

請注意,概述的技術不容易應用於結構數組(至少,外部結構數組)。如果你付出相當大的努力,你可以讓它工作。另外,請注意realloc();如果您重新分配空間,則必須在數據移動時修復指針。

另一點:特別是在64位機器上,如果字符串的大小足夠統一,那麼你可能會更好地在結構中分配數組,而不是使用指針。

struct data_channel 
{ 
    char chan_name[16]; 
    char chan_type[16]; 
    char chan_units[8]; 
}; 

這佔用40個字節。在一個64位的機器上,原始數據結構將佔據三個指針的24個字節和(9 + 10 + 5)個字節的數據的另外24個字節,總共分配了48個字節。

0

Joachim和Jonathan的回答很好。除此之外我想提的是這個。

單獨mallocs和釋放給你一些基本的保護,如緩衝區溢出,訪問 免費等我的意思是基本的,而不是Valgrind喜歡的功能。分配一個單獨的塊並在內部使用它將導致此功能的丟失。未來,如果malloc完全是不同大小的,那麼單獨的malloc可能會向您購買來自malloc實現中不同分配桶的效率,尤其是如果您要在不同時間釋放它們的話。

你必須考慮的最後一件事是你多頻繁地調用malloc。如果頻繁,那麼多個malloc的成本可能會很高。