一 内存区域划分
一) 区域划分
代码区 常量区 栈区 堆区 静态全局区
1 代码区
二进制代码
2 常量区(文字常量区)
字符串
3 栈区
由编译器自动分配和释放,存放函数的参数的值,局部变量等.
4 堆区
由程序员手动申请,手动释放.
5 全局区(静态区)
全局变量和静态变量的存储是在一起的
#include <stdio.h>
// 普通全局变量:定义在函数外
int num;
// 静态全局变量:定义在函数外 并且使用static进行修饰
static int hp;
int main()
{
// 普通局部变量
int a; // a 栈区
// 静态局部变量
static int mp;
char str[] = "abcd"; // str 栈区 abcd 常量区
float* p; // p 栈区
char* pstr = "abcd"; // pstr 栈区 abcd 常量区
// 编译器可能将两个abcd 优化为一份内存
return 0;
}
二) 生存周期,作用域
普通全局变量(外部变量)
作用域:从定义开始到(源)文件结束
生存周期:从程序执行到程序结束
普通局部变量
作用域:函数(复合语句)内部==>当前大括号
生存周期:从函数调用开始 到函数调用结束
static局部变量
作用域:同普通局部变量
生存周期:同普通全局变量
static全局变量
作用域:被编译文件的剩余部分
生存周期:同普通全局变量
补充:
1 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的.
2 静态全局变量只在当前文件有效
static
#include <stdio.h>
/*
static:静态的
特点:只会定义一次
*/
int func1()
{
int a = 0;
a++;
return a;
}
int func2()
{
static int a = 0; // 只会定义一次
a++;
return a;
}
int main()
{
func1();
func1();
printf("%d
", func1()); // 1
func2();
func2();
printf("%d
", func2()); // 3
return 0;
}
二 动态内存管理
使用一个头文件: #include <stdlib.h>
使用三个函数: malloc calloc free 实现申请内存和释放内存
一) malloc
#include <stdio.h>
#include <stdlib.h>
int main()
{
// malloc
// 函数原型: void * __cdecl malloc(_In_ _CRT_GUARDOVERFLOW size_t _Size);
// 返回:void* 参数:size_t(unsigned int) 需要申请的字节数
// 1 使用malloc申请一个int类型大小的内存
int* p = (int*)malloc(1 * sizeof(int));
*p = 10;
printf("*p = %d
", *p);
/*
int*p 指针p指向申请的内存
(int*) 类型强转,原函数返回值是void*,所以需要强转
1 * sizeof(int) 需要申请的字节数
*/
// 2 使用malloc申请八个int类型大小的内存
int* p1 = (int*)malloc(8 * sizeof(int));
*p1 = 66;
printf("*p1 = %d
", *p1);
*(p1 + 1) = 77;
printf("*(p1 + 1) = %d
", *(p1 + 1));
p1[2] = 88;
printf("p1[2] = %d
", p1[2]);
for (int i = 0; i < 8; i++)
{
p1[i] = i;
}
for (size_t i = 0; i < 8; i++)
{
printf("%3d", *(p1 + i));
}
return 0;
}
*(p+n)=str[n]
二) calloc
#include <stdio.h>
#include <stdlib.h>
int main()
{
// calloc
// 函数原型: void * __cdecl calloc(_In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size);
// 返回:void* 参数: 申请的内存个数,单个内存的大小
// 1 使用calloc申请一个int类型大小的内存
int* p = (int*)calloc(1, sizeof(int));
// 2 使用calloc申10个int类型大小的内存
int* p1 = (int*)calloc(10, sizeof(int));
for (size_t i = 0; i < 10; i++)
{
p1[i] = i * 10;
}
for (size_t i = 0; i < 10; i++)
{
printf("%3d", *(p1 + i));
}
return 0;
}
三)free
#include <stdio.h>
#include <stdlib.h>
int main()
{
// calloc
// 函数原型: void * __cdecl calloc(_In_ _CRT_GUARDOVERFLOW size_t _Count, _In_ _CRT_GUARDOVERFLOW size_t _Size);
// 返回:void* 参数: 申请的内存个数,单个内存的大小
// 1 使用calloc申请一个int类型大小的内存
int* p = (int*)calloc(1, sizeof(int));
free(p); // 释放申请的内存
p = NULL; // 指针置空
// 2 使用calloc申10个int类型大小的内存
int* p1 = (int*)calloc(10, sizeof(int));
for (size_t i = 0; i < 10; i++)
{
p1[i] = i * 10;
}
for (size_t i = 0; i < 10; i++)
{
printf("%3d", *(p1 + i));
}C
free(p1); // 释放一段内存
p1 = NULL; // 指针置空
return 0;
}
思考:
连续申请一段内存,类似于一维数组,那么二维数组呢?
#include <stdio.h>
#include <stdlib.h>
int main()
{
/*int* p0 = (int*)calloc(5, sizeof(int));
int* p1 = (int*)calloc(5, sizeof(int));
int* p2 = (int*)calloc(5, sizeof(int));
int** pp = (int**)calloc(3, sizeof(int*));
pp[0] = p0;
pp[1] = p1;
pp[2] = p2;
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 5; j++)
{
printf("%2d", pp[i][j]);
}
printf("
");
}*/
int lin, row;
printf("input line and row:");
scanf("%d %d", &lin, &row);
// 申请
int** pp = (int**)calloc(lin, sizeof(int*));
for (size_t i = 0; i < lin; i++)
{
pp[i] = (int*)calloc(row, sizeof(int));
}
// 使用
for (size_t i = 0; i < lin; i++)
{
for (size_t j = 0; j < row; j++)
{
printf("%2d", pp[i][j]);
}
printf("
");
}
// 释放
for (int i = 0; i < lin; i++)
{
free(pp[i]);
pp[i] = NULL; // 可以省略
}
free(pp);
pp = NULL;
return 0;
}
注意:
二维数组的内存是连续的 但是 像这样申请的内存 不一定连续(%99申请的列不连续,极小可能申请的列连续)
1.如果 malloc之后没有及时的free,则内存泄漏(memory leak)
malloc的内存free一次就可以,重复free也是未定义行为
如果申请了两次malloc但是free一次情况:
第一次申请的内存使用p进行了保存,接下来修改p的内存后,在也没法找到第一次申请内存的地址,有就无从释放。
2.free只是将内存释放,并不会设为NULL, //此刻应该手动将str设为NULL,这样才不会存在隐患
作业:
动态数组(自动扩容)
扩容规则:变为原来的两倍
三 指针与函数
四 指针小结
一)使用指针的注意事项
二)指针与引用对比
int a = 10;
int& b = a;
printf("b = %d", b);
// 引用:给变量取别名
typedef _W64 unsigned int size_t; 给类型取别名