C语言内存管理

160 阅读5分钟

作用域

C语言变量的作用域分为

  • 代码块作用域,代码块是{}之间的一段代码
  • 函数作用域
  • 文件作用域

局部变量

局部变量也叫auto 自动变量,auto 可以不写,一般情况下代码块{}内定义的变量都是自动变量,特点如下:

  • 在一个函数内定义,只在函数范围内有效
  • 在复合语句中定义,只在复合语句中有效
  • 随着函数调用的结束或者复合语句的结束局部变量的生命周期也就结束
  • 如果没有赋值,内容是随机的

静态局部变量

  • static 局部变量的作用域也是在定义的函数内有效
  • static 局部变量的生命周期和程序运行的周期一样,static 局部变量的值只初始化一次,但是可以赋值多次
  • static 局部变量若未赋初始化值,则由系统自动赋值.数值型变量自动赋值为0,字符型为空字符串

全局变量

  • 在函数外定义,可以被本文件及其它文件中的函数所调用,若其他文件中的函数调用此变量,需用extern 声明
  • 全局变量的生命周期和程序运行周期一样
  • 不同文件的全局变量不可以重名

extern int a;声明一个变量,这个全局变量在别的文件中已经定义了,这个知识声明,不是定义

静态全局变量

  • 在函数外定义,作用范围被限制在多定义的文件中
  • 不同文件的静态全局变量可以重名,但是作用域不冲突
  • static 全局变量的生命周期跟程序运行周期一样,static 全局变量的值只初始化一次

全局函数和静态函数

C 语言中 函数默认都是全局的,使用关键字static 可以将函数声明为静态函数,函数定义为static就意味着这个函数只能在定义这个函数的文件中使用,其他文件中不能调用,即使在其他文件中声明这个函数也没有用

  • 允许在不同函数中使用相同的变量名,她们代表的不同的对象,分配不同的空间,互相不干扰
  • 同一个源文件中,允许全局变量和局部变量同名,使用上是就近原则
  • 所有函数默认都是全局的,意味着所有函数都不能重名,但是如果是static 函数,作用域是文件级的,所以不同的文件static函数可以相同

内存布局

内存分区

c代码经过预处理,编译,汇编,链接,4个步骤后生成一个可执行的程序,在windows下通过size 可执行程序名 可以得到一个这个可执行程序的文件结构,也就是说程序运行之前,程序还没有加载到内存中,可执行程序的内部已经分好了信息,分别是代码区(text),数据区(data)和未初始化数据区(bss)

代码区(text)

该区域存放CPU执行的机器指令.通常代码去是可共享的(别的可执行程序可以调用它),使其共享的目的是对于频繁被执行的程序,只需要内存中有一份代码即可,代码区通常是只读的,使其只读的原因是防止程序意外的修改了它的指令,另外代码区还规划了局部变量的相关信息

全局初始化数据区/静态数据区(data)

该区包含了在程序中明确被初始化的全局变量,已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)

未初始化数据区(bss)

存入的是全局未初始化变量和未初始化静态变量,未初始化数据区的数据在程序开始执行之前都被内核初始化为0或者null

程序在加载到内存前,代码区和全局区(data和bss)的大小是固定的,程序运行期间不能改变,然后程序开始执行,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text),数据区(data)和未初始化数据区(bss)之外,还额外的增加了栈区和堆区

内存操作函数

memset

 void * memset(void *s,int c,size_t n)
  • 将s的内存区域的前那个字节用参数c填充
  • 参数:
    • s:需要操作内存的s的首地址
    • c:填充的字符,c虽然参数是int,但是必须是unsigned char,范围是0~255
    • n:指定需要设置的大小
  • 返回值:s的首地址
#include<string.h>
int main(int argc, char const *argv[])
{
    
    int a[10] ={0};
    printf("%lu\n",sizeof(a));
    for(int i  = 0;i<10;i++){
     printf("%d\n",a[i]);
    }
     printf("----------------------------------------------------------------\n");

    int *p = memset(a,49,4);
    for(int i  = 0;i<10;i++){
     printf("%c\n",*(p+i));
    }
    return 0;
}
  • 最后一个参数 是表示多少字节

memcpy

 void * memcpy(void * dest,const void * src,size_t n)
  • 把src所指向的内存内容的前n个字节拷贝到dest所指的内存地址上
  • 参数:
    • dest 目的内存首地址
    • src 源内存首地址,注意 dest和src所指向的内存空间不可重叠,否则会导致程序出错
    • n 需要拷贝的字节数
  • 返回值: dest的首地址

memmove

  • memmove的功能用法和memcpy一样,区别是dest和src所指的内存空间重叠时,mommove仍能处理,不过执行效果比memcpy低一些

memcmp

  int memcmp(const void* s1,const void* s2,size_t n)
  • 比较s1和s2所指向内存区域的前n个字节

  • 返回值:

    • 相等 = 0
    • s1>s2 :>0
    • s1<s2 :<0
  • memcmp遇到0或者\0不会结束,这是与strcmp的区别

malloc

 void * malloc(size_t size)
  • 在内存的动态存储区(堆区)中分配一个长度为size字节的连续区域,用来存放类型说明符制定的类型,分配的内存空间内容不确定,一般使用memset初始化

free

void free(void * ptr)
  • 释放ptr所指向的一块内存空间,ptr是一个人一类型的指针变量,指向被释放区域的首地址,对于同一个内存空间多次释放会出错