C语言学习笔记9——动态内存管理-创新互联
1.原则:谁申请谁释放。申请和释放在同一个函数里,最不济也要在同一个模块中。否则会产生内存泄漏,虽然在进程结束时所占用的资源都会释放,但如果一个进程中频繁申请又不释放,有可能很快用完内存
创新互联是一家专注于成都网站设计、成都做网站与策划设计,历下网站建设哪家好?创新互联做网站,专注于网站建设十年,网设计领域的专业建站公司;建站业务涵盖:历下等地区。历下做网站价格咨询:028-86922220*note:
1)像这种需要成对出现的,最好在开始写的时候就都写出来,防止以后忘记了。比如写malloc/calloc/realloc时,直接在函数结尾或者模块结尾出写上free
2)分配内存语句写完后要先判断是否分配成功
2.size_t是封装的整型
3.malloc申请联系的size个字节空间,返回值是空指针,因为空指针和任意指针类型可以相互赋值
calloc连续申请多个变量的内存,每个变量都占size个字节
realloc将malloc和calloc申请的内存当大小需求变化时重新申请,如果内存空间足够就申请,如果不够,将当前释放后从满足条 件的地方重新申请。所以realloc的指针的地址(不是指针值)有可能是变化的
4.malloc申请内存没有必要强转的,如果报警告那是因为没有包头文件stdlib.h,只有当malloc返回给函数指针这种情况c99没有定义,可能是需要强转的。
5. malloc实现变长的数组也就是动态数组
6.动态内存面试题 :调用函数完成指针指向对象的更改
1)free空指针也是合法的
2)局部函数被调用执行完后,里面涉及到的除静态的数据都被释放掉了(形参指针也释放掉了,但是对实参指针的操作效果还是保留下来的,调用函数还可以使用),如果执行结果想被调用函数使用指针,对于传入的auto实参或者被调函数里的auto想要返回执行结果,用return返回。
3)传参:指针形参如果没有改变指针对象,形实参指针就是同一个东西,对形参操作就是对实参操作,return影响不到二者感情。一旦形参被赋了它值,形实参就分道扬镳,对形参操作和实参无关。
a)实参和传参是两个独立的变量,是在内存中两个不同的地址上的,只是数据类型相同,其他无关联。当实参是个指针时,形参保存实参的指针值,这样形参指向实参所指对象。只要在被调函数中没有对形参重新赋值将其指向别处,那么通过形参进行的操作即使函数结束前没有返回形参指针,结果也是传给了实参,如func0中的操作。
b)func和func1中,虽然实参将指针赋给了形参指针,但二者是相互独立的指针,当二者指向相同时形参的操作就是实参的操作,即使调用结束前没有return指针且因auto变量被释放,实参也有形参相同的执行效果。一旦在调用函数里形参被赋了其他值指向别处,那么二者没有任何关系,对形参做的任何操作如果没有其他措施下都会随函数的解释而被消灭掉,而不会实参有任何改变。
func1当函数结束后,malloc的内存无法被使用导致内存泄漏,直到进程运行结束。
*note:修改的方式有两种:一是用return 返回malloc成功的指针,函数返回类型是int*或者百搭void*;二是用func2
c)func2中,传入的是二级指针且形参在调用函数里没有改变过指向,因此二级指针的形参实参二者一体。改变了形参的一级指针就是改变了实参的一级指针
*Note:
1)通过调用函数对本级指针进行更改或者操作,传的实参只能是多一级指针,多N级的指针不可以,且在对本级指针修改完成之前不要对多一级的指针就行修改。比如修改一级指针指向,实参要传二级指针,在完成一级指针操作之前不要改变二级指针指向。
2)在被调函数没有将本级指针返回的情况下,传参过程就是实参和形参的赋值过程,实参直接传本级指针auto变量的形参是和实参独立,当形参指向没更改之前,二者指向同一对象,当对形参malloc时其实是将形参指向了其他对象,而实参指向的对象却没有变。malloc成功后对形参进行的操作都和实参无关。想在被调函数中完成对实参X级指针的修改,只能传X级指针的地址也就是(X+1)级指针,然后通过对(X+1)级指针解引用来改变X级指针所指对象,且这之前不能改变(X+1)级指针所指对象。
void func(int* p)
{
int q = 6;
printf("[%s] &q=%x, &p=%x p=%x *p = %d\n", __FUNCTION__, &q, &p, p, *p);
p = &q;
printf("[%s] &q=%x, &p=%x p=%x *p = %d\n", __FUNCTION__, &q, &p, p, *p);
}
void func0(int* p)
{
*p = 100;
printf("[%s] &p=%x p=%x *p = %d\n", __FUNCTION__, &p, p, *p);
//printf("[%s] n=%d, &n=%x\n",__FUNCTION__, n, &n);
}
void func1(int* q, int n)
{
printf("[%s] &q=%x, q=%x\n",__FUNCTION__, &q, q);
q = malloc(sizeof(int)*n);
if( NULL == q)
{
printf("malloc err\n");
exit(1);
}
printf("[%s] &q=%x, q=%x\n",__FUNCTION__, &q, q);
}
void func2(int** qq, int n)
{
printf("[%s] &qq=%x, *qq=%x, qq=%x\n",__FUNCTION__, &qq, *qq, qq);
*qq = malloc(sizeof(int)*n);
if( NULL == *qq)
{
printf("malloc err\n");
exit(1);
}
printf("[%s] &qq=%x, *qq=%x, qq=%x\n",__FUNCTION__, &qq, *qq, qq);
}
int main()
{
int a = 1;
int* p = &a;
printf("[%s] a = %d, &p=%x, p=%x, *p = %d\n", __FUNCTION__,a, &p, p, *p);
func(p);
printf("[%s] a = %d, &p=%x, p=%x, *p = %d\n\n",__FUNCTION__,a, &p, p, *p);
int* q = NULL;
int n = 10;
q = &n;
printf("[%s] q=%d, &q=%x\n",__FUNCTION__, *q, q);
func0(q);
printf("[%s] q=%d, &q=%x\n\n",__FUNCTION__, *q, q);
q = NULL;
printf("[%s] &q=%x, q=%x\n",__FUNCTION__, &q, q);
func1(q, n);
printf("[%s] &q=%x, q=%x\n\n",__FUNCTION__, &q, q);
printf("[%s] &q=%x, q=%x\n",__FUNCTION__, &q, q);
func2(&q, n);
printf("[%s] &q=%x, q=%x\n\n",__FUNCTION__, &q, q);
free(q);
exit(0);
}
执行结果:
[main] a = 1, &p=d73a81a8, p=d73a81a0, *p = 1
[func] &q=d73a8184, &p=d73a8178 p=d73a81a0 *p = 1
[func] &q=d73a8184, &p=d73a8178 p=d73a8184 *p = 6
[main] a = 1, &p=d73a81a8, p=d73a81a0, *p = 1
[main] q=10, &q=d73a81a4
[func0] &p=d73a8188 p=d73a81a4 *p = 100
[main] q=100, &q=d73a81a4
[main] &q=d73a81b0, q=0
[func1] &q=d73a8188, q=0
[func1] &q=d73a8188, q=8fa420
[main] &q=d73a81b0, q=0
[main] &q=d73a81b0, q=0
[func2] &qq=d73a8178, *qq=0, qq=d73a81b0
[func2] &qq=d73a8178, *qq=8fa5c0, qq=d73a81b0
[main] &q=d73a81b0, q=8fa5c0
7.free(void*p)只是解除了p和某块地址的关系,有可能该地址存的值没变,甚至p还可能指向这块地址,p此时是个野指针——free了p之后p是个野指针,一定在free后将之置空为NULL。
8.不要犯错:指针初始化为NULL,然后直接对*p 赋值,造成段错误。p要指向某非空地址(要么用非空变量地址赋给p,要么用malloc申请内存空间)后,再赋具体的值
int* p = NULL;
//*p = 123;//指向空,然后直接赋值,直接段错误
9.注意如果结构体里有指针成员,要先初始化或者malloc之后再用;
如果指针为字符串指针,只要被赋值的字符串不是指向静态区字符串常量的指针就可以用strcpy。
scanf给结构体赋值时,别忘记&
void set_function(struct student* pst, struct student* psrc)
{
pst->id = psrc->id;
pst->math = psrc->math;
pst->chinese = pst->chinese;
pst->name = malloc(strlen(psrc->name + 1));//字符串有个尾零,strlen不统计尾零,所以加1
//此时psrc->name是字符串指针,pst->name是个未指向字符串常量静态区的指针,所以可以用strcpy
//不能用指针赋值,否则当psrc->name改了,pst->name也自动被改了
strncpy(pst->name, psrc->name, strlen(psrc->name+1));
}
void set(struct student *st)
{
printf("enter the id name math and chinese score:\n");
st->name = malloc(MAXNAME);
if(NULL == st->name)
exit(1);
scanf("%d%s%d%d", &st->id, st->name, &st->math, &st->chinese);//别忘取地址符
}
运行结果:
enter 1 for set, 2 for change name, 3 for show info, and q to quit:
1
enter the id name math and chinese score:
1 rrr 7 8
[set] end
enter 1 for set, 2 for change name, 3 for show info, and q to quit:
3
1 rrr 7 8
enter 1 for set, 2 for change name, 3 for show info, and q to quit:
2
enter new name:
yy
enter 1 for set, 2 for change name, 3 for show info, and q to quit:
3
1 yy 7 8
enter 1 for set, 2 for change name, 3 for show info, and q to quit:
q
10.typedefine
1)有分号,没#号,typedef是简写不是全称;define后面无分号
2)当定义指针时,define有类似优先级的问题,typedef一视同仁
include#include#define PD int*
typedef int* PTD;//有分号,没#号,typedef是简写不是全称
int main()
{
PD p, q;//只有p是int*,q是int
PTD pp, qq;//p和q都是int*
printf("%ld, %ld, %ld, %ld\n"
, sizeof(p), sizeof(q), sizeof(pp), sizeof(qq));
exit(0);
}
运行结果:
8, 4, 8, 8
3)typedef定义数组
typedef int ARR[6];//依据类型定义公式<-->typedef int[6] ARR;
ARR a; //<-->int a[6];
4)定义结构体指针:
无头结构体后跟变量名,用typedef定义结构体后面要跟要定义为的变量名
//虽然*和NODEP在一块,其实* 和前面的struct node_st一起构成重新定义的类型
typedef struct node_st *NODEP;
5)定义函数
定义普通函数
typedef int FUNC(int);
FUNC p;
定义指针函数,只要返回值时指针类型的就是指针函数
typedef int* FUNC(int);
FUNC p;
定义指向指针函数的函数指针
typedef int* (*FUNC)(int);
FUNC p;
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网页名称:C语言学习笔记9——动态内存管理-创新互联
当前地址:http://myzitong.com/article/jpocg.html