襄州区住房和城乡建设局网站,如何在工商网站做预先核名,wordpress调用jquery,网站公告栏模板本章大致内容介绍#xff1a;
1.malloc函数和free函数
2.calloc函数
3.realloc函数
4.常见错误案例
5.笔试题详解
6.柔性数组
一、malloc和free
1.malloc函数
#xff08;1#xff09;函数原型 函数参数#xff1a;根据用户的需求需要开辟多大的字节空间#xff…本章大致内容介绍
1.malloc函数和free函数
2.calloc函数
3.realloc函数
4.常见错误案例
5.笔试题详解
6.柔性数组
一、malloc和free
1.malloc函数
1函数原型 函数参数根据用户的需求需要开辟多大的字节空间为无符号的字节。 返回值malloc函数成功开辟内存后会返回该内存的起始地址可以根据需要强制转换成任意的类型若开辟空间失败则会返回空指针NULL。 头文件#includestdlib.h 2使用方法
1申请空间
#includestdio.h
#includestdlib.h
int main()
{int* p (int*)malloc(40);return 0;
} 目的申请十个整形空间所以参数传4*1040。 结果用一个整形指针来接收其返回值 2检查安全和使用
#includestdio.h
#includestdlib.h
int main()
{int* p (int*)malloc(40);if (p NULL)//必须对指针安全性检查{printf(申请空间失败\n);return;}//申请成功就开始用int i 0;for (i0;i10;i){*(pi) i;}i 0;for (i0;i10;i){printf(%d\n,*(pi));}return 0;
}
当使用结束之后我们需要删除该动态生成的空间则需要对空间进行释放这就是我们接下来讲的free。
2.free函数
1函数原型 1.参数为动态开辟内存的首地址 2.无参数返回 3.头文件#includestdlib.h 2配合动态内存开辟的函数使用
前面malloc函数开辟的内存还没释放接下来它们配合使用。
#includestdio.h
#includestdlib.h
int main()
{int* p (int*)malloc(40);if (p NULL){printf(申请空间失败\n);return;}//申请成功就开始用int i 0;for (i0;i10;i){*(pi) i;}i 0;for (i0;i10;i){printf(%d\n,*(pi));}free(p);p NULL;//及时将指针置空return 0;
} 注意事项: 1.free只能释放由动态内存开辟的空间 2.free释放的是指针所指向的那块空间释放后指针仍在但是指向的空间不咋了就会变成野指针所以我们需要及时置空。 二、calloc
1.函数定义 函数参数第一个参数是需要开辟的数据个数第二个是该数据类型的内存大小。 返回值calloc函数成功开辟内存后会返回该内存的起始地址可以根据需要强制转换成任意的类型若开辟空间失败则会返回空指针NULL。 头文件#includestdlib.h 2.calloc的使用
目的需要开辟10个整形空间
#includestdio.h
#includestdlib.h
int main()
{int* p (int*)calloc(10,sizeof(int));if (p NULL){printf(申请空间失败\n);return;}//申请成功就开始用int i 0;for (i 0; i 10; i){*(p i) i;}i 0;for (i 0; i 10; i){printf(%d\n, *(p i));}free(p);p NULL;//及时将指针置空return 0;
}
运行结果 根据malloc和calloc函数使用的两段代码好像除了名字和参数之外其他没什么不同呀其实他们还有一处区别。 3.calloc函数与malloc函数的区别
1区别
malloc函数开辟好空间之后并不会对其初始化但是calloc函数开辟好空间之后会将数据的每一个字节都初始化成0。
2对照
1malloc 2calloc 除了以上三点不同之外其他的都一样。所以我们需要根据内存需求需不需要初始化内存而选择合适的开辟方式。 三、realloc
1.函数定义
realloc可以对已有的内存进行调整 函数参数ptr是要调整的内存地址size为内存调整之后的新大小单位是内存总大小字节 返回值内存调整后的起始地址同样有申请内存成功和失败两种情况 头文件#includestdlib.h 2.realloc申请空间成功的两种情况
1原空间后的空间足够大
开辟空间方式直接原有内存之后直接追加空间原来空间的数据不发生变化。 2原空间之后没有足够大的空间 原有空间之后没有足够多的空间时扩展的方法是在堆空间上另找一个合适大小 的连续空间来使用。这样函数返回的是一个新的内存地址。而原有数据也会被拷贝到新内存中。
3.realloc的使用
#includestdio.h
#includestdlib.h
int main()
{int* p (int*)calloc(10,sizeof(int));if (p NULL){printf(申请空间失败\n);return;}//申请成功就开始用int i 0;for (i 0; i 10; i){*(p i) i;}i 0;for (i 0; i 10; i){printf(%d\n, *(p i));}//要求加大空间内存int* ptr realloc(p,40*sizeof(int));if (ptr NULL){printf(内存开辟失败\n);return;}p ptr;//将新开辟好的内存赋值原地址free(p);p NULL;//及时将指针置空return 0;
} realloc的使用一般在原有空间的情况下同样也需要对指针进行判空操作和free。 我们也可以看到free和这些函数是紧紧联系在一起的。 四、常见错误解析
这些错误都是动态内存开辟前后的问题与指针也有很大的联系
1.对NULL指针的解引用操作
错误写法
int main()
{int* p (int*)malloc(4);*p 20;printf(%d\n,*p);return 0;
} malloc有可能开辟动态内存失败则会返回NULL这个时候对NULL指针解引用操作就是非法的。 正确写法
int main()
{int* p (int*)malloc(4);*p 20;if (p NULL)//对指针安全性限制{perror(malloc);return;}printf(%d\n,*p);
//后续需要对内存释放return 0;
} 知识点1在每次动态内存开辟完成之后都要先对其指针进行判空操作若非空才能对其进行后续的操作。 2.对动态开辟空间的越界访问
错误写法
int main()
{int i 0;int* p (int*)malloc(10 * sizeof(int));if (NULL p){exit(EXIT_FAILURE);}for (i 0; i 10; i){*(p i) i;//当i是10的时候越界访问}free(p);
}
错误的后果提示 知识点2在对指针解引用操作时要注意指针所指向的个数 3.对非动态开辟内存使用free释放
错误写法
test()
{int a 100;int* p a;free(p);//错误
} 知识点3free函数只能释放动态开辟的内存否则会非法。 4.使用free释放一块动态开辟内存的一部分
错误写法
int *p (int *)malloc(100);
p;
free(p);//p不再指向动态内存的起始位置
当p之后p指向的起始位置就变了当free(p)之后会释放不完整也会造成内存泄漏。 知识点4使用指针尽量不要改变指针指向的起始地址。可以再重新使用新指针进行或--操作或者1/-1操作。 5.对同一块动态内存多次释放
错误写法
void test()
{
int *p (int *)malloc(100);
free(p);
free(p);//重复释放
} 知识点5切记要对内存释放但是每一块内存有且只能释放一次。 6.动态开辟内存忘记释放内存泄漏
错误
void test()
{
int *p (int *)malloc(100);
if(NULL ! p)
{
*p 20;
}
}
int main()
{
test();
while(1);
}
这是忘记对动态内存的释放的也是不可取的。
五、关于动态内存开辟的笔试题
分析下面四道代码题存在什么问题
运行Test函数会有什么样的后果
1.对NULL解引用操作
void GetMemory(char *p)
{
p (char *)malloc(100);
}
void Test(void)
{
char *str NULL;
GetMemory(str);
strcpy(str, hello world);
printf(str);
} 1.str指针为空 2.malloc开辟的空间只是被p指向没有被str指向相当于形参的改变不影响实参 3.所以strcpy函数就会对NULL指针进行解引用操作 4.没有free操作还会操作内存泄漏 图解
正确写法
void GetMemory(char** p)
{*p (char*)malloc(100);
}
void Test(void)
{char* str NULL;GetMemory(str);strcpy(str, hello world);printf(str);free(str);strNULL;
} 2.
问题代码
char *GetMemory(void)
{
char p[] hello world;
return p;
}
void Test(void)
{
char *str NULL;
str GetMemory();
printf(str);
} 1.字符数组p为栈空间的局部变量函数返回后会被销毁 2.数组被销毁返回的p就是野指针所指向的空间已不属于自己 类型代码情况
int* test()
{int a 10;return a;
}
int main()
{int* p test();printf(%d\n,*p);return 0;
}
这种运行的结果仍然可以得到10虽然空间依然属于p但是值仍在没有被其他的数据覆盖。但是下面这种情况则不行。 3.题目3
问题代码
void GetMemory(char **p, int num)
{
*p (char *)malloc(num);
}
void Test(void)
{
char *str NULL;
GetMemory(str, 100);
strcpy(str, hello);
printf(str);
} 动态开辟的内存最后没有被free释放 4.
问题代码
void Test(void)
{
char *str (char *) malloc(100);
strcpy(str, hello);
free(str);
if(str ! NULL)
{
strcpy(str, world);
printf(str);
}
} 1.str所指向的空间已被销毁 2.str变成野指针对其解引用操作为非法 六、柔性数组 1.柔性数组的定义
标准定义C99 中结构中的最后一个元素允许是未知大小的数组这就叫做『柔性数组』成员。 也就是说柔性数组不是指简单的数组而是在结构体中的数组。 有两种写法
第一种
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a; a数组就称为柔性数组。但是这种定义方式容易报错所以我们还有第二种。 第二种
typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a; 就是不需要指定数组的大小数组的大小是未知的。 2.柔性数组的特点
1sizeof 返回的这种结构大小不包括柔性数组的内存。
2结构中的柔性数组成员前面必须至少一个其他成员。 因为柔性数组是不计入sizeof的计算的只有柔性数组成员sizeof就会出错。 3包含柔性数组成员的结构用malloc ()函数进行内存的动态分配并且分配的内存应该大于结构的大小以适应柔性数组的预期大小。 在计算包含柔性数组的结构体时柔性数组是不计入内存的计算的。大于结构体内存大小的部分就会分配给柔性数组。 4代码验证
struct S
{int a;int arr[];
};
int main()
{struct S s;printf(%zd\n,sizeof(s));//计算该结构体的内存大小return 0;
}
运行的结果 柔性数组确实是不会参加sizeof对结构体的计算 3.柔性数组的使用
1开辟空间
#includestdio.h
#includestdlib.h
#includestring.h
struct S
{int a;int arr[];
};
int main()
{struct S* ps(struct S*)malloc(sizeof(struct S)16);if (ps NULL){perror(malloc);return;}return 0;
} 2增容realloc函数
#includestdio.h
#includestdlib.h
#includestring.h
struct S
{int a;int arr[];
};
int main()
{struct S* ps(struct S*)malloc(sizeof(struct S)16);if (ps NULL){perror(malloc);return;}struct S* str (struct S*)realloc(ps,sizeof(struct S)40);if (str ! NULL){ps str;}else{perror(realloc);return;}return 0;
} 用malloc开辟空间之后再用reallo增容减容。增容之后的空间都会加在柔性数组上这个时候数组的大小就可以根据realloc变化因此称为柔性数组。 4.柔性数组的优势
1方便内存释放
#includestdio.h
#includestdlib.h
#includestring.h
struct S
{int a;int arr[];//定义一个柔性数组
};
int main()
{struct S* ps(struct S*)malloc(sizeof(struct S)16);if (ps NULL){perror(malloc);return;}struct S* str (struct S*)realloc(ps,sizeof(struct S)40);if (str ! NULL){ps str;}else{perror(realloc);return;}free(ps);ps NULL;return 0;
} 因为开辟的空间都是连续的在一块内存中所以只需要free一次即可。 我们再对比一下另一种写法就更加明显了。 结构体中有指针的写法
struct S
{char c;int i;int* data;//定义一个指针
};
int main()
{struct S* ps (struct S*)malloc(sizeof(struct S));if (ps NULL){perror(malloc1);return 1;}ps-c w;ps-i 100;ps-data (int*)malloc(20);if (ps-data NULL){perror(malloc2);return 1;}int i 0;for (i 0; i 5; i){ps-data[i] i;}for (i 0; i 5; i){printf(%d , ps-data[i]);}//空间不够了增容int* ptr (int*)realloc(ps-data, 40);if (ptr NULL){perror(realloc);return 1;}else{ps-data ptr;}//增容成功就使用//...//释放free(ps-data);//第一次ps-data NULL;free(ps);//第二次ps NULL;return 0;
} 1.两次开辟空间的原因是使得他们的数据都开辟在堆区上 2.使得跟第一种一样的写法突然第一种的优势 3.这种写法开辟的空间是不连续的容易造成空间零碎空间导致空间浪费。 2有利于访问速度和节约内存
连续的内存有益于提高访问速度也有益于减少内存碎片更大程度的利用内存空间。