iOS开发学习第十一课——动态内存分配


//
动态内存分配 (Dynamic Memory Allocation) 就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法 .
// 它是相对重要的一节课 , 如果能够将它学好 , 那么对于今后学习 OC 的内存管理会非常有帮助 .
//1. 内存区划分
// 内存区域划分 ( 地址由高到低 )
//(1) 栈区
//(2) 堆区
//(3) 静态区 ( 全局区 )
//(4) 常量区
//(5) 代码区

//(1) 栈区
// 函数中定义的局部变量由系统在栈区分配内存
// 开发人员不用关心系统如何分配内存 , 完全由系统分配和释放内存
// 栈区内的数据以栈的形式存储
// 栈的特点 : 先进后出

//(2) 静态全局区
// 全局变量和用 static 修饰的变量都由系统在静态全局存放
// 静态全局区所分配的内存 , 直到程序结束才能被释放

// static 修饰的变量
//a. 存放在静态全局区
//b. 只能被初始化一次
//c. 如果没有赋初值 , 默认为 0

//(3) 常量区
// 常量 ('a', 123, 34.5, "iPhone")( 字符型 , 整型 , 实型 ) 都被存到常量区
// 常量区由系统管理

// 常量区的数据 , 是只读状态 , 不能修改

//(4) 代码区
// 程序中的函数和语句会被编译成 CPU 指令存在代码区
// 代码区由系统控制

//(5) 堆区
// 由开发人员手动申请 , 手动控制

// 申请内存函数
//void *malloc(size_t)
// 返回值类型 :void * ( 泛类型 ), 泛型指针可以转换成任意一种指针类型
// 函数名 :malloc
// 参数类型 :size_t, unsigned int( 无符号整型 )
// 参数含义 : 多少个字节

// 堆区地址存放在栈区

int  main( int  argc,  const  char  * argv[]) {
//static  静态标示符 , 修饰的变量只会初始化一次
     int  a = 10;
   
  for  ( int  i = 0; i < 10; i++) {
       
  static  int  b = 20;
        b += a;
       
  printf ( "b = %d  " ,b);
    }
   
   
  // 常量和变量
   
  // 常量 : 程序运行期间 , 常量里面的内容不可以被改变 , 只能被访问
   
  // 变量 : 程序运行期间只可以被重新赋值被改变的量
   
   
  //const   如果在变量类型之前加上关键字 const, 那么此时变量被当做常量使用
   
  const  int  c = 30;
    c = 60;
  // const 修饰之后的 c 此时不能被再重新赋值了
   
  printf ( "c = %d\n" ,c);
   
   
  /* 复习
    
  不同的数据类型所存储的不同的内容
     int , short , long     
  存储整数 , 整形类型
     float , double         
  存储小数 , 浮点型类型
     char          
  存储字符或字符串 , 字符型类型
     BOOL/bool     
  存储 YES(1) NO(0), 布尔类型
    
  假设定义一个结构体类型为 Stu 的结构体 :
     Stu           
  存储结构体成员变量  , 结构体类型
     int * , char * , float * , double * ,short * ,long *
  存储相应类型的地址    指针类型
     Stu *         
  存储结构体变量指针地址
     */

   
   
  //const 关键字    公司笔试题
   
  //const 关键字修饰的是 *p, 如何看修饰的变量 , 先忽略数据类型 , 再看修饰的变量是谁 , 修饰谁谁就不能被改变 , 但是可以改变 p ( 地址 )
   
   
  int  x = 10,y = 30;
   
  //1.
   
  const  int  *p = &y;
    p = &x;
//p 可以被改变
    *p = 33;
//*p 被修饰 , 所以不能被改变
   
   
  //2. 和第一种情况一样
   
  int  const  *p = &y; // 报错
   
   
  //3.const  修饰 p( 地址 ), 那么指针就不能重新指向 ( 地址不能重新赋值 ) p 不可改变 ,*p 可变
   
  int  * const  p = &y;
    *p = 60;
    p = &x;
// 已被修饰不可改变
   
   
  //4. 里面的 const  修饰了 p, 外面的 const 修饰 *p
   
  const  int  * const  p = &y;
    *p = 40;
// 报错
    p = 50;
// 报错
   
   
   
  /*
   
  内存的五大分区 :
     1.
栈区 : 主要存放局部变量或者数组 , 定义在函数内部 , 当函数调用时开辟空间 , 函数结束的时候释放内存 ; 内存由系统分配和管理
     2.
堆区 : 由开发人员手动开辟的内存区间 , 手动释放
     3.
静态区 / 全局区 : 用于存储全局变量或者静态变量 ( 程序运行时开辟空间 , 程序结束时则释放空间 , 也是由系统管理内存 )
     4.
常量区 : 存储各种类型的常量  只读
     5.
代码区 : 存放程序编译之后形成的 CPU 指令 , 告诉计算机存放在代码区内
     */

   
   
  // 如何在空间上存放数据 :
   
  // 在堆区开辟空间
   
   
  //malloc  手动开辟 n 个字符的空间 , 返回的是一个泛类型的地址 (void *  泛类型 : 所有的指针类型 )
   
  // 如果申请的空间为 8 个字节的话
   
  /*
     char *
  可以存储 8 个字符
     int * 
  可以存储两个整型数
     float *
  可以存储两个浮点数
     short *
  可以存储 4 个短整型
     long * 
  可以存储 1 个长整型
     */

   
   
  char  *p = malloc(8); // 在堆区开辟 8 个字节的空间
   
  strcpy (p,  "iphone6" );
   
  printf ( "%s\n" ,p);
   
  printf ( "%s\n" ,p + 2);
   
  printf ( "%c\n" ,*(p + 3));
   
  free (p); // 手动释放 , 释放的是我们手动开辟的空间 , 也就是把空间还给系统 ( : 不可过度释放 , 即开辟一次释放一次 ,malloc 的次数  = free 的次数 )
    p =
  NULL ; // 把空间还给系统之后 , 需要将地址置空
   
   
  /*
    
  堆区里比较常见的一些错误 :
     1.
野指针    访问一块我们不能管理的内训空间 , 也就是没有获得空间所有权 ( 即此时这块空间不在堆区 )
             
  解决方案 : 再将我们手动开辟的空间还给系统之后 , 记得将地址置空  NULL
     2.
过度释放 : 对同一片内存 free 了多次
     3.
内存泄露 : 没有对我们手动开辟的内存进行及时释放 , 就会造成内存堆积 , 造成隐患 ; 记得手动开辟空间之后一定要 free 一次
     */

   
   
  // 在堆区开辟一块空间 , 存储 5 个整数 , 随机出 [10 30] 的值并输出
   
  int  *a = malloc( sizeof ( int ) * 5); // 开辟存储 5 个整数的空间
   
  for  ( int  i = 0; i < 5 ; i++) {
        *(a + i) = arc4random() % (30 - 10 + 1) + 10;
        printf(
"%d  " ,*(a + i));
    }
   
  free (a); // 释放内存    防止内存泄露
    a =
  NULL ; // 置空    防止野指针
   
   
  // 在堆区开辟一块空间 , 存储 10 个整数 , 随机出 [10 30] 的值并升序输出
   
  int  *b =  malloc ( sizeof ( int ) * 10);
   
  for  ( int  i = 0; i < 10; i++) {
        *(b + i) =
  arc4random () % (30 - 10 + 1) + 10;
    }
   
  for  ( int  i = 0; i < 10 - 1; i++) {
       
  for  ( int  j = 0 ; j < 10 - 1 - i; j++) {
           
  if  (*(b + j) > *(b + j + 1)) {
               
  int  temp = *(b + j);
                *(b + j) = *(b + j + 1);
                *(b + j + 1) = temp;
            }
        }
       
    }
   
  printf ( "\n 排序好的数组为 :" );
   
  for  ( int  i = 0; i < 10; i++) {
       
  printf ( "%d  " ,*(b + i));
    }
   
   
   
   
   
  char  symbol[] =  "Y8ou C6an3 Y1ou U2p,No3 Can8 N6o B B9!" ;
   
  int  count = 0; // 用来记录数字的个数
   
  int  i = 0;
   
  while  (symbol[i] != 0) {
       
  if  (symbol[i] >= '0' && symbol[i] <= '9') {
            count++;
// 如果是数字的话 , count+1
        }
        i++;
// 循环增量
    }
   
  // 根据数字的个数来动态在堆区开辟相应字节大小字节数的空间 , 因为字符串结束标志是 '\0', 所以我们开辟 count + 1  个字节数
   
  char  *c = malloc(count + 1);
   
  int  j = 0,k = 0;
   
  while  (symbol[j] != '\0') {
       
  if  (symbol[j]  >= '0' && symbol[j] <= '9') {
            *(c + k) = symbol[j];
            k++;
// 设置 k 的原因是每当 if 语句成立一次数组地址就可以往高位平移 +1
        }
        j++;
    }
    *(c + k) = '\0';
// 将堆区最后一个空间设置为 '\0'
   
  printf ( "%s\n" ,c);
   
  // 释放内存
   
  free (c);
    c =
  NULL ;
   
   
   
  //calloc   堆区开辟空间 ,calloc(m, n);   分配  m  size = n  字节大小的空间
   
  int  *p1 =  calloc (8, 4); // 8 4 个字节大小的整型数 , 共占 32 个字节
   
  //realloc  工作原理 : 先以原先开辟的空间为基准 , 如果发现之前开辟的空间剩余不足 , 重新分配新的空间 , 同时 realloc 集成了对之前开辟空间的 free 操作
   
  char  *p2 =  malloc (8);
   
  // 假设此时发现开辟的空间不够使用 , 则需要重新分配一个更大的内存
   
  char  *p3 =  realloc (p2, 20); // p2 free 掉了 , 重新分配了 20 个字节大小
   
  
// 动态内存的其他函数

//(1)void *calloc(n, size)
// malloc 一样 , 都是申请内存 , 但是 calloc 申请内存后 , 会对内存中的内容做清空的操作 ; 由于多了一步清空操作 , 效率要比 malloc
//n: 个数
//size: 字节数
//calloc 申请的内存字节数  = n * size

//(2)void realloc(p, size)
// 从给定的位置 p, 开始重新申请 size 个字节
// 从地址 p 向后申请 size 个字节 , 如果后面可用的字节够的话 , 就申请内存 , 并返回当前的指针 p; 如果不够的话 , 会再去内存中找一块连续的空间 , 如果找到 , 就返回这一块连续空间的首地址 , 并且把之前所占用的内存释放

//(3)void memset(p, c, n)
// 从指针 p 的位置开始初始化 n 个字节的内容 , 把内容改成 c

//(4)void *memcpy(void *dest, const void *source, n)
// 从指针 source 的位置开始 , 向指针 dest 位置 , 拷贝 n 个字节的内容
char  str1[] =  "ABC" ;
char  str2[] =  "123" ;
memcpy(str1, str2, 2);
printf(
"%s\n" , str1);

//(5)memcmp(p1, p2, n)
// 比较 p1 p2 指向的内存所存放的内容是否相同 , 比较 n 个字节 , 相同返回 0, 不同返回差值
int  *a1 =  malloc (4);
*a1 = 1;
int  *a2 =  malloc (4);
*a2 = 3;
int  result = memcmp(a1, a2, 1);
printf("%d\n", result);
return 0;
}

作业:
int  main( int  argc,  const  char  * argv[]) {
   
  //1. 输入 3 个单词 , 动态分配内存保存单词 , 并在最后输出
   
  // 提示 : 定义一个指针数组保存数据  char * words[3] = {0};
   
  // 存储 3 个单词 , 就意味着需要在堆区开辟 3 次空间 ,  每次开辟空间都会返回对应的首地址 .  所以为了存储 3 个地址 , 定义指针数组存储 .
   
  char  * words[3] = { "Wo" , "Ai" , "Ni" };
   
  for  ( int  i = 0; i < 3; i++) {
       
  char  *p1 =  malloc ( sizeof (words[3]));
       
  char  *p1i =  strcpy (p1, words[i]);
       
  printf ( "%s\n" ,p1i);
       
  free (p1i);
        p1 =
  NULL ;
    }
   
   
  //2. (***) 已知一个数组  20  个元素 ( 随机  1    100  之间包含  1    100), 求大于平均数的元素个数 , 并动态生成一个新数组保存 ( 提示 :malloc    20  个元素保存 )
   
  int  a[20] = {0};
   
  int  sum = 0;
   
  printf ( " 随机出的数字为 :\n" );
   
  for  ( int  i = 0; i < 20; i++) {
        a[i] =
  arc4random () % (100 - 1 + 1) + 1;
        sum += a[i];
       
  printf ( "%d  " ,a[i]);
    }

   
  int  count = 0; // 定义大于平均数的元素的个数 count
   
  int  avg = 0;
    avg = sum / 20;
   
  for  ( int  i = 0; i < 20; i++) {
       
  if  (a[i] > avg) {
            count++;
// 遍历 20 个元素 , 只要 if 语句成立一次 , 元素个数 count 就增加一个
        }
    }
   
  printf ( " 大于平均数的个数为 :%d   , 分别为 :\n" ,count);
   
   
  int  *p =  malloc (count * 4); // 定义一个指针类型 *p 来存储大于平均数的元素的地址 , 因为是 int 类型所以要 count * 4
   
  int  k = 0;
   
  for  ( int  i = 0; i < 20; i++) {
       
  if  (a[i] > avg) {
            k++;
// 设置 k 的原因是每当 if 语句成立一次数组地址就可以往高位平移 +1
            p[k - 1] = a[i];
           
  printf ( "%d  " ,p[k - 1]);
        }
    }
   
  free (p);
    p =
  NULL ;
   
   
   
  return  0;
}

  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值