C内存管理

内存管理主要掌握生命周期和内存布局,如果变量没有释放就可以用,释放了就不能用了。

一.作用域和声明周期

  1. 普通的局部变量

    1. 在{}内部定义的变量就是局部变量。
    2. 只有执行到定义变量的这个语句,系统才会给这个变量分配空间。
    3. 当离开{}时自动释放。
    4. 作用域在当前{}内。
    5. 加不加auto关键字都是一样的。
    6. 不初始化则值为随机数。
  2. static局部变量

    1. 在{}内部定义的变量就是局部变量。
    2. 在编译阶段分配空间。
    3. 离开{}不会释放,程序结束才释放。
    4. 作用域在当前{}内。
    5. 不初始化则值为零。
    6. 初始化语句只会执行一次,但可以赋值多次。
    7. 只能用常量初始化。
  3. 普通全局变量

    1. 在函数外面定义的为全局变量。

    2. 如果使用变量前找不到定义,需要声明。

      #include <stdio.h>
      int main() {
          extern int a;
          printf("%d\n",a);
      }
      int a = 10;
      
    3. 全局变量不初始化则值为零。

    4. 声明只针对普通全局变量。

    5. 所有文件中,普通全局变量只能定义一次,可以声明多次。

    6. 全局变量在编译阶段分配空间,程序结束释放。

    7. 多文件

      //main.c
      int main() {
          extern void test();	
          //也可以把声明写到头文件里,头文件中不能放定义,多个文件包含会重复定义
          //#program once表示一个文件包含n次头文件只有一次有效
          test();
      }
      
      //test.c
      void test() {
          printf("hello\n");
      }
      
  4. static全局变量

    1. 在函数外面定义的为全局变量。
    2. static全局变量只在当前文件有效,同名static全局变量可以在多个文件定义。
    3. 全局变量不初始化则值为零。
    4. 全局变量在编译阶段分配空间,程序结束释放。

二.内存布局

实际上的内存分段远不止这几个,这里拣重点说。

  1. 在程序没有执行前,有几个内存分区已经确定,但只有程序运行时才会加载内存,linux中可以使用size *.out查看。

    • text(代码区):只读,放函数。
    • data(全局初始化数据区/静态数据区):初始化的数据,全局变量,static变量,常量(只读)。
    • bss(未初始化数据区):定义但未初始化的数据,全局变量,static变量。
  2. 当程序运行时,加载内存,首先前面确定的内存分区(text,data,bss)先加载,然后额外加载两个区:

    • stack(栈区):普通局部变量,自动管理内存,先进后出。
    • heap(堆区):手动申请空间,手动释放,整个程序结束,系统也会自动回收,如果没有手动释放,程序也没有结束,这个堆区空间不会自动释放。
  3. 存储类型总结

    类型作用域生命周期存储位置
    auto变量{}内当前函数stack
    static局部变量{}内整个程序运行期data/bss
    extern变量整个程序整个程序运行期data/bss
    static全局变量当前文件整个程序运行期data/bss
    extern函数整个程序整个程序运行期text
    static函数当前文件整个程序运行期text
    register变量{}内当前函数运行时存储在cpu寄存器

| 常量 | 当前文件 | 整个程序运行期 | data |

  1. 栈越界

    Linux中可以用ulimit -a查看栈区多大

    #include <stdio.h>
    int main() {
        int a[100000000000000] = {0};	//很显然栈区存不下,越界导致段错误
        return 0;
    }
    
  2. 内存操作函数

    1. #include <stdio.h>
      #include <string.h>
      void *memset(void *s,int c,size_t n);
      /*
      功能:将s的内存区的前n个字节以参数c填入
      参数:
      	s:需要操作内存s的首地址
      	c:填充的字符,类型虽然是int,但必须是unsigned char,范围0-2555
      	n:指定需要设置的大小
      返回值:s的首地址
      */
      int main() {
          int a;
          //主要就是用来清零
          memset(&a,0,sizeof(a));
          printf("a = %d\n",a);	//"a = 0"
          
          //中间参数虽然是整形,但是以字符处理
          //每个字节都存的97,所以只能char单字节的读
          memset(&a,97,sizeof(a));
          printf("a = %c\n",a);	//"a = a"
          
          int b[10];
          memset(b,0,sizeof(b));
          memset(b,0,10 * sizeof(int));
          return 0;
      }
      
    2. #include <stdio.h>
      #include <string.h>
      void *memcpy(void *dest,const void *src,size_t n);
      
      int main() {
      	char p[] = "hello\0world";
      	char buf[100];
          printf("sizeof(p) = %d\n",sizeof(p));
          strncpy(buf,p,sizeof(p));
          printf("buf1 = %s\n",buf);
          printf("buf2 = %s\n",buf + strlen("hello") + 1);
          /*
          sizeof(p) = 12
      	buf1 = hello
      	buf2 =
      	*/
          
          memset(buf,0,sizeof(buf));
          memcpy(buf,p,sizeof(p));
          printf("buf1 = %s\n",buf);
          printf("buf2 = %s\n",buf + strlen("hello") + 1);
          /*
          buf1 = hello
      	buf2 = world
          */
          return 0;
      }
      
    3. #include <string.h>
      int main() {
      	int a[10] = {1,2,3,4,5,6,7,8,9,10};
          /*
          使用memcpy()最好别出现内存重叠,如果出现最好使用memmove()
          memcpy(&a[2],a,5*sizeof(int));
          */
          memmove(&a[2],a,5*sizeof(int));
          return 0;
      }
      
    4. #include <string.h>
      int main() {
          int a[5] = {1,2,3,4,5};
          int b[5] = {1,2,3,4,6};
          int flag = memcmp(a,b,5*sizeof(int));
          /*
          flag < 0: a < b;
          flag > 0: a > b;
          flag = 0: a = b;
          */
          return 0;
      }
      
  3. 堆区内存分配和释放

    指针指向堆区空间

    #include <stdlib.h>
    #include <stdio.h>
    int main() {
        int *p = (int*)malloc(sizeof(int));	//malloc分配堆区空间,成功返回void*,需要转int*,失败返回NULL
        *p = 10;
        printf("%d\n",*p);
        if(p != NULL) {
        	free(p);	//手动释放p指向的堆区空间,同一块内存只能释放一次,释放完就不能用了
            p = NULL;	//p的值还是原先的,需要设置为NULL
        }
        return 0;
    }
    

    上例中不手动释放程序结束也会回收内存,但如果是服务器上的程序是不会结束的,如果只分配不释放就会造成内存泄露:动态分配了内存,不释放,如果windows,andriod长期不关机,就会很卡,重启就好了也是这个原因。

    内存污染:非法操作内存,就是野指针。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值