欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言中的存储类型以及内存空间的管理

程序员文章站 2022-07-13 23:20:01
...

存储类型

1.C语言中存储类型有哪些?各自的适用场景和区别。

变量的声明 : <存储类型> <数据类型> <变量名>
存储类型有:auto(默认) register static extern

  • auto: 存储局部变量,必须初始化,不然是随机值

  • register: 寄存器类型,将变量放到CPU的寄存器中可以加快程序的运行速度
    -在申请不到空间时,就会使用一般内存,同auto
    -寄存器变量是没有地址而言,不能用取地址符 &

  • static 静态存储类型的变量 修饰局部变量,默认是0
    -在内存中是以固定的地址存放,而不是以堆栈的形式存放
    -只要程序没结束,就不会随着说明它的程序段的结束而消失,下次再调用该函数,该存储类型的变量不会再重新说明,而且还保留上次调用存入的数值。
    -static修饰的全部变量,其他文件无法使用

  • extern:外部参照引用类型
    < extern > <外部变量数据类型> <外部变量名>
    -当一个变量在一个文件的函数体外声明,所有其他文件中的函数或程序端都可引用这个变量。

对比auto 与 static 修饰局部变量的区别

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i = 1;
    while(i<5)
    {
        int a = 0;  //默认是 auto 类型
        a++;
        printf("a = %d\n",a);
        i++;

    }
    return 0;
}
输出为 
a=1
a=1
a=1
a=1
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i = 1;
    while(i<5)
    {
        static int a = 0;  //此为static 类型
        a++;
        printf("a = %d\n",a);
        i++;

    }
    return 0;
}
输出为 
a=1
a=2
a=3
a=4

extern 使用举例 (linux ubuntu)
C语言中的存储类型以及内存空间的管理

内存空间管理

1.C/C++中的内存区间有几种?各自的特点是?

C/C++ 中分为 代码区 静态存储区 局部变量存储区(即栈区) 动态存储区(即堆区)

  • 代码区通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。
  • 静态存储区一般用来存储 全局变量,static 修饰的变量,以及字符串常量。静态存储区内的变量在程序编译阶段已经分配好内存空间并初始化。这块内存在程序的整个运行期间都存在。
  • 局部变量存储区(栈区):主要存放函数的参数以及局部变量
    在函数完成执行,系统自行释放栈区内存,不需要用户管理。栈内存分配运算 内置于处理器的指令集中,效率很高,但是分配的内存容量(stack size)有限。
  • 动态存储区(堆区)
    有些操作对象只有在程序运行时才能确定,这样在编译器在编译时就无法为他们预定存储空间。
    所有动态存储分配都在堆区进行,利用malloc 和 free 申请内存和释放内存。

举例分析:以下程序会得到什么样的结果?怎么修改呢?

#include <stdio.h>
#include <stdlib.h>

char * get_string()
{
    char s[]="Welcome!";
    return s;

}
int main()
{
    char * p;
    p = get_string();
    printf("%s\n",p);

    return 0;
}

解答:从内存的角度,字符数组 s (放在栈区)在函数调用完后立即释放了内存,传不到p ,所以会输出一段乱码,并不能得到预期的结果 Welcome!

修改方式

char s[]="Welcome!"; 改为 static char s[] = "Welcome!"; 或者 char * s = "Welcome!"; //但这样改,就不能修改此常量的值
或者:

#include <stdio.h>
#include <stdlib.h>

char * get_string()
{
    char *s;
    s = (char *)malloc(10*sizeof(char));    //在函数内部使用malloc
    if(s==NULL)
    {
        printf("malloc failed!\n");
        return 0;
    }
    
    printf("please input:");
    scanf("%s",s);   //这里 不能以 s="welcome!"的方式赋值,等式左边s 申请的是一块动态内存,放在堆区,等式右边是一个字符串常量,放在静态存储区,会引起段错误,将 s 的地址更新后,就肯定不能正确的释放申请的堆内存空间!
    printf("s = %s\n",s);
    return s;       //若改成 s="welcome"的形式,这里return 就是返回的静态存储区的地址,后面 free(p)就会造成段错误,原来申请的堆内存不能正确释放

}
int main()
{
    char * p;
    p = get_string();
    printf("p = %s\n",p);

    free(p);                          //在函数外部释放
    p = NULL;
    return 0;
}

malloc free 解析

1.malloc/free的使用注意事项

  • void * malloc(size_t num) malloc函数原型,注意返回的是一个 void * 类型,是申请到的内存的起始地址。因此,在调用malloc时要显式的进行类型转换,将 void * 转换成所需要的指针类型。
  • void free (void *p),这里的参数是申请到的内存的起始地址,如果参数是NULL的话,没有任何效果。同时,只释放一块内存中的一部分是不允许的。
  • malloc 本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。
    malloc 申请到的是一块连续的内存,有时可能会比所申请的空间大,有时也会申请不到内存,返回一个空指针(NULL),表示发生了异常,堆资源不足,动态内存分配失败。
  • malloc 与 free 总是配对使用的,free只能用来释放堆空间。如果malloc返回的指针值丢失,则所分配的堆空间也无法回收,称内存泄漏。而且,同一空间的重复释放也是危险的,因为该空间可能已经重新分配,所以必须妥善保存malloc返回的指针。
  • free (p); 的意思是 删除了p所指的目标(变量或者对象等),释放了它所占的堆空间,而不是删除p本身,因此p就成了空悬指针,所以也经常在 free(p);后再加一句 p=NULL; 确保安全。
  • 动态分配的变量或者对象的生命期:无名对象生命期并不依赖于建立它的作用域,比如在函数中建立的动态对象在函数返回后仍然可以使用,我们也称堆空间为*空间就是这个原因。但必须记住释放该对象所占的堆空间,并且只能释放一次。

用例分析

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char * p;
    p = (char *)malloc(10*sizeof(char)); //必须进行类型转换,因为malloc默认返回void * 类型 ,必须说明申请内存空间的大小
    if(p==NULL)
    {
        printf("malloc failed!\n");
        return 0;
    }
    printf("p = %p\n",p); //查看其分配的内存空间的首地址

    printf("input:");
    scanf("%s",p);    //必须保证输入的值不能多,最后 '\0' 还要占一位
    printf("p = %s\n",p);

    free(p);
    p = NULL;

    return 0;
}

把一个结构体放到堆内存中,怎么写?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N 20

typedef struct student{
  int no;
  char name[N];
  float score;
}Stu;

Stu * get_info()
{
   Stu *p;
   if ((p=(Stu *)malloc(sizeof(Stu)))==NULL)
   {
       printf("malloc failed");
       return NULL;
   }
   p->no = 1;
   strcpy(p->name,"Tom");
   p->score = 90;

   return p;

}
int main()
{
    Stu * s;
    if((s=get_info())==NULL)
    {
        return 0;
    }
    printf("Student info: %d %s %.2f\n",s->no,s->name,s->score);

    free(s);
    s=NULL;
    return 0;
}

野指针

是指向“垃圾”内存的指针。它是很危险的。

  • 指针变量没有初始化。
  • 指针p被free后,没有置为NULL,让人误以为p 是个合法的指针。
  • 指针操作超越了变量的作用范围。
char *p;
char s[] = "wel";
p=s;
strcpy(p,"welcome"); //越界
相关标签: Linux C语言