C语言程序在内存中的分布:
#include <stdio.h> char bss[1024*1024]; int main() { return 0;}
现在来看看程序的可执行文件大小
定义 char bss[1024*1024]={0};也是这个结果
可以看到 bss的大小并没有1MB 说明未初始化的全局变量不占程序文件的存储空间
#include<stdio.h> char data[1024*1024]={1}; int main() {return 0;}
运行结果:
char* p = "12345"
如何使用堆内存:
void *malloc(size_t size);
void free(void *ptr);
举个例子:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> int main() { printf("%d\n",getpid()); //得到进程id char* p = malloc(sizeof(char)*10); char str1[7] = "123avbd"; char* str2 = "101"; printf("&p:%p\np:%p\n",&p,p); printf("str1:%p\nstr2:%p\n",str1,str2); while(1){}; //死循环不让程序结束 free(p); return 0; }
让我们看一下结果:
其他的有关内存的操作函数:
申请适合数组使用的内存,size指的是一次申请多少个字节的内存,nmemb指的是申请多少次size,申请到的内存会被设置为0
void *calloc(size_t nmemb, size_t size);
调整内存的大小,可以把ptr指向的内存,变大或变小。如果内存被调小,数据不会立即删除,会一直存在,直到被别人覆盖;如果内存调大,如果后面没有被使用,则在原来的基础上调大,如有人使用,会重新开辟一块内存,再把原来的数据复制过去。
void *realloc(void *ptr, size_t size);
内存操作辅助函数:
void bzero(void *s, size_t n);
void *memset(void *s, int c, size_t n);
碎片问题:对于堆来讲,频繁的malloc()势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低,也就是说当如果没有足够大小的空间,malloc()申请内存可能会失败。
虚拟内存:
内存映射:
内存分配:
内存越界:
我们看个例子:
#include<stdio.h> #include<string.h> int main() { char str[10]={}; char arr[10]="0123456789"; printf("str:%p\narr:%p\n",str,arr); puts(str); puts(arr); strcpy(str,"abcdef123456"); puts(str); puts(arr); printf("len=%d\n",strlen(str)); printf("size=%d\n",sizeof(str)/sizeof(str[0])); return 0; }
但是这串字符串的长度已经大于str定义的长度10了,可是编译结果却没有报错 看下编译结果:
这是为什么呢?
内存越界访问造成的后果非常严重,是程序稳定性的致命威胁之一。更麻烦的是,它造成的后果是随机的,表现出来的症状和时机也是随机的,让BUG的现象和本质看似没有什么联系,这给BUG的定位带来极大的困难。
对于内存越界,比较保险的方法还是在编程时就小心,特别是对于外部传入的参数要仔细检查。