内存管理

int main(){
char *ptr;
// alloc a memory area.
ptr = (char*)malloc(10);
free(ptr);

// alloc an array.
ptr = (char*)calloc(10, 1);
printf("%u\n", (unsigned)ptr);

// realloc.
ptr = (char*)realloc(ptr, 20);
printf("%u\n", (unsigned)ptr);
free(ptr);

return 0;
}

  realloc用的比较少,其实就是改变大小。这里返回的ptr可能是和原来相同的,也可能不同。calloc与malloc的不同之处是申请一个数组,而且返回之前会把数组中所有的值都设置为0.

int main(){
char *ptr;
int ret;

ret = posix_memalign((void**)&ptr, 8, 10);
if(ret){
printf("posix_memalign error.\n");
}else{
printf("%d\n", (unsigned)ptr%8);
}
return 0;
}

  上面的这段代码可以设置对齐方式,当然第二个参数(也就是和什么对齐)必须是2的幂次,而且是void*指针大小的倍数。在默认的情况下用malloc等分配的时候,如果是32位系统上总是会对齐8字节,64位系统上则对齐16字节。

struct a{
int x;
char y1, y2, y3, y4;
};
struct b{
char y1, y2;
int x;
char y3, y4;
};
int main(){
printf("%d\n", sizeof(struct a));
printf("%d\n", sizeof(struct b));
return 0;
}

  如果不是标准类型,比如结构对齐的就是该结构中的最大者。结构还会有填充的问题,如果一个char后面跟着一个int,那么char后面会有3个字节来对齐。通过这点可以发现通过改变结构中的数据的顺序,结构的大小也会变化。上面这个例子中,第一个的大小是8,第二个的大小是12,如果需要暴力取得一个结构中变量的位置这点还是需要注意下的(挺有意思的吧)。

int main(){
void *p;
p = mmap(NULL, 512*1024, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if(p == MAP_FAILED){
printf("mmap error.\n");
}
    munmap(p, 512*1024);
return 0;
}

  上面是一个匿名映射的例子,分配了512KB的内存。这样做的好处是可以为这块内存加上各种权限,然后还不会出现碎片,因为是整块分配的。但是缺陷页正在于此,整块分配的时候必然会导致内存的浪费。如果没有MAP_ANONYMOUS这个选项的时候解决办法也很简单,就像映射普通文件那样映射/dev/zero,这个和匿名映射一样会把所有位填充为0。其实这里的0只是看到0而已,并没有填充。简单的Copy-On-Write。

int main(){
void *ptr = malloc(10000);
malloc_stats(); // get malloc stats.
free(ptr);

struct mallinfo m;
m = mallinfo();
printf("data segment malloc used = %d\n", m.arena);
printf("free chunks = %d\n", m.ordblks);
printf("fast bins   = %d\n", m.smblks);
printf("anonymous mapping    = %d\n", m.hblks);
printf("anonymous size = %d\n", m.hblkhd);
printf("max total alloc size = %d\n", m.usmblks);
printf("avaliable fast bins = %d\n", m.fsmblks);
printf("total alloc space = %d\n", m.uordblks);
printf("avaliable chunks = %d\n", m.fordblks);
printf("trimmable = %d\n", m.keepcost);

return 0;
}

  对于程序对内存的使用情况可以通过上面例子中的两个函数取到,在调试程序的时候有必要观察一下程序所使用内存的多少,而不是仅仅去关注运行的效率,感觉这个控制CPU使用率是一个意思。

char* test(char* str){
char *name = (char*)alloca(strlen(str)+1);
strcpy(name, str);
printf("%s\n", name);
return name;
}

int main(){
char str[20] = "abcaba";
char *name = test(str);
printf("%s\n", name);
return 0;
}

  动态分配的所有机制都是使用堆或内存映射来取得动态内存的。在用alloca分配内存的时候有一个比较有意思的地方,在函数返回的时候所分配的内存会被释放,通过上面的这段代码可用进行验证。自动释放的原因是函数返回时的stack unwind操作。

int main(){
char *name = strdupa("abc");
printf("%s\n", name);
name = strndupa("abcdefghijk", 10);
printf("%s\n", name);
return 0;
}

  因为在堆栈上分配的时候会快一些,所以有些函数会通过这个来提高一些效率,比如上面的两个例子。但是如果滥用的话可能导致堆栈溢出,所以并不是所有的环境都允许你用这两个函数。与alloca相同,在函数返回的时候会释放内存。

int main(){
char str[10];
// zeroed.
bzero(str, sizeof(str));
printf("%s\n", str);

// set memory.
memset(str, 'a', sizeof(str));
printf("%s\n", str);

// cmpare.
int ret = memcmp("aaaaaaaaaa", str, 5);
printf("%d\n", ret);

// move.
str[0] = 'b';
memmove(str+1, str, 1);
printf("%s\n", str);

// can't overlap.
memcpy(str+2, str, 2);
printf("%s\n", str);

// copy by char.
memccpy(str+5, str, 'a', 10);
printf("%s\n", str);

return 0;
}

  前面介绍的是如何取得内存,而上面的例子是如何操作分配到的内存数据的函数。在这些拷贝函数的时候需要注意源地址和目的地址是否会有重叠的部分,如果有的话结果可能会变的不可预知。而memset在不同的平台上面的表现也是不同的。

    char *ans = (char*)memchr(str, 'a', sizeof(str));
printf("%s\n", ans);
ans = (char*)memrchr(str, 'a', sizeof(str));
printf("%s\n", ans);

// just simple revolve.
memfrob(str, sizeof(str));
printf("%s\n", str);

  memchr可以用来找到内存中特定的一个值的位置(从头开始找),如果你想找到所有的也很简单,只需要在循环中不断更新指针就好了。memrchr的不同之处是从给定内存的结尾处开始找。memfrob对给定的内存进行一次简单的旋转,两次调用就会复原。

原文地址:https://www.cnblogs.com/ggzwtj/p/2230702.html