动态内存分配


存储区的划分:
在计算机的内存中,可以分成5个区,每个区都有着不一样的效果。按内存编号从小到大的顺序,分别是:
(1)、代码区: 
计算机将我们写的代码通过二进制转换后,放进了这个代码区。
(2)、常量区:
在我们写代码时,所有的常量,都放在常量区,常量区的所有值都是可读不可写的。也就是说,常量区的所有值都是不能改变的。若强行对其赋值,则在运行的时候,直接导致程序崩溃。
常用的一个关键字:const。
const可以把一个变量声明成常量:const int i = 10; 此时,就应该把num1当成常量来使用。此时再给num1赋值,会报错。如果用指针取到num1的地址,然后再用 * 取值后给其赋其他值,虽然可以成功赋值,但是此时的num1已经不是原来的num1,而是原来的num1用const修饰后,直接转移到了常量区,用指针取地址后,取到的是num1在栈区的地址,改动的也是原来栈区内的num1。也就说,有两个num1,一个在常量区,一个在栈区。因此,为了保证安全,不能这样写。
//    常量区
    
//    程序中出现的所有常量,保存到常量区
//    常量区的所有值:只读不可写    
//    const
//    可以将变量声明成常量    
//    int num1 = 10;
//    num1 = 20;//可以改值
//    //const的意思:只读,num1的值不能改变了。
//    const int num1 = 10;//定义成常量,要当做常量使用。
//    num1 = 20;//报错
    //不要这样写。这样写会破坏安全性。
//    int *pi = &num1;
//    *pi = 20;
    //num1的值在常量区有一份,在栈区有一份。
//    printf("%d\n",num1);//10,这个num1已经放到了常量区。
//    printf("%d\n",*pi);//20,这个值是num1的值,但是这个num1的位置是在栈区。
    
    //const 放在不用的位置,效果不一样(作业)
//    const int *p = NULL;
3)、静态区:
用来存放静态值的地方。在代码中,用static来标记。用static来标记的语句,在整个程序运行过程,只进行一次初始化,不论程序是否循环到该语句,都不再执行。如果初始化的时候,没有给初始值,系统会自动给0。静态区的东西常驻内存,直到 程序运行结束才会释放。全局变量也放在静态区,也叫全局区。这个时候,其他文件可以用到这个全局变量。此时,如果我们在这个全局变量加上static修饰,这时候的static会限制作用域,限制其他文件不能用。static 依然受到作用域的约束。
#import <Foundation/Foundation.h> 
//也在静态区,也叫做全局区
//int n2 = 10;
//如果全局变量加了static 会限制作用域,其他文件不能使用。
static int n2 =10;

//静态区的变量,可以不做初始化,自动清0.
//可以计数使用
int test(){
    static int count = 0;
    return  ++ count ;
}

int main(int argc, const char * argv[]){
 //静态区
//    static int num = 10;
//    printf("%d\n",num);
//    
//    num = 20;
//    
//    printf("%d\n",num);
    
//    while (1) {
//        int i = 0;
//        printf("%d\n",i++);//全部打印0
//    }
//    while (1) {
//        //静态变量只被初始化一次。
//        static int i = 0;//这条语句执行过一次后,这条语句就失效了,不再执行
        printf("%d\n",i++);//全部打印0
    }
    //依然受作用域的约束
    i = 10;//报错    
    printf("%d\n",test());//1
    printf("%d\n",test());//2
    return 0;
}
(4)、堆区:
用malloc等函数分配的内存。
堆区是唯一一个由我们自己可以掌管内存大小,并对内存回收的区。即:⼿动分配,⼿动释放。
堆,是内存中,最大的一块。因此,大部分情况下,类型的内存申请是在堆中完成的。
malloc函数:动态分配内存函数:void *malloc();
void * 表示任意指针类型。也就是返回值可以是任意类型的指针。
括号内的参数,意思是分配多少字节的大小。
    //参数的意思是:写多少,就分配几个字节的内存。
    //void * 任意指针类型
//    char *p1 = malloc(1);//在堆中分配了一个字节内存
//    char *p2 = malloc(10);
    
//    int *ip = malloc(sizeof(int));
//    *ip = 20;
//    printf("%d\n",*ip);//20
一些例子:
//    //练习
//    //数组:20个元素,都是int类型的。
//    //在堆区分配空间
//    //使用随机数填值。[0~30]后打印
//    
//    int *arr = malloc(sizeof(int) * 20);//在堆中分配了20个int类型的内存。用arr指针来找到在堆中的位置。
//    
//    for (int i =0; i < 20; i ++) {
//        *(arr + i) = arc4random() % 30;
//    }
//    
//    for (int i = 0; i < 20 ; i ++) {
//        printf("第 %d 个元素:%d\n",(i+1),arr[i]);
//    }
//    //申请的内存,如果不手动释放,会一直存在。造成内存泄露。
//    //释放内存
//    free(arr);

free()函数:与malloc等动态分配内存的函数使用,对分配号的内存进行释放,回收内存。但是这里的释放,是标记删除。意思是对编号进行释放,但是内容仍然存在,只是告诉系统,这个内存已经可以回收,可以重新分配给其他人用。
注意:不能像下面那样写
    //切记,不能这么写,是错误的
    //原因是:先分配了6个字节,还没有释放,就去申请另外的空间,这样导致6个空间的内存不能释放。
//    int *p4 = malloc(6);
//    p4 = malloc(10);
例子:
    //练习:
//    //找出字符串中的所有数字,用malloc来存放这些数字
//    char *str = "a1b2c3d4";
//    //统计数字
//    int count = 0;
//    //循环增量
//    int i = 0;
//    //收集数字
//    char tempArr[100] = {0};
//   
//    while (*(str+i) != '\0') {
//        if (str[i] >= '0' && str[i] <='9') {
//            tempArr[count] = str[i];
//            count ++;
//        }
//        i ++;
//    }
//    //加上'\0'表示结束
//    tempArr[count] = '\0';
//    //分配空间
//    char *arr = malloc(sizeof(char) * (count+1));
//    //赋值:把栈区的数据,拷贝到堆区。
//    strcpy(arr, tempArr);
//    printf("%s\n",arr);//1234
//    //释放
//    free(arr);//释放的意思是标记删除
//    
//    printf("%s\n",arr);//仍然为1234
//    
//    arr = NULL;
//    
//    printf("%s\n",arr);//打印 (null)

由于free是标记删除,并不对内存里的内容进行清除,此时,还需要将我们的指针指向NULL:arr = NULL;
练习:

//    char *words[3] = {0};
//    for (int i = 0; i < 3; i ++) {
//        char temp[20] = {0};
//        printf("输入单词,回车结束:\n");
//        scanf("%s",temp);
//        words[i] = malloc(sizeof(char) * (strlen(temp)+1));
//        strcpy(words[i], temp);
//    }
//    
//    for (int i = 0; i < 3; i++) {
//         printf("%s\n",words[i]);
//    }
////    free(words); 三个地址要逐个释放。其实words是数组名,数组名就在栈区,不能用free来释放。words[i]的值才是在堆中开辟的空间的地址。能用free释放
//    for (int i = 0 ; i < 3; i ++) {
//        free(words[i]);
//        words[i] = NULL;
//    }

calloc函数:与malloc差不多,只是有两个参数,第一个参数是告诉有多少个后面的类型,第二个参数是告诉有多少字节。那么全部分配下来的内存为:第一个参数 * 第二个参数。calloc在分配好内存后
    //calloc
    
//    //第一个参数:有多少个
//    //第二个参数:有多少字节
//    //全部清0
//    //使用规则和malloc分配的内存一样
//    char *p2 = calloc(10, sizeof(int));//有10个int类型,4字节的空间,即分配了10 * 4 = 40个字节的空间,并全部清0
//    char * p1 = calloc(10, 4);//有10个4字节的空间,即分配了10 * 4 = 40个字节的空间,并全部清0
//    
//    for (int i = 0; i < 40 ; i ++) {
//        printf("%d",p1[i]);//0000000000000000000000000000000000000000
//    }
//    free(p1);
//    p1 = NULL;
//    free(p2);
//    p2 = NULL;

realloc 函数:改变一块内存。
    //realloc
    
//    char *p2 = malloc(100);
//    
//    //第一个参数:你要改变哪一块的内存
//    //第二个参数:要变成多大
//    char *p3 = realloc(p2, 120);
//    
//    free(p3);
//    p2=NULL;
//    p3 = NULL;

内存操作函数:
(1)、memset:设置内存给定一些内容:

    //memset内存设置
//    char *p3 = malloc(10);
//    //第一个参数:设置哪块内存
//    //第二个参数:设置成什么
//    //第三个参数:设置多少个字节
//    memset(p3, 2, 10);//设置p3所指向的内存,把里边的东西设置成2,设置10个字节。
//    for (int i = 0; i < 10; i ++) {
//        printf("%d",p3[i]);//2222222222
//    }2)、memcpy:内存拷贝函数
    //memcpy内存拷贝
//    char *p1 = malloc(10);
//    char *p2 = malloc(10);
//    
//    memset(p1, 5, 10);
//    //第一个参数:拷贝到哪
//    //第二个参数:从哪拷贝
//    //第三个参数:拷贝多少字节
//    memcpy(p2, p1, 10);//拷贝到p2,从p1拷贝过来,拷贝10个字节。
//    for (int i = 0; i < 10; i ++) {
//        printf("%d",p2[i]);//5555555555
//    }
//    free(p1);
//    p1 = NULL;
//    free(p2);
//    p2 = NULL;3)、memcmp: 内存比较函数
    //memcmp内存比较
    
//    char *p1 = malloc(10);
//    memset(p1, 2, 10);
//    
//    char *p2 = malloc(10);
//    
//    memset(p2, 3, 10);
//    //第一个参数:第一块内存
//    //第二个参数:第二块内存
//    //第三个参数:比较多少个字节
//    //内存比较和字符串比较一样,找到第一个不相等的字节,求差值。
//    //如果所有字节逗相等,返回0。
//    //返回 >0 ,第一个比第二个大,
//    //返回 <0 ,第一个比第二个小。
//    int n = memcmp(p1, p2, 10);
//    printf("%d",n);//-1
5)、栈区
栈区的东西,有个特性,就是在栈内,数据是先进后出的。
在函数里定义的变量,基本上都是在栈区的,因此,一旦函数执行完毕,所有栈释放(也叫出栈),只是删除编号(地址),并没有清除数据,这时候,这些栈的使用权回归系统所有,系统可以再次分配,对里边的值重写。若是没有分配,那么在下一次遇到一个结构,类型一样的函数,里边的变量如果不给初值,那么这个变量就会使用上次出栈后遗留在栈中的数据。
#import <Foundation/Foundation.h>

int a(){
    int num1 = 100;
    return num1;
}
int b(){
    int num2 ;
    return num2;
}
int c(){
    int num3 ;
    return num3;
}
int main(int argc, const char * argv[])
{ //    2015-04-08 09:36:38 北京
    
//    栈区
    
    
//    int n1 = 10;
//    int n2 = 20;// 
//    内存不具有清空的功能
//    
//    //每一个函数都有一个叫做 栈帧 的东西
//    //由于a,b两个函数结构相同,使用内存情况相同,b其实使用的是a的内存,所以原来的值被使用。
//    a();
//    printf("%d\n",b());//打印100
//    //在c函数使用之前,调用了printf,导致b残留的数据被覆盖,原来的值无法使用。
//    printf("%d\n",c());//打印0
//    int n3 = 30;
    return 0;
}
一个错误的例子:
#import <Foundation/Foundation.h>
////错误
//char *backStr(){//    
//    //str在backStr的栈内,一旦函数执行完毕,所有的栈内释放(也叫出栈),这时候,你的str字符数组的内存使用权回归系统,系统可以再分配。这个时候,原来的数据会被覆盖,导致无法使用。
//    char str[] = "iPhone";
//    return str ;
//}

int main(int argc, const char * argv[])
{ //    char *ps = backStr();
//    printf("%s\n",ps);    
    return 0;
}
此时,应该想到,跨作用域改值的只有指针作为函数参数的时候,才能实现。所以重新定义函数:
#import <Foundation/Foundation.h>
void backString(char str[],int length){
    char s[] = "iPhone";
    if (strlen(s ) > length) {
        strcpy(str , s );
    }else{
        return;//经常用来终止函数
    }
}

int main(int argc, const char * argv[])
{ 
    char st[20] = {0};
    backString(st,20);    
    printf("%s",st );
    return 0;
}


29、链表
#import <Foundation/Foundation.h>
//节点结构体
typedef struct Node{
    int data;//数据
    struct Node *next;//节点
}Node ;

//添加节点
void addNode(Node *n,int num){
    //临时指针
    Node *temp = n;
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = malloc(sizeof(Node));
    temp->next->data = num;
    temp->next->next = NULL;
}

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

    // 2015-04-08 17:19:41 北京
    Node *first = malloc(sizeof(Node));
    first ->data = 0;
    first ->next = NULL;
    
    addNode(first,1);
    addNode(first,2);
    addNode(first,5);
    
    return 0;
}

 

posted @ 2016-05-09 19:45  胡一波  阅读(104)  评论(0编辑  收藏  举报