变量
在讲C语言程序内存地址分布之前,先讲讲关于C语言的变量:
大体上分为三种:
1.块变量
在语句块(函数里面的分支或者循环语句中或者 {} )中定义,只能在定义的语句块中使用。
作用域:语句块
{
//块变量
}
2.局部变量
在函数内部定义的(除块变量以外),形参是局部变量,只能在函数内部访问。
作用域:函数内部
3.全局变量
定义在函数外部的变量 在程序中都可以访问
作用域:整个程序
下方举例说明各中变量:
1 #include<stdio.h>
2 int p;//全局变量
3
4 void a(){
5 int i ;//局部变量
6 }
7 int main(){
8 int i = 100;//局部变量
9 for(i=0;i<100;i++){
10 int j = i+1;//块变量
11 }
12 return 0;
13 }
同时在定义变量时有一些注意点:
1.在同一个作用域下面,不能定义和声明同名的变量,但是在块变量中可以定义和局部变量同名的变量,在函数中可以定义和全局同名的变量。
2.局部优先原则:块变量-->局部变量-->全局变量
3.如何访问呗隐藏掉的全局变量:
1).写个函数
2).全局指针指向全局变量
3).语句块注入
{
extern 全局变量;
}
C语言程序内存地址分布
关于程序内存地址的分布:
一个程序(进程)有4G的虚拟内存地址空间
sizeof(指针) == 4
0-3G 用户空间 3G-4G 内核空间
从低地址到高地址: 用户空间(代码区->数据区->BSS->堆区->(堆栈缓冲区)->栈区)->内核空间
代码区: 存储代码文本、字面值神圣不可以修改,只要修改代码区的内容,会出现段错误(核心已转储
数据区: 存储的是已经初始化的全局变量 和 已经初始化的静态变量
BSS : 存储的是未初始化的全局变量 和 未初始化的静态变量 自动清零
程序启动时会对该区域进行全部清零(擦除)
堆区: 动态内存(手动申请 手动释放) 从小到大扩张使用
栈区: 存储普通的局部变量和块变量,形参,函数调用时的开销,从大到小使用
一般来说,代码区、数据区、BSS程序运行之后,大小都是固定的,栈区和堆区是根据数据的多少在自由的扩张,
堆区从小到大扩张,栈区从大到小扩张,合理分配和使用内存,利用最大化。
今天先来讲讲关于堆区的内容:
堆区的动态内存,如果需要使用,需要自己申请,然后使用完之后手动释放。
#include<stdlib.h>
void *malloc(size_t size)
申请的动态内存 size为申请的字节数
返回值为void * 是一个万能指针,可以转换成任意类型的指针
如果申请返回NULL,需要对申请的动态内存进行成功的判断。
比如:
4 int words(const char *s){
5 if(s==NULL){
6 return -1;
7 }
这里的if就是判断是否申请动态内存成功!
void free(void *ptr);
释放动态内存
一定要确保参数ptr为malloc/calloc/realloc返回的值。
如果ptr前面的控制块被修改,将会导致核心段错误。
即使释放掉所有动态内存,第一次申请的33页内存将一直会保留
动态内存如果没有释放将造成内存泄露(这个问题很严重)。
动态内存也不能够多次释放。
malloc虽然是申请size个字节的动态内存,但实际上确不是这个数字。
第一次malloc分配了至少33页的内存空间 1页=4096字节。
在该内存没有全部使用之前,不会再申请更多的内容(只是在之前申请的内存中划一部分出来)。
申请多少个字节的内容,严格控制使用多少个字节的内存,不要越界。
动态内存:堆内存,手动申请,手动释放。
内存泄露:申请的动态内存没有释放或者没有及时释放。
内存碎片:多次申请和释放内容,会造成一些动态内存块中内存将无法使用。
以下程序包括动态内存的申请,释放,以及一些需要注意的点,需要理解透彻:
#include <stdio.h>
2 #include <stdlib.h>
3 int main(){
4 //100个整数
5 int arr[100] = {};//栈区 arr int *
6
7 //int *p = malloc(0xffffffff);
8 int *p = malloc(400);
9 if(p==NULL){
10 printf("malloc申请动态内存失败!\n");
11 return -1;
12 }
13 //50 double
14 //malloc(50*8);
15 int i = 0;
16 for(;i<100;i++){
17 p[i] = i;
18 }
19 p[50] = 9527;
20 p[66] = 8888;
21 for(i=0;i<100;i++){
22 printf("%d ",p[i]);
23 }
24 printf("\n");
25 double *pd = (double*)p;
26 double d = 3.14;
27 for(i=0;i<50;i++){
28 pd[i] = d;
29 d = d + 2.5;
30 }
31
32 for(i=0;i<50;i++){
33 printf("%g ",pd[i]);
34 }
35 printf("\n");
36 free(p);
37
38 return 0;
39 }
另外简单提一下calloc和realloc这两块内容
void *calloc(size_t nmemb, size_t size);
申请nmemb个size字节大小的连续内存 总共nmemb*size个字节
等同于malloc(nmemb*size)
malloc申请的动态内存不一定会擦除内存中的数据
calloc一定会擦除
返回NULL表示失败
void *realloc(void *ptr, size_t size);
ptr:必须是指向动态内存
size: 想要最终调整为size个字节大小的动态内存,调整动态内存大小为size个字节。
#include <stdio.h>
2 #include <stdlib.h>
3 int main(){
4 int *p = calloc(10,4);
5 if(p==NULL){
6 printf("calloc申请动态内存失败!\n");
7 return -1;
8 }
9 size_t i;
10 for(i=0;i<10;i++){
11 *(p+i) = i*i;
12 }
13 printf("%p\n",p);
14 int *p1 = malloc(4);
15
16 p = realloc(p,20*4);//把p所指向的动态内存调整为80个字节
17 printf("%p\n",p);
18 for(i=0;i<20;i++){
19 printf("%d ",p[i]);
20 }
21 printf("\n");
22 free(p);
23 p = NULL;
24 return 0;
25 }
**注意:**
调整动态内存之后必须重新接收该函数的返回值,调整大小之后动态内存的位置可能发生变化,
如果在扩大的时候,后面的动态内存已经被使用了,那么将重新申请一块size个字节的动态内存然后把
之前内存中的数据拷贝过来,释放之前的动态内存。