我们已经掌握的开辟内存方式:
int a = 20;
char c = ‘c’;
int a[20] ;等等等…
以上的这些开辟的内存都是已经固定好了的,数组在声明时,必须指定数组长度,编译之后便不能增减了,那么当我们遇到像只有在运行时才能知道我们需要多大的内存空间时的需求该怎么办呢?
答:使用动态内存函数,运行时动态申请空间,需要多少申请多少。
C语言提供了一个动态内存开辟的函数:
void* malloc (size_t size);
官方说明:分配大小为字节的内存块,返回指向块开头的指针。
新分配的内存块的内容未初始化,仍保留不确定的值。
如果size为零,则返回值取决于特定的库实现(它可能是也可能不是空指针),但返回的指针不应被取消引用。
例如:动态申请一块2个int型的堆内存空间
int *ptr = NULL;
int num = 2;
ptr = (int*)malloc( num * sizeof(int) );
if( NULL == ptr){
printf("申请失败!\n");
}
变量说明:
由于需要的是int型空间,所以指针必须是int类型,故ptr是int型并且malloc返回值必须强转为int*型
由于需要一块2个int型大小的空间,所以malloc需要申请空间大小为2 * sizeof(int)字节,即8字节
由以上代码便动态申请了一块2个int型大小的空间
C语言提供了一个函数free,用来对动态开辟的内存进行释放回收
void free (void* ptr);
官方说明:
作用:释放内存块
如果ptr没有指向分配给上述函数的内存块,则会导致未定义的行为。
如果ptr是空指针,则函数不执行任何操作。
请注意,此函数不会更改ptr本身的值,因此它仍然指向相同(现在无效)的位置。
free(ptr);
使用free()函数便将之前申请的动态内存释放了
注意:
在如下代码中,ptr存储的地址为0X00B59B98,该片地址中存储的是1111111十进制数字,在free之后,ptr存储的地址仍然是为0X00B59B98,但是该片地址中存储的是随机数字
void* calloc (size_t num, size_t size);
官方说明:分配一块num个大小为size字节的内存块,并将每一位初始化为0
size为个数
num为每个的字节数
总大小 = num * size
想一想:有时我们动态分配的内存大小,万一出现了偏大或偏小了,为了最大限度的利用内存,我们应该怎么办呢?
答案:C语言提供了一个函数,可以修改已经动态分配的内存大小
void* realloc (void* ptr, size_t size);
官方说明
作用:重新分配内存块,更改ptr指向的内存块的大小。
size变量说明:内存块的新大小,以字节为单位;size_t是无符号整数类型。
函数可以将内存块移动到新的位置(其地址由函数返回)。
内存块的内容将保留到新大小和旧大小中的较小值,即使块被移动到新位置。如果新大小更大,则新分配部分的值不确定。
如果ptr是空指针,那么函数的行为类似malloc,分配一个新的大小字节块,并返回一个指向其开头的指针。
void test()
{
int *p = (int *)malloc(100); //动态分配内存
if(NULL != p)
{
*p = 20;
}
//未释放内存,导致内存泄漏
}
int main()
{
test();
while(1);
}
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p); //重复释放
}
void test(){
int *p = (int *)malloc(100);
p++; //p指针已经不是指向动态开辟的内存的起始位置了
free(p); //p指向空间内存只释放了一部分
}
int a = 10;
int *p = &a;
free(p); //由于p指向的空间并不是动态申请的,所以无需释放
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
exit(EXIT_FAILURE);
for(i=0; i<=10; i++)
//一共访问了11次,动态申请的内存只可以访问10从
*(p+i) = i; //当i是10的时候越界访问
free(p);
}
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
由于结构体是一种自定义类型,它和int、char、double、float等C语言自带的类型是没区别的,我们来看一看结构体如何分配内存空间:
struct Array{
int num;
int* mem; //指向一个边长空间
};
int main(){
//由外而内申请内存
struct Array* pArray = (struct Array*)malloc(sizeof(struct Array));
if(pArray == NULL){ //检查结构体申请空间是否成功
return 1;
}
pArray->num = 10;
pArray->mem = (int*)malloc(pArray->num * sizeof(int));
if(pArray->mem == NULL){ //检查结构体成员空间是否申请成功
free(pArray); //失败返回前不要忘记释放结构体的空间
return 2;
}
//由内而外释放内存
free(pArray->mem);
free(pArray);
return 0;
}
也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
写法1:
typedef struct A
{
int i;
int a[0];//柔性数组成员
};
写法2:
typedef struct B
{
int i;
int a[];//柔性数组成员
};
柔性数组的特点:
上述代码实现了一个变长数组,存放了0-9这10个数字,每一个数字存放在一个int型大小的内存中。
sizeof(struct A) + 10 * sizeof(int)中申请了一个结构体大小的内存空间外加10个int类型的内存空间,这10个int类型的内存空间就被保存在柔性数组中。
a[0]是结构体的终止地址,同时又是后面内存的起始地址。
struct A* pA = (struct A*)malloc(sizeof(struct A) + 10 * sizeof(int));
面试题一
void GetMemory(char *p){
p = (char*)malloc(100);
}
int main(){
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world!");
printf(str);
return 0;
}
上述代码有哪些错误?
1.p指向的动态内存未被释放
2.试图向空指针str中写入数据
3.试图输出空指针中的内容
如何修改代码能完美解决上述错误呢?
提示:使用二级指针,将str的地址传入!
修改后代码:
void GetMemory(char **p){
*p = (char**)malloc(100);
}
int main(){
char* str = NULL;
GetMemory(&str);
strcpy(str, "hello world!\n");
printf(str);
return 0;
}