简介:

现代计算机操作系统中,重要的一个功能就是内存管理。在Linux系统中,提供了malloc函数来进行内存的分配。对应的,内存释放函数式free。

在C语言中,可以用printf函数输出变量的值,包括变量被分配的地址,%d用于输出整形,%s用于输出字符串,而%p就用于输出变量的地址值。 

C语言程序的内存空间:

C语言程序包括以下几部分:

正文段:存放由CPU执行的机器指令,通常为只读;

初始化数据段:通常将此段称为数据段,它包含了程序中需赋初值的变量。

例如,C程序中任何函数之外的说明:

float PI= 3.14f;

使此变量以初值存放在初始化数据段中;

非初始化数据段:通常将此段称为b s s段,这一名称来源于早期汇编程序的一个操作符,意思是“block started by symbol(由符号开始的块)”。在程序开始执行之前,内核将此段初始化为0。

例如,函数外的说明:

long sum[1000] ;

使此变量存放在非初始化数据段中。

:自动变量以及每次函数调用时所需保存的信息都存放在此段中。

每次函数调用时,其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调用的函数在栈上为其自动和临时变量分配存储空间。

通过以这种方式使用栈,C函数可以递归调用。

例如,

int main() {

    int x =10;  // 栈存储

    int y = 20; // 栈存储

    return 0;

}

:通常在堆中进行动态存储分配,例如用malloc函数分配的地址空间。由于历史上形成的惯例,堆位于非初始化数据段顶和栈底之间。

例如,

int main() {

    int x =10;  // 栈分配

    int y = 20; // 栈分配

    char *p = (char*)malloc(256); //堆分配

    return 0;

}

用malloc在堆中分配了一块内存空间,用p保存这个内存空间的首地址,用于后续访问这块内存。但p这个变量本身是在栈中分配的。

下图,是一个典型的C语言程序内存空间分布图:

 

内存地址的测试:

1.栈上分配的地址测试:

malloc_p.c,用于打印内存地址值。

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

int test1() {
	/*栈上分配,猜测,x和y的地址相邻*/
	int x = 10;
	int y = 20;

    /*预想: p = x的地址; &p =p的地址(而且,和y的地址相邻); 实际结果:和预想的一样*/
	int *p = &x; 

	printf("x=%d, &x=%p\n",x,&x);
	printf("y=%d, &y=%p\n",y,&y);
	printf("p=%p, &p=%p\n",p,&p);
	printf("\n");

	return 0;
}

int main() {
	test1();

}

编译:用gcc命令进行编译:

gcc malloc_p.c

运行结果:

x=10, &x=0x7ffeed1df88c

y=20, &y=0x7ffeed1df888

p=0x7ffeed1df88c, &p=0x7ffeed1df880

 可以看到,

一,p的值就是x的地址,而p也有自己的地址;

二,x,y,p三个变量的地址都挨着。其实都是系统在堆栈上给分配的。

2.malloc地址测试:

再看一个复杂的例子,我们加上malloc函数,再进行对比:

    /*预想:q的值和p的值离的很远,因为,q的值是在堆中分配的,q本身的地址在栈中分配*/
	char *q = (char*)malloc(256);

	printf("q=%p, &q=%p\n",q,&q);
	printf("\n");

    /*一定要进行free,否则,就有内存泄露*/
    free(q);

 运行结果:

x=10, &x=0x7ffee115788c

y=20, &y=0x7ffee1157888

p=0x7ffee115788c, &p=0x7ffee1157880

q=0x7f7f0ac05990, &q=0x7ffee1157878

可见,和我们的预想结果一样,q本身的地址和其它变量的地址都紧挨着,它们的内存都是系统在栈上分配的;而q中存储的值,和其它的变量的地址离的特别远,其实,这个地址就是malloc在堆中分配的内存地址。 

3.静态存储区的地址测试: 

 我们再加上静态全局变量和非静态全局变量来看一下:

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

/*初始化数据段, 0x402000, 0x402004*/
/*m=1024, &m=0x402000
  n=2048, &n=0x402004*/
int m = 1024;
int n = 2048;

/*非初始化数据段,  0x403100*/
int arr[10];

/*静态存储,初始化数据段*/
int static MAX = 256;
int static MIN;

int test1() {
	/*栈上分配,猜测,x和y的地址相邻*/
	int x = 10;
	int y = 20;

    /*预想: p = x的地址; &p =p的地址(而且,和y的地址相邻); 实际结果:和预想的一样*/
	int *p = &x; 

	printf("x=%d, &x=%p\n",x,&x);
	printf("y=%d, &y=%p\n",y,&y);
	printf("p=%p, &p=%p\n",p,&p);

    /*预想:q的值和p的值离的很远,因为,q的值是在堆中分配的,q本身的地址在栈中分配*/
	char *q = (char*)malloc(256);
	printf("q=%p, &q=%p\n",q,&q);
    
    /*初始化数据段*/
    printf("m=%d, &m=%p\n",m,&m);
	printf("n=%d, &n=%p\n",n,&n);
	printf("\n");

    /*非初始化数据段*/
	printf("arr[0]=%d,&arr[0]=%p\n",arr[0],&arr[0]);
	printf("&arr=%p\n",&arr);
	printf("\n");

    /*静态存储,初始化数据段*/
	printf("MAX=%d, &MAX=%p\n",MAX,&MAX);
	printf("MIN=%d, &MIN=%p\n",n,&MIN);
	printf("\n");
    
    /*一定要进行free,否则,就有内存泄露*/
    free(q);

	return 0;
}

int main() {
	test1();

}

运行结果:

x=10, &x=0x7ffeef42988c

y=20, &y=0x7ffeef429888

p=0x7ffeef42988c, &p=0x7ffeef429880

q=0x7ffcd2c05990, &q=0x7ffeed011878

/*1.初始化数据段*/

m=1024, &m=0x1007de010

n=2048, &n=0x1007de014

/*2.非初始化数据段*/

arr[0]=0,&arr[0]=0x1007de020

&arr=0x1007de020

/*3.静态存储,初始化数据段*/

MAX=256, &MAX=0x1007de018

MIN=2048, &MIN=0x1007de01c

 可以看出:

1)m,n,arr,arr,MAX,MIN的地址挨着,但和前面的栈中分配的x,y,p, 以及malloc分配的堆中的内存地址,都相隔甚远;

2)其实,m,n,arr,arr,MAX,MIN这些变量,对于系统在具体分配内存的时候,并无差别。虽然我们从逻辑上将其分成了初始化数据段中,非初始化数据段中,以及静态存储的初始化数据段。


内存分配malloc,千万别忘了free。

GitHub 加速计划 / li / linux-dash
6
1
下载
A beautiful web dashboard for Linux
最近提交(Master分支:3 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐