1.使用内存的时候,可以创建一个变量或者创建数组,局部的属于栈区,全局的属于静态区。
2.栈区放置局部变量和函数的形式参数,
堆区进行动态内存分配,静态区放置全局变量和静态变量。
基本函数的理解
malloc
void*malloc(size_t size)
(1)这个函数向内存申请一块连续可用的空间,并且返回指向这片空间的指针。malloc的返回值一定要做检查。
(2)返回值的类型如果是void*,malloc函数并不知道开辟的空间的类型,具体使用的时候根据使用者自己来决定。
(3)如果参数size为0,malloc的行为是标准未定义的,取决于编译器。
?
#include<stdlib.h>
#include<string.h>
#include<error.h>
int main()
{
int *p=(*int)malloc(10*sizeof(int));
if(p==NULL)
{
printf("%s\n",strerror(error);
}
else{
int i=0;
for(i=0;i<10;i++)
*(p+i)=i;
free(p);
p=NULL;
return 0;
}
free
对动态内存空间进行回收和释放
?(1)如果参数ptr指向的空间不是动态内存开辟的,那么free的行为是未曾定义的。
(2)如果参数是NULL,则函数什么都不做。
calloc
开辟一块空间内容赋值为0 ?
int main()
{
int *p=(int*)calloc(10,sizeof(int));
if(p==NULL)
{
printf("%s",strerror(error));
}
else{
int i=0;
for(i=0;i<10;i++)
{
printf("%d",*(p+i));
}
return 0;
free(p);
p=NULL;
}
realloc
对动态开辟的内存大小进行调整
如果原有空间后面的空间足够用,直接追加上再返回原有的地址。 如果不够用,就再开辟一块新空间满足需求,并且把原来内存的数据拷贝回来,释放旧的内存空间,最后返回新开辟的内存空间地址.得用一个新的变量来接受realloc的返回值
void*realloc(void*ptr,size-t size)
? 实操案例:
int main()
{
int *p=(int*)malloc(20);
if(p==NULL)
{
printf("%s",strerror(errno));
}
else{
int i=0;
for(i=0;i<5;i++)
{
*(p+i)=i;
}
}
int *ptr=realloc(p,INT_MAX);
if(ptr!=NULL)
{
p=ptr;
int i=0;
for(i=5;i<10;i++)
{
*(p+i)=i;
}
for(i=0;i<10;i++)
{
printf("%d",*(p+i));
}
free(p);
p=NULL;
}
使用的误点
int main()
{
int *p=malloc(40);
*p=10;
}
void text()
{
int i;
int *p=(int*)malloc(10*sizeof(int));
if(NULL==p)
{
exit(EXIT_FAILURE);
}
for(i=0;i<=10;i++)
{
*(p+i)=i;
}
}
??3.
{
int a=10;
int*p=&a;
free(p);
p=NULL;
}
?
#include<stdlib.h>
#include<stdio.h>
int main()
{
int *p=(int*)malloc(40);
if(p==NULL)
{
return 0;
}
int i=0;
for(i=0;i<10;i++)
{
*p++=i;
}
free(p);
p=NULL;
?5.
#include<windows.h>
#include<stdio.h>
int main()
{
while(1)
{
malloc(1);
sleep(1000);
}
}
经典笔试题
? 题目一:
int main()
{
Test();
return 0;
}
void Test(void)
{
char *str=NULL;
GetMemory(str);
strcmp(str,"hello world");
printf(str);
}
void GetMemory(char*p)
{
p=(char*)malloc(100);
}
1.开辟空间后,p里面放的是开辟空间的首地址。 p是形参变量,在函数内部使用完以后返回主函数就消失了。当strcmp的时候把hello world传到空指针,使得程序崩溃了。
2.程序存在内存泄漏问题。 str以值传递的形式给了p. p是GetMemory的一个形参,只有在函数内部有效。 等GetMemory函数返回之后,动态开辟内存尚未释放会造成内存释放。
修改:
int main()
{
Test();
return 0;
}
void Test(void)
{
char *str=NULL;
GetMemory(*str);
strcmp(str,"hello world");
printf(str);
free(str);
str=NULL;
}
void GetMemory(char**p)
{
*p=(char*)malloc(100);
}
题目二:返回栈空间地址的问题
int main()
{
Text();
return 0;
}
void Text(void)
{
char*str=NULL;
str=GetMemory();
printf(str);
}
char* GetMemory(void)
{
char p[]="Hello world";
return p;
)
p[]是局部数组,在函数内部存在,倒是也把p返回来了。 但是当使用str的时候这个p已经被销毁了。会存在内存的非法访问问题。
题目三:
int*text()
{
int a=10;
return &a;
}
int main()
{
int *p=text();
*p=20;
return 0;
}
题目四:
int *f2(void)
{
int *ptr;
*ptr=10;
return ptr;
}
ptr没有赋值,是一块随机值,当解引用之后存在非法访问的问题。
题目五: ?
int main()
{
test();
return 0;
}
void Text()
{
char*str=(char*)malloc(100);
strcmp(str,"hello");
free(str);
if(str!=NULL)
{
strcmp(str,"world");
printf(str);
}
}
柔性数组
C99中结构体中的最后一个元素允许是未知大小的数组,是柔性数组的成员。
开辟方式一:
struct s{
int n;
int a[];
};
int main()
{
struct s S;
struct S* ps=(struct S*)malloc(sizeof(struct)+5*sizeof(int));
ps->n=100;
int i=0;
for(i=0;i<5;i++)
{
ps->arr[i]=i;
}
struct S*ptr=realloc(ps,44);
if(ptr!=NULL)
{
ps=ptr;
}
for(i=5;i<10;i++)
{
ps->arr[i]=i;
}
for(i=0;i<10;i++)
{
printf("%d",ps->arr[i])
}
free(ptr);
return 0;
}
开辟方式二:
?
struct S{
int n;
int*arr;
};
int main()
{
struct S*PS=(struct S*)malloc(sizeof(struct S));
ps->arr=malloc(5*sizeof(int));
int i=0;
for(i=0;i<5;i++)
{
ps->arr[i]=i;
}
int *ptr=realloc(ps->ptr,40);
if(ptr!=NULL)
{
ps->arr=ptr;
}
for(i=0;i<10;i++)
{
arr[i]=i;
}
free(ps->arr);
ps->arr=NULL;
free(ps);
ps=NULL;
return 0;
}
柔性数组的好处:
(1)第二个方式使用俩次malloc释放内存的时候更容易出错 (2)malloc开辟空间的时候,可能会有一些地方没有被开辟,内存碎片较多。柔性数组的内存碎片更少,内存利用率更高啦! (3)柔性数组的开辟空间的内存是连续的,访问的效率会更高。
寄存器,cache——高速缓存,内存,硬盘。 越往上速度越快,空间越小,造价越高。从寄存器里面拿数据,更快一些。
局部性原理:当你访问内存中的数据的时候,接下来百分之八十的可能性访问的是它周边的数据。
柔性数组的特点
1.结构中的柔性数组成员前面一定要有一个其他成员
2.sizeof返回的这种结构大小不包括柔性数组的内存
3.包含柔性数组成员的结构用配malloc()函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,来适应柔性数组的预期大小。
?
数据类型
c语言类型==内置类型+自定义类型(构造类型)
==整型+浮点型+构造+数组类型**int arr[10] 类型int [10]**
结构体类型 枚举类型 联合类型)+ 指针 +空类型
类型一来决定了这个类型开辟的空间大小,一来决定了如何看待内存空间的视角
整型存储
整型存储 int a=-10; 内存中存二进制补码:1111 1111 1111 1111 1111 1111 1111 0110 转换为16进制 ff ff ff f6
unsigned char(0----2^8-1) (-127----128)
大端小端:
大端:数据的高位存在低地址中,低位存在高地址中
小端:数据的低位存在高地址中,高位存在低地址中
低地址————————>高地址
11 22 33 44(大端 44 33 22 11(小端
指针类型解引用决定了指针解引用操作可以访问几个字节
决定了+1或-1加的是几个字节
int main()
{ int a=1;
char*p=(char*)&a;
if(*p==1)
{
printf("小端");
}
else printf("大端");
return 0;
}
习题
无符号数>=0 ,所以下面的程序进入死循环。
int main()
{
unsigned int i;
for(i=9;i>=0;i--)
{ printf("%u",i)}
}
i== -1 -2 -3… -128 ----->127 3 2 1 0 字符串长度 (到0之前)(127+128=255)
int main()
{
char a[1000];
int i;
for(i=0;i<1000;i++)
{
a[i]=-1-i;
}
printf("%d",strlen(a));
}
浮点型在内存中的存储
int main()
{
int n=9;
float *pFloat=(float*)&n;
printf("%d",n);
printf("%f",*pFloat);
*pFloat=9.0;
printf("%d",n);
printf("%f",*pFloat);
}
结果为9 0.000000 1091567616 9.000000
内存的具体存储
-
计算机内部的程序的内存相互独立(A不可访问B)
内存2区(B) 内存1区(A)
2.栈区:存储临时变量(当变量超出当前作用域会弹出)
3.堆区:开辟大的内存空间(对动态内存进行分配)————开发人员分配,使用完都释放
4.数据区:存放全局变量,常量,静态变量
5.代码区:存储可执行代码 ?
|