我一直在研究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