Day10.内存结构
一、作用域
1、全局变量和局部变量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//声明变量,声明之后便可以在此文件中使用这个变量
//声明不会分配内存空间
//extern int a;
//全局变量 作用在整个项目中,使用的前提是需要在使用的文件中进行声明
int a = 100;
int main(void)
{
//变量的作用范围:从创建到所在函数结束
int a = 10;
return 0;
}
2、静态全局和局部变量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void test()
{
//静态局部变量在函数中,只会执行一次
static int a = 10;
printf("%d\n", a++);
}
int main(void)
{
for (int i = 0; i < 10; i++)
{
test();
}
return 0;
}
三、作用域和生命周期
/*
类型 作用域 生命周期
局部变量 int a=10 从变量定义到函数结束 局部变量创建到函数结束
全局变量 int a=10 整个项目文件中 程序创建到程序结束
static 局部变量 int a=10 从定义变量到函数结束 程序创建到程序结束
static 全局变量 int a=10 当前文件中 程序创建到程序结束
static void test() 当前文件中 程序创建到程序结束
void test() 整个项目文件中 程序创建到程序结束
*/
四、内存布局
内存四区:
代码区(text):
程序指令
数据区(Linux系统编程会将数据区拆开分析):【也称全局区,静态区】
初始化的数据(data):
1、初始化的全局变量
2、初始化的静态全局变量
3、初始化的静态局部变量
未初始化的数据:(bss)
1、未初始化的静态局部变量 默认初始值为0
2、未初始化的全局变量 默认初始值为0
3、未初始化的静态全局变量 默认初始值为0
字符串常量
#difine 定义的常量
栈区(stack):
局部变量
数组
结构体
指针
枚举
函数形参
常量
堆区(heap):
音频文件
视频文件
图像文件
文本文件
打的数据
注意:
栈区:
1、只有c语言将数组放在栈区;
2、栈区大小:在不同的操作系统中系统分配给每一个程序的战区空间大小不同,一般Windows是1-8M不等,一般LINUX是1-16M不等;
3、死循环搞不死电脑 ,不会占满内存但会占满CUP;
4、递归会导致程序崩溃,栈溢出;
堆区:
开辟堆空间大小与内存有关;
代码区 | 数据区 | 堆区 (堆大小和物理内存有关) | 栈区 (栈区向下生长) |
部分公用 | 部分公用 | 堆区大小是减去其他三部分的大小 | 不是公用,可以被访问 |
===========================================================================================================
【内存低地址】--------------------->>>>>---------------->>>>>----------------->>>>>--------------【内存高地址】
代码示例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 100
int e;//未初始化的全局变量
int f = 10;//初始化的全局变量
static int g;//未初始化的静态全局变量
static int h = 10;//初始化的静态全局变量
int main()
{
printf("%d\n", MAX);
int a = 10;//局部变量
static int b;//未初始化的静态局部变量
static int c = 10;//初始化的静态局部变量
int i[10];//未初始化的数组
int j[10] = {0};//初始化的数组
int* k;//未初始化的指针
int* l = &a;//初始化的指针
char* p = "hello world";//字符串常量
char ch[] = "hello world";
const int m = 10;
printf("局部变量a的地址:%p\n", &a);
printf("未初始化的局部静态变量b的地址:%p\n", &b);
printf("初始化的局部静态变量c的地址:%p\n", &c);
printf("未初始化全局变量e的地址:%p\n", &e);
printf("初始化全局变量f的地址:%p\n", &f);
printf("未初始化的静态全局变量g的地址:%p\n", &g);
printf("初始化的静态全局变量h的地址:%p\n", &h);
printf("未初始化的数组i的地址:%p\n", i);
printf("初始化的数组j的地址:%p\n", j);
printf("未初始化的指针k的地址:%p\n", &k);
printf("初始化的指针l的地址:%p\n", &l);
printf("字符串常量p的地址:%p\n", p);
printf("字符数组ch的地址:%p\n", ch);
printf("常量m的地址:%p\n", &m);
return 0;
}
/*
运行结果:
100
局部变量a的地址:00BBF8D0
未初始化的局部静态变量b的地址:00C9A15C
初始化的局部静态变量c的地址:00C9A010
未初始化全局变量e的地址:00C9A164
初始化全局变量f的地址:00C9A008
未初始化的静态全局变量g的地址:00C9A158
初始化的静态全局变量h的地址:00C9A00C
未初始化的数组i的地址:00BBF8A0
初始化的数组j的地址:00BBF870
未初始化的指针k的地址:00BBF864
初始化的指针l的地址:00BBF858
字符串常量p的地址:00C97B34
字符数组ch的地址:00BBF838
常量m的地址:00BBF82C
*/
五、堆空间开辟数组冒泡排序
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MAX 10
void test_printf()
{
printf("==================\n");
}
int main(void)
{
//加入随机数种子
srand((unsigned int)time(NULL));
//创建堆空间
int* p = (int*)malloc(sizeof(int) * MAX);
//产生随机数
for (int i = 0; i < MAX; i++)
{
*(p + i) = rand() % 50;
}
//打印
for (int i = 0; i < MAX; i++)
{
printf("%d\n", *(p + i));
}
test_printf();
//冒泡排序
for (int i = 0; i < MAX - 1; i++)
{
for (int j = 0; j < MAX - i - 1; j++)
{
if (*(p + j) > *(p + j + 1))
{
int temp = *(p + j);
*(p + j) = *(p + j + 1);
*(p + j + 1) = temp;
}
}
}
//打印
for (int i = 0; i < MAX; i++)
{
printf("%d\n", *(p + i));
}
//释放堆空间
free(p);
return 0;
}
六、堆空间操作字符串
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
//开辟多大堆空间,就要操作多大堆空间
char* p = malloc(sizeof(char) * 100);
strcpy(p, "hello world!!");
printf("%s\n", p);
free(p);
return 0;
}
七、内存操作函数
memset() memcpy() memmove() memcmp()
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
int* p = (int*)malloc(sizeof(int) * 10);
//重置(初始化):将s的内存区域的前n个字节以参数c填入 返回值s的首地址
//参数:目标地址s 值c 字节大小n
memset(p,0,40);
for (int i = 0; i < 10; i++)
{
printf("%d\n", p[i]);
}
if(p)
free(p);
char* p1 = (char*)malloc(sizeof(char) * 10);
memset(p1, 0, 10);
printf("%s\n", p1);
if(p1)
free(p1);
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
memset(arr, 0, 40);
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p2 = malloc(sizeof(int) * 10);
//参数:目标地址,源地址(被拷贝对象的地址),大小
//注意:目标地址和源地址不要重叠
/*
和strcpy的区别:
1、函数参数不同
2、strcpy拷贝字符串 memcpy可以拷贝一块内存
3、拷贝结束标志不同,strcpy以\0为结尾,memcpy以个数为结尾
*/
memcpy(p2, arr1, 40);
for (int i = 0; i < 10; i++)
{
printf("%d\n", p2[i]);
}
if(p2)
free(p2);
//memmove 拷贝重叠内存地址不会出现问题,但是效率比较低
//如果拷贝目标和拷贝源没有重叠,两个函数效率一样
int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(&arr2[2], arr, 20);
for (int i = 0; i < 10; i++)
{
printf("%d\n", arr2[i]);
}
int arr3[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr4[5] = { 1,2,3,4 };
//比较arr3和arr4所指向内存区域的前20个字节
int value = memcmp(arr3, arr4, 20);
printf("%d\n", value);
return 0;
}
八、练习
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
//求出三名学生 三门功课成绩 并排序 通过堆空间来实现
int** p = (int**)malloc(sizeof(int*) * 3);
p[0] = (int*)malloc(sizeof(int) * 3);
p[1] = (int*)malloc(sizeof(int) * 3);
p[2] = (int*)malloc(sizeof(int) * 3);
p[0][0] = 4;
p[0][1] = 6;
p[0][2] = 5;
p[1][0] = 2;
p[1][1] = 3;
p[1][2] = 1;
p[2][0] = 8;
p[2][1] = 7;
p[2][2] = 9;
//排序
for (int i = 0; i < 3 - 1; i++)
{
for (int j = 0; j < 3 - i - 1; j++)
{
for (int k = 0; k < 3; k++)
{
if (p[k][j] > p[k][j + 1])
{
int temp = p[k][j];
p[k][j] = p[k][j + 1];
p[k][j + 1] = temp;
}
}
}
}
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf("p[%d][%d]为%d\n",i,j,p[i][j]);
}
}
if (p[0])
free(p[0]);
if (p[1])
free(p[1]);
if (p[2])
free(p[2]);
if (p)
free(p);
return 0;
}
九、栈内存存储过程
- 栈遵从规则:后进先出 先进后出