十八、内存的五大区

2020-11-02  本文已影响0人  Mjs

在程序编译的时候内存一般分为以下几个部分

栈区

优点是快速高效,缺点时有限制,数据不灵活。[先进后出]

MAC只有8M

int main()
{
    int *p1= alloca(4);
    *p1 = 123;
    printf("%d,%p,%p\n",*p1,p1, &p1);
    return 0;
}
·················
123,0x7ffeefbff420,0x7ffeefbff430
·················

所以p1和p1指向的值也是放在栈中的

堆区

int main()
{
    int *p1=malloc(4);//申请4字节的空间
    *p1=123;// 该空间赋值123
    printf("%p:%d\n",p1,*p1);
    printf("%p\n",&p1);
    free(p1);
    return 0;
}
···················
0x10380f1c0:123
0x7ffeefbff430
···················

当需要访问堆中内存时,一般需要先通过对象读取到栈区的指针地址,然后通过指针地址访问堆区
堆区是由系统通过链表管理维护的,所有应用程序共享的一块内存空间。包括内存+虚拟内存(磁盘缓存)
程序运行时堆区的内部操作,以及引发内存泄漏的原因:
创建一个新的对象时,对象p指针存放在栈区,p将指向在堆区开辟的一块存储空间Person
在程序结束之前,p对象必须release,不然系统不知道释放堆区的Person内存。
如果p对象没有release,只是p=nil; 就是p指针指向了堆区地址为0的地方,那么原来的Person永远无法再次访问,而且也无法释放掉。
堆是所有程序共享的内存,当N个这样的内存得不到释放,堆区会被挤爆,程序立马瘫痪。这就是内存泄漏。

全局区/静态区

全局区是编译时分配的内存空间,在iOS中一般以0x1开头

static int a;
int c;
void test(void)
{
    static int b=1;
    b++;
    printf("b:%p: %d\n", &b , b);
}
int main()
{
    printf("a: %p: %d\n", &a , a);
    printf("c: %p: %d\n", &c , c);
    for(uint8_t i=0;i<5;i++)
    {
        test();
    }
    return 0;
}
···············
a: 0x100002014: 0
c: 0x100002018: 0
b:0x100002010: 2
b:0x100002010: 3
b:0x100002010: 4
b:0x100002010: 5
b:0x100002010: 6
Program ended with exit code: 0
···············

a是静态全局变量,b是静态局部变量,c是全局变量,他们都存放在静态区;a和c并未初始化,打印出来的都是0,说明比那一起自动把他们初始化为0;b在for循环中初始化了5次,但实际上b仅初始化一次,后面每次调用b都是上次的值,且b的地址是不变的,编译器只会为第一次初始化的b分配内存,后面4次初始化是无效的。

常量区

常量区是编译时分配的内存空间,在iOS中一般以0x1开头,在程序结束后由系统释放
char *p="hello“;
以这种方法初始化的字符串是常量字符串,所以不能修改。

int main(int argc, const char * argv[]) {
    char* p ="abcdef";
    printf("%p: %s\n", p , p);//打印指针p的地址和p指向的字符串内容
    p="qwedma";
    printf("%p: %s\n", p , p);//打印指针p的地址和p指向的字符串内容
    p[0]='1';                     //尝试把p指向的第一个字符q修改为1
    printf("%p: %s\n", p , p);//打印指针p的地址和p指向的字符串内容
    return 0;
}
···········
0x100000f9a: abcdef
0x100000fab: qwedma
(lldb) 
···········

可以看到,p的值改变,地址也发生了改变。在尝试把p指向的第一个字符q修改为1时,系统崩溃了,常量区的内容不可以改变。
继续看定义
char p[]="hello“;在这里把p定义成一个字符数组


int main(int argc, const char * argv[]) {
    char p[] ="abcdef";
    printf("%p: %s\n", p , p);//打印指针p的地址和p指向的字符串内容
    p[0]= '1';                     //尝试把p指向的第一个字符q修改为1
    printf("%p: %s\n", p , p);//打印指针p的地址和p指向的字符串内容
    return 0;
}

···········
0x7ffeefbff489: abcdef
0x7ffeefbff489: 1bcdef
Program ended with exit code: 0
···········

这个p数组是存放在栈区的,然后再把字符串常量”abcd”拷贝到栈区的str数组内,那么此时的str是可以修改的。str是指向栈区的地址:0x0x7ffeefbff489,且指向的内容可以被修改,第一个字符a被修改为e。

代码区

int main(int argc, const char * argv[]) {

        printf("main:%p\n",main);//打印main函数的存放地址

    return 0;
}
..........
main:0x100000eb0
..........

可以看到main函数确实存放在0x0x100000eb0这片地址区域,在代码区中。

上一篇 下一篇

猜你喜欢

热点阅读