1.代码区
代码区code,程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,也叫代码段,这块内存是不可以在运行期间修改的。
代码区中所有的内容在程序加载到内存的时候就确定了,运行期间不可以修改,只可以执行。
2.静态区
静态区是程序加载到内存的时候就确定了,程序退出的时候从内存消失。所有的全局变量和静态变量在程序运行期间都占用内存。
所有的全局变量以及程序中的静态变量和常量(const)都存储到静态区
3.栈区
栈stack是一种先进后出的内存结构,所有的自动变量,函数的形参,函数的返回值都是由编译器自动放出栈中,当一个自动变量超出其作用域时,自动从栈中弹出。
不同的系统栈的大小是不一样的,即使相同的系统,栈的大小也是不一样的,windows程序在编译的时候就可以指定栈的大小,linux栈的大小是可以通过环境变量设置的。
3.1静态区、代码区、栈区
静态区和代码区的大小受到物理内存大小的限制
栈区的大小一般很小,单位一般是k,所以栈中不能有太多变量
内存说明.png
4.堆区
堆heap和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。
堆是一个大容器,它的容量要远远大于栈,但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。
5.堆的分配和释放
1.malloc
void * malloc(size_t _Size);
malloc函数在堆中分配参数_Size指定大小的内存,单位:字节,函数返回void*指针
2.free
void free(void *p);
free负责在堆中释放malloc分配的内存。参数p为malloc返回的堆中的内存地址
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *p;
p = malloc(100);//在堆中分配了一个100字节的内存
//p指向了堆的内存首地址
//变量p在哪里?p在栈里面,但p的值是堆地址编号
free(p);
//为什么要写free?因为p可以自动销毁,但是malloc产生的堆内存并不会自动销毁,所以要free函数清理
//在堆中分配一个int
int *p1 = (int*)malloc(sizeof(int));
*p1 = 0;//在堆中的int的值设置为0
printf("%d\n",*p1);
free(p1);
p1 = NULL;//避免野指针
//在堆中分配一个int[10]
int *p2 = malloc(sizeof(int) * 10);
memset(p2,0,sizeof(int)*10);
for(int i = 0; i<10;i++{
printf("%d\n",p2[i]);
}
free(p2);
p2 = NULL;//避免野指针
/*
*堆内存有一个最小单位,叫内存页
*一个内存页的大小也不是固定的,在测试的机器里面是4k为一个单位变化的
*当我们要堆里面一个内存的时候,总是4k为一个单位
*/
return 0;
}
内存分配例子1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *test()
{
char *p = malloc(100);//在堆中分配了100个char,假设堆地址为0x123
strcpy(p,"hello");
//free(p);//p就成了野指针
return p;
char *test1()//错误的代码
{
(auto) char p[] = "hello";
return p;
}
char *test2()
{//不要通过函数的返回值返回一个自动变量的地址(返回堆地址可以!)
(auto) char a = 'a';//栈!
return &a;
}
char *test3()//正确
{
static char a = 'a';
return &a;//返回一个静态变量的地址是有效的,因为静态变量的内存一直存在
}
const char *test4()//和const没有关系,都是栈!
{
char a[] = "hello world";
return a;
}
const char *test5()//正确的,参数const表示常量,常量在静态区,静态区可读写,可以返回!
{
const char *s = "hello world";
return s;
}
const char *test6()
{
return "abc";//和test5道理一样
}
int main()
{
char *p2 = test2();//返回了野指针!
*p2 = 'b';
char *str1 = test1();//test1这个函数调用完成后,test1里面的自动变量地址都已经从栈中弹出了!
//所以,str1是一个无效的地址
char *str = test();
str[0] = 'a';//str指向的不是代码区(代码区只读),而是指向的堆内存,所以可以改!
printf("%s\n",str);
free(str);
return 0;
}
内存分配例子2
void test2(char *s)//错误的,形参的值改变了,但实参没有改变,见图1!
{//s是栈区的
s = malloc(100);
strcpy(s,"hello");
//由于s在栈里面,test2这个函数执行完后,s就从栈中弹出了!
//结果是malloc分配的内存再也没有机会free了!
}
void test3(char **s)//正确,见图2
{
*s = malloc(100);
strcpy(*s,"hello");
}
char *test4()//这里没问题
{
char *s = malloc(100);
strcpy(s,"hello");
return s;
}
int main()
{
char *p = NULL;
test3(&p);
printf("%s\n",p);
free(p);
return 0;
/*
总结:如果通过一个函数内部给一个
实参指针分配堆内存,一定是二级指针
*/
char *p = NULL;
test2(p);//p没有分配到堆的内存
printf("%p\n",p);//p还是空指针
return 0;
}
gcc调试
gcc -o a a.c -g
a
gdb a
where
形参实参分析1.png
p是实参,s是形参,p的值会给到s,所以s的值是NULL
此时创建了堆内存空间,地址是0x123,那么s的值就是0x123
往堆内保存"hello",也没有出错。
但是p地址存值没有变化!!!!
形参的值改变了,但是实参没有改变
用二级指针 **s 的话(如test3(&p))
假设变量的地址是0x100,那么传到**s中,s的值是0x100
malloc创建堆内存,地址是0x123
*s的地址就是0x123,*s是什么,是0x100里面的值,所以*p=0x123
内存分配例子3
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char *test1()//错误的
{
char a[] = "hello";
return a;//a的值是一个自动变量的地址
}
const char *test2()//对的
{
char *a = "hello";
return a;//a的值是一个常量的地址
}
const char *test3()//对的
{
static char a[] = "hello";
return a;//a是静态变量
}
const char *test4()//对的
{
char a[] = malloc(100);
strcpy(a,"hello");
return a;
}
int main()
{
return 0;
}
网友评论