C语言的内存分配主要如下:
(1)栈区:在执行函数时,函数内局部变量(不包含static变量)、函数返回值的存储单元在栈区上创建,
函数执行结束时这些存储单元自动被释放。栈区内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(2)堆区:即动态分配内存分配,程序在运行时用malloc(calloc,realloc等)申请的内存,程序员自己负责用free释放内存。
(3)静态存储区:该内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。此处存放全局变量、static变量。
(4)常量区:整型常量、浮点型常量、字符型常量及字符串常量都分配在文字常量区,程序结束后由系统释放。
(5)代码区:存放整个程序的代码。数据和代码是分开存储的。
如下有几种容易出错的情况:
#include <stdlib.h>
#include <stdio.h>
char* function();
main()
{
char *b;
char * golble_b;
char a=(char)(100);
b=&a;
golble_b=&a;
printf("the int value of a:%d,%c\n",a,a);
printf("before functin:%c,g_b:%c\n",*b,*golble_b);
golble_b=function();
printf("after functin:%c,g_b:%d\n",*b,*golble_b);
getchar();
}
char* function()
{
char a[2];
char *b;
b=&a;
printf("please input a char:");
scanf("%s",b);
printf("inner of function %c\n",*b);
return b;
}
运行结果:
下面简单分析一下。
对于子函数char* function()。
内部分配了
char a[2];
char *b;
很明显a[2]用来存储一个字符串。
而b执行一个字符。如下图。a[1]存储的是字符串结束符。
返回的是b。是一个指向指向字符的指针。
修改一下function()
char* function()
{
char a[2];
char *b;
b=&a;
printf("please input a char:");
scanf("%s",b);
printf("inner of function %c\n",*b);
return a;
}
存在一个warning
这个很好解释。a是数组名,就是一个指针,是一个执行含有两个char元素的数组的指针。
但是传过去的还是一个字符。
再改一下:
#include <stdlib.h>
#include <stdio.h>
char(*function())[2];
main()
{
char *b;
char * golble_b;
char(*array_b)[2];
char a=(char)(100);
b=&a;
golble_b=&a;
printf("the int value of a:%d,%c\n",a,a);
printf("before functin:%c,g_b:%c\n",*b,*golble_b);
//golble_b=function();
array_b=function();
printf("after functin:%c,g_b:%d\n",*b,*golble_b);
printf("after functin:%c,array_b:%c\n",*b,*array_b);
getchar();
}
char(*function())[2]
{
char a[2];
char *b;
b=&a;
printf("please input a char:");
scanf("%s",b);
printf("inner of function %c\n",*b);
return a;
}
注意
char(*function())[2];
和
char(*array_b)[2];
前者为声明了一个返回指向存储2个char元素数组的指针的函数。声明了一个函数,函数无形参,返回值为返回一个指针,一个指向数组的指针,一个指向存储了两个char元素数组的指针。
后者为声明了一个指针,一个指向存储了两个char元素的数组的指针。
看结果:
看,并没有传过来,是因为return a 中的a是局部数组的首地址,它是栈区地址。funciton函数执行结束时组数a存储单元自动释放。所以不应该返回过去。如果想要正确返回组数a的地址,可以再定义数组时加static修饰或定义为全局变量。
看结果:
至今为止没有使用malloc。其实这个也可以达到这个结果。但是不能忘记free。
看如下程序:
#include <stdlib.h>
#include <stdio.h>
int* function();
main()
{
int *c,d;
d=888;
c=&d;
printf("after function,b:%d\n",*c);
c=function();
printf("after function,b:%d\n",*c);
getchar();
}
int* function()
{
int a,*b;
a=1314521;
b=&a;
(*b)++;
printf("inner of function %d\n",a);
return b;
}
看结果:
不是说“函数执行结束时这些存储单元自动被释放。”么?
没关系我们再修改一下:
#include <stdlib.h>
#include <stdio.h>
int* function();
int* function2();
main()
{
int *c,d;
d=888;
c=&d;
printf("before function,c:%d\n",*c);
c=function();
printf("after function,c:%d,address=%p\n",*c,c);
c=function2();
printf("after function2,c:%d,address=%p\n",*c,c);
getchar();
}
int* function()
{
int a,*b;
a=1314521;
b=&a;
(*b)++;
printf("inner of function %d\n",a);
return b;
}
int* function2()
{
int a,*b;
a=12580;
b=&a;
(*b)++;
printf("inner of function %d\n",a);
return b;
}
看结果:
两个子函数,形参、返回值、函数体出了存储在静态区的常量不同其他都一样。
所以进入子函数时,系统为之分配的栈空间一样,再因为操作系统和编译器都一样。外部环境很相似。所以栈的地址区域和所有变量的分配地址都一样。
我们这里再说明一下:
(1)我们是在xp操作系统上运行的。是有MMU的。是有内存映射的。所以我们的地址这时都是虚拟地址,是操作系统为每一个
进程分配的4G的虚拟地址,通过内存映射这些地址会被映射到物理地址上。
(2)当第一个自函数退出时,为之分配的栈空间就会被释放。但并不是清零的操作,而是这块内存标明可以被使用,
在学习操作系统的过程中,还记得内存分配的。是沿着那个链表式的东西在找一块儿合适的内存空间。
被释放只是说明这块儿内存可以被重新分配。没有清空的操作,这个对比试验可以得出这个结果。
看程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int address_array;
int address_pointer;
int address_pointer_char;
int function();
int main()
{
char *p,*q;
function();
printf("address_array=%d,address_pointer=%d,address_pointer_char=%d\n",address_array,address_pointer,address_pointer_char);
p=(char *)address_pointer;
q=(char *)address_pointer_char;
printf("p=%p\n",p);
printf("q=%p,%c\n",q,*q);
}
int function()
{
char *c,d;
char a[50]="Hello word!!";
char *b;
b=a;
d='a';
c=&d;
printf("a[]=%s,*b=%s",a,b);
address_array=a;
address_pointer=b;
address_pointer_char=e;
}
看结果:
子函数中初始化了一个字符数组,一个char指针,一个指向字符的char指针。结束前把这些局部变量的地址都传给了全局变量。
在主函数中,访问这些地址,都未成功。这是因为在函数结束后和打印变量之间还有其他函数的调用。所以,那块栈空间又被改写了。
注意,为了安全,在子函数中千万不要return 指针类型的局部变量值。栈内地址不能安全的传递到函数外。可以传,但是不安全,因为很肯能会改变。