C语言中的存储类型以及内存空间的管理
存储类型
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)
内存空间管理
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"); //越界
上一篇: Arduino与光敏电阻
下一篇: Arduino+光敏传感器