brk()和sbrk()
#include <unistd.h>
int brk(void *end_data_segment);
Returns 0 on success, or -1 on error.
void *sbrk(intptr_t increment);
Returns previous program break on success, or (void*)-1 on error
这两个函数,目前的水平是没必要用,看看就行了。
brk()
设定program break
(堆的顶部)为end_data_segment
所指的位置,由于虚拟内存以页为单位进行分配,end_date_segment
实际会四舍五入到下一个内存页的边界处。设定为低于&end
的位置,会有无法预知的行为。sbrk()
在原有地址上增加从参数increment
传入的大小(在linux中,sbrk()
实现是基于brk()
的),调用成功会返回前一个program_break
的地址。(对此有用法sbrk(0)
以监视内存分配函数包的行为)
malloc()、calloc()、realloc()、free()
这三个都是C语言的基础,就不啰嗦了。
其中realloc()
新分配的内存是未初始化的
传给free()
空指针,什么都不会做,但是如果两次释放同一个指针以及对释放过的指针的任何使用,都会产生错误,可能导致不可预知的后果。(free()两次空指针似乎不会报错,毕竟free(NULL)什么都不会做)
#include <stdlib.h>
void *malloc(size_t size);
Returns pointer to allocated memory on success, or NULL on error
void *calloc(size_t numites, size_t size);
Returns pointer to allocated memory on success, or NULL on error
void *realloc(void* ptr, size_t size);
Returns pointer to allocated memory on success, or NULL on error
void free(void* ptr);
memalign()和posix_memalign()
这两个函数的作用是分配内存时,起始地址可以与2的整数次幂对齐。对于某些应用非常有用。(水平不够,用不上)
#include <malloc.h>
void *memalign(size_t boundary, size_t size);
Returns pointer to allocated memory on success, or NULL on error
memalign()
分配size
个字节,起始地址是boundry
的整数倍,而boundry
必须是2的整数次幂。(这个函数并非所有UNIX系统都实现了。大多数提供memalign()
的其他UNIX实现都要求引用头文件<stdlib.h>
而非<malloc.h>
以获得函数声明)。
#include <stdlib.h>
int posix_memalign(void **memptr, size_t alignment, size_t size);
Returns 0 on success, or a positive error number on error
这个是SUSv3规范里的函数,不过似乎还没有完全普及。
已经分配的内存地址会通过参数memptr
返回;内存与alignment
的整数倍对齐,alignment
必须是sizeof(void*)
(在大多数硬件架构上是4或8个字节)与2的整数次幂两者间的乘积。
出错时不会返回-1,而是返回错误号(即通常在errno中返回的正整数)。
示例
例如,如果 sizeof(void*)
是4,就可以使用posix_memalign()
分配65536字节的内存,并与4096字节的边界对齐:
int s;
void *memptr;
s = posix_memalign(&memptr, 1024 * sizeof(void*), 65536);
if(s != 0)
/*Handle error*/
alloca()
感觉这是最有用的一个~
#include <alloca.h>
void *alloca(size_t size);
Returns pointer to allocated block memory
作用
参数size
指定在堆栈上分配的字节数。会返回分配内存块的指针。不需要也不可能调用free()
来释放alloca()
分配的内存。 同样也不能用realloc()
来调整alloca()
分配的内存大小。
可移植性
虽然这个函数不是SUSv3规范中的,但大多数UNIX都提供了此函数。所以也具有可移植性。
旧版本的libc
和其他一些UNIX实现(主要是BSD的衍生版本),要获取alloca()
函数的声明,需要引入<stdlib.h>
而非<alloca.h>
特点
- 若调用
alloca()
造成堆栈溢出,则程序行为无法预知,特别是没有收到NULL返回值时。(事实上,可能会收到一个SIGSEGV
信号)。 - 不能在一个函数参数列表中调用
alloca()
,
如:
func(x, alloca(size), z)
这会使alloca()
分配的堆栈空间出现在当前参数的空间内(函数参数都位于栈帧内的固定位置)。相反,必须采用这样的代码:
void *y;
y = alloca(size);
func(x, y, z);
- 在信号处理程序中调用
longjmp()
或setjmp()
,以执行非局部条扎un时,alloca()
的作用尤其突出。此时,在“起跳”函数和“落地”函数之间的函数中,如果调用了malloc()
来分配内存,要想避免内存泄露极其困难,甚至是不可能的。但是,调用alloca()
就完全可以避免这一问题,因为堆栈时由这些调用展开的,所以已分配的内存会被自动释放。
(这个特性真的很妙,不懂的应该是longjmp()
和setjmp()
的实现原理没搞清楚。可以看看我的上一篇博客 setjmp()与longjmp())