我一直在研究CLISP的代碼。在那裏做了類似的事情,將一些立即值壓入指針(而不是從垃圾收集堆中分配一個對象)。
在CLISP的情況下,這是有效的,因爲我們知道分配器可以返回的地址範圍。然後有一點永遠不會被一個有效的地址設置,如果設置表明「指針」不是一個實際的指針,而是數據(在你的情況下,單個或更多的字符)。
btw:使用char *
並不是一個好計劃,由於未定義的行爲,或者意外地將這樣的「指針」傳遞給例如strlen
,您已經完成了一步。我想使用union
是一個更好的方法(儘管我現在沒有時間檢查標準中是否允許以這種方式使用聯合的實際規則)。更安全的是在結構中包裝一個uintptr_t
。
[..]但是如果某人在這裏對malloc的深層有一些瞭解,那可能爲我節省很多時間。
這不會給你買東西。切換操作系統,平臺或只是使用的標準庫,一切都可能與您瞭解的當前正在查看的malloc實現有所不同。
一種方法是使用您自己的分配器 - 就像使用CLISP一樣 - 從某個池中獲取內存(例如,通過Linux/BSD上的mmap
獲取),它駐留在某個預定義的地址處。
但是,您還可以使用malloc
(或更適合的功能,例如C11 aligned_alloc
或posix_memalign
)將allocate aligned memory用於您的字符串。假設您將每個字符串對齊到一個偶數地址。這樣,當你看到一個設置了最低有效位的地址時,你可以確定它實際上不是一個地址,而是直接數據,即字符串本身。
僅使用malloc
分配2個字節對齊的內存,所以:分配2個附加字節。如果malloc
返回的地址已正確對齊,則返回下一個正確對齊的地址(char_ptr + 2
),並在該地址之前直接標記單元格,並指示原始地址已對齊(char_ptr[1] = '1'
)。另一方面,如果返回的地址未正確對齊,則返回直接跟隨的字節(其正確對齊; char_ptr + 1
),並將該單元直接標記在該地址之前(因此,第一個; char_ptr[0] = '0'
)。
釋放時,直接在傳入的地址之前查看單元格,它包含標記以告訴您需要哪個地址free
。
在代碼:
#define IS_ALIGNED_2BYTE(value) (((uintptr_t)(value) & 0x01) == 0)
/// Allocate a memory region of the specified size at an even (2 byte
/// aligned) address.
///
/// \param[in] size Required size of the memory region.
/// \return Pointer to the memory, or NULL on failure.
inline static void * allocate_2byte_aligned(size_t size) {
#ifdef HAVE_ALIGNED_ALLOC
return aligned_alloc(2, size);
#elif defined(HAVE_POSIX_MEMALIGN)
void * ptr;
if (posix_memalign(&ptr, sizeof(void *), size) == 0) {
assert(IS_ALIGNED_2BYTE(ptr)); // Paranoia due to uncertainty
// about alignment parameter to
// posix_memalign.
return ptr;
} else {
return NULL;
}
#else
char * const memory = malloc(size + 2);
if (! memory) {
return NULL;
}
if (IS_ALIGNED_2BYTE(memory)) {
// memory is correctly aligned, but to distinguish from originally
// not aligned addresses when freeing we need to have at least one
// byte. Thus we return the next correctly aligned address and
// leave a note in the byte directly preceeding that address.
memory[1] = '1';
return &(memory[2]);
} else {
// memory is not correctly aligned. Leave a note in the first byte
// about this for freeing later and return the next (and correctly
// aligned) address.
memory[0] = '0';
return &(memory[1]);
}
#endif
}
/// Free memory previously allocated with allocate_2byte_aligned.
///
/// \param[in] ptr Pointer to the 2 byte aligned memory region.
inline static void free_2byte_aligned(void * ptr) {
assert(IS_ALIGNED_2BYTE(ptr));
#if defined(HAVE_ALIGNED_ALLOC) || defined(HAVE_POSIX_MEMALIGN)
free(ptr);
#else
char const * const memory = ptr;
void const * original_address;
if (memory[-1] == '0') {
// malloc returned an address that was not aligned when allocating
// this memory block. Thus we left one byte unused and returned
// the address of memory[1]. Now we need to undo this addition.
original_address = &(memory[-1]);
} else {
// malloc returned an address that was aligned. We left two bytes
// unused and need to undo that now.
assert(memory[-1] == '1');
original_address = &(memory[-2]);
}
free((void *) original_address);
#endif
}
創建和銷燬「指針或即時的數據」的結構是那麼簡單:
typedef struct its_structure {
uintptr_t data; ///< Either a pointer to the C string, or the actual
///< string, together with a bit to indicate which
///< of those it is.
} its;
its its_alloc(size_t size) {
if (size < sizeof(uintptr_t)) {
its const immediate_string = {.data = 0x01};
return immediate_string;
} else {
void * const memory = allocate_2byte_aligned(size);
assert(IS_ALIGNED_2BYTE(memory));
its const allocated_string = {
.data = memory ? (uintptr_t) memory : (0x01 | 0x02) /* Invalid string */};
return allocated_string;
}
}
void its_free(its string) {
if (IS_ALIGNED_2BYTE(string.data)) {
free_2byte_aligned((void *) string.data);
} // else immediate, thus no action neccessary
}
上面的代碼實際上是從a small library I wrote測試/寫這個答案。如果你想然後使用/增強它。
什麼是這樣做的呢?你的程序是否有一些不尋常的內存限制?另外,如果有時你的指針指向一個以NUL結尾的字符串,有時它們指向一個沒有NUL終結符的'char',你的程序如何知道哪個是哪個? –
你有一個內存泄漏,注意男孩! –
或者......你可以爲單個角色分配一大塊內存。讓指針指向該緩衝區,並簡單地檢查該地址是否在該緩衝區的範圍內。從而消除一些駭客。 – StoryTeller