IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言动态内存分配+柔性数组处理+数据类型 -> 正文阅读

[C++知识库]C语言动态内存分配+柔性数组处理+数据类型


1.使用内存的时候,可以创建一个变量或者创建数组,局部的属于栈区,全局的属于静态区。
2.栈区放置局部变量和函数的形式参数, 堆区进行动态内存分配,静态区放置全局变量和静态变量。

基本函数的理解

malloc

void*malloc(size_t size)
//向内存中申请size个字节,申请成功后返回地址,失败的话返回空指针
//打印失败云隐strerror

(1)这个函数向内存申请一块连续可用的空间,并且返回指向这片空间的指针。malloc的返回值一定要做检查。

(2)返回值的类型如果是void*,malloc函数并不知道开辟的空间的类型,具体使用的时候根据使用者自己来决定。

(3)如果参数size为0,malloc的行为是标准未定义的,取决于编译器。

?

#include<stdlib.h>
#include<string.h>
#include<error.h>

int main()
{
  //向内存中申请10个整型的空间
   int *p=(*int)malloc(10*sizeof(int));
   //把*void强制转换为*int
   if(p==NULL)
   {
    printf("%s\n",strerror(error);
    //把错误码转换为错误信息后打印
   }
else{
 //正常使用空间
 int i=0;
 for(i=0;i<10;i++)
   *(p+i)=i;
//当申请完的内存空间不再使用的时候用free来释放空间
  free(p);
  p=NULL;
 //p只free了相当于分手之后还留着电话号码,通过电话号码还可以给人家打电话,但是变成空号了就不行了
   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)
//ptr是要调整的内存地址  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;
  }
}

//就是在使用malloc开辟的20个空间 到这里发现不够用了 我们希望有一个40个字节的空间 这里就能用realloc来调整动态开辟的内存

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;
}

使用的误点


//对NULL指针的解引用操作
int main()
{
   int *p=malloc(40);
   *p=10;//malloc开辟的空间失败,对NULL进行解引用操作
}

//对动态开辟的内存空间越界访问
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.

//对非动态开辟的内存使用free释放
{
  int a=10;
  int*p=&a;
  free(p);
  p=NULL;
}
?

#include<stdlib.h>
#include<stdio.h>

//使用free释放动态内存的一部分
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;//由于p在不断变化,已经不是指向malloc刚刚第一次开辟的空间了

?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);
  //str传的是值 传自己过去是传值 传地址过去是传址
  strcmp(str,"hello world");
  printf(str);//写法没问题
}
void GetMemory(char*p)//接收指针变量的地址得用二级指针
{
  p=(char*)malloc(100);
}//运行的结果是什么,bug有什么

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;//保存在栈区之中
  //static int a=20;保存在静态区就可以了
  //堆区开辟一块空间也行,只有当free的时候才会被回收
  return &a;//出栈的时候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);
//释放内存后仍然非法访问并且使用
//free释放str指向的空间之后,并不会把str置为NULL
//篡改动态内存区的内容,后果难以预测,非常危险
//因为free(str)之后,str成为野指针,if(str!=NULL)不起作用
  if(str!=NULL)
     {
           strcmp(str,"world");
           printf(str);
      }
}

柔性数组

C99中结构体中的最后一个元素允许是未知大小的数组,是柔性数组的成员。

开辟方式一:
struct s{
  int n;
  int a[];
//未知大小柔性数组成员—柔性数组——数组的大小是可以改变的 或者写成int a[0];
};

int main()
{
   struct s S;
  //计算包含柔性数组的结构体的大小的时候不包含柔性数组的大小
   struct S* ps=(struct S*)malloc(sizeof(struct)+5*sizeof(int));//开辟了24个字节
   
    ps->n=100;
    int i=0;
    for(i=0;i<5;i++)
     {
        ps->arr[i]=i;//0 1 2 3 4
     }

   struct S*ptr=realloc(ps,44);//用realloc来调整数组的大小
   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开辟了一块空间,有n和arr
 ps->arr=malloc(5*sizeof(int));//arr又指向一块空间
 
 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

内存的具体存储

  1.      计算机内部的程序的内存相互独立(A不可访问B)
    

内存2区(B)
内存1区(A)

2.栈区:存储临时变量(当变量超出当前作用域会弹出)

3.堆区:开辟大的内存空间(对动态内存进行分配)————开发人员分配,使用完都释放

4.数据区:存放全局变量,常量,静态变量

5.代码区:存储可执行代码
?

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:10:02  更:2022-03-08 22:10:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:36:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码