- 动态内存分配介绍
在开辟空间时,内存中主要有三个区,为栈区,堆区,静态常量区,栈区中中主要存储局部变量和形式参数,栈区存储由动态内存函数开辟的空间,静态常量区存储有static关键字的全局变量(但不完全是)和静态数据 - 动态内存函数
1.malloc
void* malloc(size_t size)
这个函数向内存申请一个连续可用的空间,并返回这块空间的指针,若开辟成功,返回开辟好空间的指针,若开辟失败,返回NULL指针,因此malloc函数的返回值要做检查。
2.free
专门用来做动态内存的释放和回收,表示系统回收这个空间,这个空间归还给操作系统。
3.calloc
void* calloc(size_t n,size_t size)
函数是为n个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
4.realloc
void* realloc(void* ptr,size_t size)
ptr为要调整的内存地址,size为调整之后大小,调整原内存空间大小后,将原来内存中的数据移动到新的空间
realloc模拟实现
void* my_realloc(void* memblock, size_t size)
{
void* new_base = malloc(size);//开辟新空间
assert(new_base != NULL);
memcpy(new_base, memblock, size);//拷贝原内容
free(memblock);//释放原空间
memblock = new_base;//改变指针方向
return memblock;//返回新开辟空间的地址
}
5._alloca
这个函数不能free,因为申请的空间在栈区。
- 常见动态内存错误
1.对NULL指针的解引用操作
void test()
{
int *p = (int*)malloc(INT_MAX / 4);
*p = 20;
free(p);
}
如果p的值为NULL,就会出现问题,应该改正为
void test()
{
int *p = (int*)malloc(INT_MAX / 4);
if (p == NULL)
{
perror("use malloc");
exit(EXIT_FAILURE);
}
*p = 20;
free(p);
}
2.对动态开辟空间的越界访问
void test()
{
int i = 0;
int *p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
perror("use malloc");
exit(EXIT_FAILURE);
}
for (i = 0; i <= 10; i++)
{
*(p + i) = i;
}
free(p);
}
只开辟了10个整形空间,可是却访问到了第11个,越界。
3.使用free释放一块动态内存开辟内存的一部分
void test()
{
int *p = (int*)malloc(100);
p++;
free(p);
}
因为p++后不再指向动态内存的起始位置。
4.对同一块动态内存多次释放
void test()
{
int *p = (int*)malloc(100);
free(p);
free(p);
}
重复释放
5.内存泄漏
void test()
{
int *p = (int*)malloc(100);
if (NULL == p)
{
*p = 20;
}
}
忘记释放空间会造成内存泄露,应该正确释放空间。
- 经典笔试题
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void test()
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf("%s",str);
}
结果程序会崩溃,因为在传递参数时传的是值,给p重新开辟了空间,str并没有改变,应该是址传递,改为
void GetMemory(char**p)
{
*p = (char*)malloc(100);
}
void test()
{
char* str = NULL;
GetMemory(&str);
strcpy(str, "hello world");
printf("%s", str);
}
char* GetMemory()
{
char p[] = "hello world";
return p;
}
void test()
{
char *str = NULL;
str = GetMemory();
printf("%s", str);
}
return p指返回地址,char p[]只是创建了临时空间,函数结束时销毁,因此在测试时地址虽然获取了回来,但是地址返回空间的值改变了。应该改为
char* GetMemory()
{
static char p[] = "hello world";
return p;
}
void test()
{
char *str = NULL;
str = GetMemory();
printf("%s", str);
}
void test()
{
char *str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf("%s", str);
}
}
在空间释放后又使用它,访问了非法内存,应该预防野指针,改为:
void test()
{
char *str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
str = NULL;
if (str != NULL)
{
strcpy(str, "world");
printf("%s", str);
}
}
- 柔性数组
结构中的最后一个元素允许是未知大小的数组
struct S
{
int n;
char arr[];//柔性数组
};
int main()
{
struct S *p=(struct S*)malloc(sizeof(struct S) + 10 * (sizeof(char)));
int i = 0;
struct S *ptr;
p->n = 10;
for (i = 0; i < 10; ++i)
{
p->arr[i] = i + 1;
printf("%d ", p->arr[i]);
}
printf("\n");
ptr = realloc(p,sizeof(struct S)+20 * (sizeof(char)));
if (ptr != NULL)
{
p = ptr;
}
p->n = 20;
for (i = 0; i < 20; ++i)
{
p->arr[i] = i + 1;
printf("%d ", p->arr[i]);
}
printf("\n");
free(p);
p = NULL;
system("pause");
return 0;
}
输出结果为1 2 3 4 5 6 7 8 9 10 和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20。