目录
C语言中的四种存储类别: 自动变量 (auto)、静态变量(static)、寄存器(register)、外部变量 (extern)。
通常在自定义函数内或代码段中(用“{}”括起来的)定义的变量,都是自动变量,除了加了static关键字修饰的变量,也称为局部变量。都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量)都属此类,在调用该函 数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。
自动变量用关键字 auto 进行存储类别的声明,例如声明一个自动变量:
- int fun(int a)
- {
- /*定义 b,c 为自动变量*/
- auto int b,c=3;
- }
-
- //a 是函数 fun()的形参,b、c 是自动变量,并对 c 赋初值 3。
- //执行完 fun()函数后,自动释放 a、b、c 所占的存储单元。
外部变量(即全局变量)是在函数的外部定义的,它的作用域为从变量定义处开始,到本程序文件的末尾。
- #include
-
- void fun(void);
-
- int a = 100; //定义一个全局变量
-
- int main(void)
- {
- //我们可以直接使用变量a
- printf("a = %d\n",a);
- //可以改变a的值
- a = 10;
- printf("a = %d\n",a);
- //可以在函数中使用变量a
- fun();
-
- return 0;
- }
- void fun(void)
- {
- printf("a = %d\n",a);
- }
如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件末尾。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字 extern 对该变量进行“外部变量声明”。表示该 变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量.
- //我们在外部创建一个C文件
-
- int w = 100;//定义一个整型变量
-
- void fun(void)//定义一个函数
- {
- printf("这是外部函数!\n");
- }
我们如何和拿到外部文件个变量呢?很简单,可以通过extern关键字来进行声明
- #include
-
- //通过关键字extern来声明外部变量,然后我们就可以在本文件可以使用了,前提是变量或函数是公开的
- extern int w;
- //通过关键字extern来声明外部函数,而在通常情况下函数的声明可以省略关键字extern
- extern void fun(void);
-
- int main(void)
- {
- //我们可以获得外部变量的值
- printf("w = %d",w);
- //同样我们可以调用外部的函数
- fun();
-
- rerurn 0;
- }
通过举例,显而易见,我们可以用 extern 声明外部变量,达到扩展程序文件中的作用域效果。
有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为静态局部变量,用关键字 static 进行声明。
- #include
-
- void fun(void);
-
- int main(void)
- {
- int a;
- fun();
- fun();
- return 0;
- }
- void fun(void)
- {
- //前面我们说过,这是自动变量,在这个fun()函数结束之后,将被释放
- int x=100;
- //如果在定义自动存储变量,前面加上关键字static,那么它就变成静态变量,在函数结束后,将被保留
- static int y = 50;
- //我们可以验证一下,通过调用两次函数打印出x和y的值,观察结果
- y +=10;
- printf("x = %d,y = %d\n",x,y);
- }
我们调用两次该函数,可以得到结果 : x的值不变,y的值加上了10;
为提高效率,C 语言允许将局部变量的值存放在 CPU 的寄存器中,这种变量叫做寄存器变量,用关键字 register 声明。使用寄存器变量需要注意以下几点:
(1)只有局部自动变量和形式参数可以作为寄存器变量。
(2)一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量。
(3)不能使用取地址运算符“&”求寄存器变量的地址。
相关概念
作用域(scope)
概念:作用域描述了程序中合法访问一个标识符的区域。
一个C变量的作用域可以是:
链接(linkage)
概念:跟作用域类似,变量的链接是一个空间概念,描述了程序中合法访问一个标识符的区域。
一个C变量的链接类型可以是:
存储期(storage duration)
概念:变量的声明周期,描述了一个C变量在程序执行期间的存在时间。
一个C变量的存储期可以是:
存储类说明符:
1、auto:声明一个自动变量
2、static:声明一个静态变量,或声明一个内部链接函数和全局变量
3、register:声明一个寄存器存储类变量
4、extern:声明一个外部存储变量
5、typedef:语法意义上的存储类,与实际存储类型无关。
前面用到的内存开辟方式为在栈上开辟空间,例如:
- int val = 20;//在栈空间上开辟四个字节
- char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有两个特点:
malloc用来开辟动态内存
void * malloc(size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
void free (void* ptr);
free函数用来释放动态开辟的内存。
- //申请5个整形字节的动态内存
- int *p = (int *)malloc(5*sizeof(int));
- //若返回值为空,则申请失败
- if (NULL == p)
- {
- printf("malloc error!\n");
- return 1;
- }
- //若申请成功,为每个元素赋值
- for (int i = 0; i < 5; i++)
- {
- p[i] = i;
- }
- printf("malloc success!\n");
-
- //打印释放前p的地址
- printf("before:%p\n",p);
-
- //释放动态开辟的内存
- free(p);
-
- //打印释放后p的地址
- printf("after:%p\n", p);
这里需要注意的几个点
p = NULL;
C语言还提供了一个函数叫 calloc , calloc 函数也用来动态内存分配。原型如下:
void* calloc (size_t num, size_t size);
- int *p = (int *)calloc(10, sizeof(int));
- if (NULL != p)
- {
- printf("success!\n");
- }
-
- free(p);
- p = NULL;
realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,为了合理使用内存,我们一定会对内存的大小做灵活的调整。realloc 函数就可以做到对动态开辟内存大小的调整。 函数原型:
void * realloc(void * ptr, size_t size);
- int *ptr = malloc(100);
- if (ptr != NULL)
- {
- //业务处理
- }
- else
- {
- exit(EXIT_FAILURE);
- }
- //扩展容量
- //代码1
- ptr = realloc(ptr, 1000);//如果申请失败造成内存泄漏
-
- //代码2
- int*p = NULL;
- p = realloc(ptr, 1000);
- if (p != NULL)
- {
- ptr = p;
- }
- //业务处理
- free(ptr);
代码1的问题在于,如果realloc申请失败返回NULL指针,老空间ptr被置空,造成内存泄漏无法找到原有的内存地址。
- void test()
- {
- int *p = (int *)malloc(INT_MAX/4);
- *p = 20;//如果p的值是NULL,就会有问题
- free(p);
- }
如果申请的空间较大可能造成malloc返回值为NULL
- void test()
- {
- int i = 0;
- int *p = (int *)malloc(10*sizeof(int));
- if(NULL == p)
- {
- exit(EXIT_FAILURE);
- }
- for(i=0; i<=10; i++)
- {
- *(p+i) = i;//当i是10的时候越界访问
- }
- free(p);
- }
- void test()
- {
- int a = 10;
- int *p = &a;
- free(p);//错误
- }
- void test()
- {
- int *p = (int *)malloc(100);
- p++;
- free(p);//p不再指向动态内存的起始位置
- }
-
- void test()
- {
- int *p = (int *)malloc(100);
- free(p);
- free(p);//重复释放
- }
- void test()
- {
- int *p = (int *)malloc(100);
- if(NULL != p)
- {
- *p = 20;
- }
- }
-
- int main()
- {
- test();
- while(1);
- }