内存操作
堆空间开辟
c
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 栈区只有 1m 大小,每个 int 4 字节,超出内存程序会报错
int arr[210000] = {0};
// 开辟堆空间存储数据
// malloc 默认返回 void 类型
// int *p = malloc(sizeof(int)); // 开辟一个 int 字节大小空间
// 转 int 直观
int *p = (int *)malloc(sizeof(int)); // 开辟一个 int 字节大小空间
// 开辟 3G 的内存,malloc 开辟的是连续的内存空间,剩余内存有 3G 不一定是连续的
// int *p = (int *)malloc(1024 * 1024 * 1024 * 3);
printf("%p\n", p); // 输出成功表示开辟成功
// 使用堆空间
*p = 123;
printf("%d\n", *p);
// 释放堆空间
free(p);
// 地址还能正常打印
printf("%p\n", p);
// 此时 p 已经是野指针
*p = 345;
printf("%d", *p); // 345
p = NULL; // 一般在 free 调用后把地址指向空指针
return 0;
}
- 赋值
c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX 10
int main()
{
// 添加随机数种子
srand((size_t)time(NULL));
// 开辟堆空间
int *p = (int *)malloc(sizeof(int) * MAX);
for (size_t i = 0; i < MAX; i++)
{
p[i] = rand() % 100; // 取 0 - 99 随机数
printf("%d\t", p[i]);
}
// 指针地址被改变不能 free 释放
// p++;
// free(p);
free(p);
return 0;
}
memset()
c
#include <string.h>
void *memset(void *s, int c, size_t n);
- 功能:将 s 的内存区域的前 n 个字节以参数 c 填入
- 参数: s:需要操作内存 s 的首地址 c:填充的字符,c 虽然参数为 int,但必须是 unsigned char , 范围为 0~255 n:指定需要设置的大小,字节大小
- 返回值:s 的首地址
c
int a[10];
memset(a, 0, sizeof(a)); // 0 是 int 字节
memset(a, 97, sizeof(a)); // 97 是 int 字节, sizeof是 int * 10 长度
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%c\n", a[i]);
}
memcpy()
c
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
- 功能:拷贝 src 所指的内存内容的前 n 个字节到 dest 所值的内存地址上。
- 参数: dest:目的内存首地址 src:源内存首地址,注意:dest 和 src 所指的内存空间不可重叠,可能会导致程序报错 n:需要拷贝的字节数
- 返回值:dest 的首地址
c
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10];
memcpy(b, a, sizeof(a));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d, ", b[i]);
}
printf("\n");
//memcpy(&a[3], a, 5 * sizeof(int)); //err, 内存重叠
- 数组拷贝
c
#include <stdio.h>
#include <string.h>
int main()
{
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
memcpy(&a[5], &a[0], sizeof(int) * 5);
for(int i = 0; i < 10; i++){
printf("%d ", a[i]);
}
// 如果拷贝的目标和源发生重叠,可能报错
memcpy(&a[5], &a[3], sizeof(int) * 5);
return 0;
}
memmove()
- memmove()功能用法和 memcpy()一样,区别在于:dest 和 src 所指的内存空间重叠时,memmove()仍然能处理,不过执行效率比 memcpy()低些。
memcmp()
c
#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
- 功能:比较 s1 和 s2 所指向内存区域的前 n 个字节
- 参数: s1:内存首地址 1 s2:内存首地址 2 n:需比较的前 n 个字节
- 返回值: 相等:=0 大于:>0 小于:<0
c
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int flag = memcmp(a, b, sizeof(a));
printf("flag = %d\n", flag);
常见问题
越界释放内存
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char a[] = "hello"; // 6 个字节空间
char *p = (char*)malloc(sizeof(char) * 5); // 开辟 5 个
// 拷贝的 a 是6字节,目标 p 是5字节
strcpy(p, a); // 遇到 \0 拷贝停止
// 正常打印
printf("%s\n", p);
// 释放时的空间释放多了,会导致出现问题,释放完成之后内存会检查一次发现出错
free(p);
return 0;
}
空指针允许多次释放
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// 野指针
int *p = (int *)malloc(0);
*p = 100;
printf("%d\n", *p);
// 内存释放出错
free(p);
int *p1 = (int *)malloc(10);
p1[0] = 1;
p1[1] = 1;
p1[2] = 1; // 越界
// 内存释放出错
free(p1);
int *p2 = (int *)malloc(10);
free(p2);
// 堆空间不允许多次释放
free(p2);
p2 = NULL;
// 空指针允许多次释放
free(p2);
free(p2);
free(p2);
return 0;
}
地址传递
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fn1(int *p) {
p = (int *)malloc(8);
}
void fn2(int **p) {
printf("%p\n", *p);
*p = (int *)malloc(8);
}
int main()
{
int *p = NULL; // 指向 NULL
fn1(p); // err 值传递,这里传递的是 NULL 进去
printf("%p\n", &p); // p 本身的地址
fn2(&p); // 取 p 自己的地址传进去
printf("%p\n", *p);
p[0] = 12;
p[1] = 23;
printf("%d\n", p[0]);
printf("%d\n", p[1]);
free(p);
return 0;
}
开辟二级指针对应空间
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// arr[5][3]
// 开辟二级指针
int **p = (int **)malloc(sizeof(int*) * 5);
for (int i = 0; i < 5; i++)
{
p[i] = (int *)malloc(sizeof(int) * 3);
}
// 赋值
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 3; j++)
{
p[i][j] = i+j;
printf("%d\t", p[i][j]);
}
printf("\n");
}
for (int i = 0; i < 5; i++)
{
free(p[i]); // 先释放内层
}
free(p); // 再释放外层
return 0;
}