C语言 动态内存管理 #动态内存函数的介绍 #常见的动态内存错误 #C\C++ 程序的内存开辟 #、柔性数组

本文主要是介绍C语言 动态内存管理 #动态内存函数的介绍 #常见的动态内存错误 #C\C++ 程序的内存开辟 #、柔性数组,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

前言

一、为什么存在动态内存分配

二、动态内存函数的介绍

1、malloc

2、free

3、calloc

4、realloc

realloc 的工作原理:

三、常见的动态内存错误

1、对NULL指针的解引用操作

2、对动态开辟空间的越界访问

3、对非动态开辟的空间使用 free 来释放

4、使用 free 释放一块动态开辟空间的一部分

5、对同一块动态内存的多次释放

6、动态开辟的空间忘记释放而造成内存泄漏

四、C\C++ 程序的内存开辟

五、柔性数组

1、柔性数组的特点

2、柔性数组的使用

3、柔性数组的优势

总结


前言

路漫漫其修远兮,吾将上下而求索。


一、为什么存在动态内存分配

回顾以往使用内存空间的时候,我们既可以直接创建一个变量来申请一小块内存空间,同样也可以申请一大块内存空间(即数组的创建),但是这两种向内存申请空间的形式存在两缺点:

  • 开辟的空间大小是固定的,无法改变。
  • 创建数组时必须得指定其长度,数组所需要得空间会在编译期间进行分配;

联想到学习中写的通讯录,如果可以动态开辟空间的话,当已经开辟的空间不够的时候再开辟一块空间,不够用了再开辟……而不是一次性开辟一个足够大的空间来存放数据,这种方法不仅难以保证开辟的空间是否够用而且还极有可能浪费许多内存空间,无法做到“因地制宜”;故而动态内存分配的存在是合理的;

接下来我们便来了解一下动态内存;

二、动态内存函数的介绍

1、malloc

  • 功能:开辟内存空间
  • 所要引用的头文件 : <stdlib.h>
  • void* malloc ( size_t size);
  • 参数size 为所要开辟空间的大小,单位为字节
  • 如若参数 size 为0,malloc 会怎么去执行这是未被标准定义的,取决于编译器;
  • 返回类型为 void* ,故而使用者要根据所开辟空间的用于放置什么样类型的数据来进行强制类型转换;
  • 返回值:malloc 开辟空间成功时便会返回此空间的起始地址;倘若开辟空间失败便会返回NULL;故而,在使用 malloc 时还得判断它是否开辟空间成功;

malloc 的使用如下图,此处我使用malloc 的目的是想创建一块能存放10个整型的空间:

详细分析使用:

  • 注:你可能会有疑问,在main 函数中,结尾是 return 0; 而此处发生异常时(malloc 开辟空间失败)为什么 return 1

C语言中的习惯,当main函数 return 0 时以表示正常返回,而如若出现了问题便 return 1;

看到此处可能你会想说利用数组也可以完成以上操作,那么用数组与利用malloc 向内存申请空间,这二者有何区别呢?

二者存在两点不同;

一是在内存中申请的空间位置不同,数组是相同类型的集合,本质上就是变量创建所开辟的空间,即在栈区上申请空间,而malloc 开辟的空间位于堆区;

二是开辟后的空间的可改性,数组在栈区上申请的空间不可被修改,而malloc 在堆区上开辟的空间可以被修改;

当你看到数组的所占用的内存空间不可修改时,是不是会想到C99标准中的变长数组

敲重点!变长数组的意思并不是说这个数组的大小是可变的,而是说可以根据一个变量来确定一个数组的元素个数,而这个变量一旦确定下来后,数组元素的个数也就确定了;即变长数组可以动态输入一个数值来确定其数组元素的个数;

在上述代码中,我们利用malloc 向内存申请了空间,但是并没有free,便存在内存泄漏;

什么是内存泄漏?

  • 并不是表面意思内存不在了,其真正的意思为,当你利用malloc 向操作系统申请了一块内存空间,使用完之后你不用了,但是又没有释放即还给操作系统,那么别人也没有使用这块空间的权限,因为你占着“茅坑不拉💩”,于是乎别人想用也不行;

既然没有归还那为什么内存的空间没有丢失?

  • 因为,操作系统已经“进化”得很聪明了,他预判了用户在写代码得时候可能会忘记释放空间,于是乎当程序退出而你没有释放空间的时候,操作系统会自动收回该空间;

如何释放内存空间呢?

C语言提供了一个专门用来释放和回收动态开辟内存函数:free;

2、free

  • 功能:释放和回收动态开辟的内存
  • 所要引用的头文件 : <stdlib.h>
  • void free ( void * ptr);
  • 参数ptr 的类型为 void* ,意为动态开辟空间的起始地址;
  • 返回类型 为 void ,即无返回值;
  • 释放完该空间,要即使将指向此空间的指针置为NULL;因为此指针为野指针,若想要避免野指针就要在释放空间之后即使置空;

将上面 举例的malloc代码完善:

注:当使用了动态开辟内存空间的函数时,不用的时候就一定要利用free 将此空间释放了;

3、calloc

  • 功能:动态开辟内存空间并且将该空间中的内容初始化为0
  • 包含的头文件: <stdlib.h>
  • void* calloc ( size_t num, size_t size);
  • 第一个参数 num的类型为size_t ,表示的是动态开辟空间中元素的个数;第二个参数size 的类型为 size_t ,表示一个元素的大小
  • 返回类型为 void *
  • 当使用calloc 成功开辟空间的时候,便会返回该空间的起始地址;倘若失败则会返回NULL;

使用calloc 动态开辟能存放10个整型的空间:

注: malloc 仅在堆区开启了一块空间;而calloc 在堆区上开辟空间的基础上还对此空间中的数据初始化为了0;

相当于 calloc 包含了 malloc 的功能;也可以这样理解 calloc = malloc + memset;

    realloc(NULL, 40) ;  //等价于 malloc(40);    

你会不会有疑问,不是说动态开辟内存吗,但是上面的 malloc 和 calloc 也仅仅体现为开辟空间,”动态“体现在何处呢?请接着往下看,realloc 这个函数 与 malloc 和 calloc 的配合使用就能体现"动态内存开辟"喔~

4、realloc

  • 功能:重新动态开辟内存空间
  • 引用的头文件 : <stdlib.h>
  • viod * realloc (void * ptr , size_t size);
  • 第一个参数为 ptr,其类型为 void* 代表着在堆区上动态开辟空间的起始地址 ; 第二个参数为 size 其类型为 size_t 代表着重新开辟空间的大小(新空间的总大小),单位为字节;
  • 返回类型为 void* 
  • 返回值:如若利用 realloc 重新开辟空间成功,则会返回该空间的起始地址;倘若失败则会返回NULL;
  • realloc 也可能会开辟空间失败,故而也要进行排空的判断;

在calloc 开辟了 40 byte 空间的基础上,再开辟40 byte 大小的空间:

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>int main()
{//开辟空间int* pf = (int*)calloc(10, 4);if (pf == NULL){perror("calloc");return 1;}//使用int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(pf + i));}printf("\n");//空间不够,我要再开辟 40byte 的空间,所以新空间的大小为 80 byteint* ptr = (int*)realloc(pf, 80);//realloc 也有可能会重新开辟空间失败if (ptr == NULL){perror("realloc");return 1;}//再使用pf = ptr;memset(pf, 0, 80);for (i = 0; i < 20; i++){printf("%d ", *(pf + i));}//释放空间free(pf);pf = NULL;return 0;
}

代码运行结果如下:

  • 为何在上述代码中,realloc 的返回值要新创建一个指针 ptr 来接收?

如若用指向上例中 calloc 动态开辟的空间的指针pf 来接收realloc的返回是非常危险的;

 因为realloc 也有可能重新开辟空间而返回NULL,倘若realloc 返回NULL 并且用pf 来接收,那么便会找不到 calloc 开辟的空间(因为pf 指向calloc 所动态开辟的空间),就好比想让realloc 对旧空间进行“扩容”处理,没想到不仅realloc 扩容失败了,还将指向旧空间的指针pf 搞成了空指针,从而找不到此旧空间;

故而此处得创建一个新指针来接收 realloc 得返回值来避免“得不偿失”;

小技巧分享:在利用malloc、calloc 、realloc 开辟空间时其参数是所开辟空间的字节大小,直接写数字的字节数容易出错,那么就可以利用 sizeof(类型)*个数 的形式,例如 利用malloc 开辟能存放10个整型的空间便可以写作 int* pf = (int*) malloc (sizeof(int) * 10) ; 倘若想再利用realloc 扩容10个整型的空间 : int* ptr = (int* ) realloc ( pf , sizeof(int) * 20) ; 

此小技巧会强行给自己增加一个思考的流程,强烈推荐;(PS:本人在注意力不太集中的情况下极易将上例中 realloc 再次开辟的代码写作 : int * ptr = (int *) realloc ( pf , 40) ; 实际上其第二个参数应该是 80); 

realloc 的工作原理:

分为两种情况,一是旧空间后面的空间够用以开辟新空间,二是旧空间后面的空间不够用

情况一:

堆区中的空间一定也会存有数据故而有些空间占用,当所要重新开辟空间后面的空间不够时,realloc 便会在堆区上找哪块空间是空的并且足以放下新空间大小的内存空间,找到之后,会将"旧空间"中的数据拷贝放入新空间之中,然后再返回此空间的起始地址;(其中旧空间中的数据会自动释放)

情况二:

所要重新开辟的空间后面放得下所要增加的空间,于是就直接在此空间的原位上进行重新开辟空间的操作;

分别调试来看此两种情况:

情况一:

重新开辟400 byte 大小空间的时候,旧空间后面的空间大小不够,于是realloc 便会在堆区中重新找空间来重新开辟空间,于是 ptr 与 pf 中存放的地址不同;

情况二:

旧空间后面的空间足够用,于是就在旧空间的处重新开辟,故而 pf 与 ptr 中存放的地址相同;

总之,realloc 在使用的时候会出现以上两种情况,一是返回的地址为新地址,即此时在旧空间后面存放不下所要增加的空间,于是 realloc 在堆区上找了一块足够大的空间,先将旧空间中的数据拷贝放入新空间中,然后自动将旧空间释放,最后返回新空间的地址,二是返回的地址就是旧地址,此时旧空间后面有足够的空间可以放下增加的空间;

特别注意:如若频繁地使用 malloc、calloc、realloc 来开辟内存空间,便会使得内存空间的使用率下降以及效率下降;

  • 因为 malloc 、calloc 、realloc 是在堆区上动态申请的内存空间,而堆区中的内存空间是由操作系统管理的,故而当我们写下malloc、calloc、realloc 来向内存申请空间的时候,会调用操作系用提供的接口然后到堆区上申请空间每一次向内存申请空间均会打断操作系统的正常执行,因为操作系统要帮助我们在堆区上申请空间,然后我们才得以使用此块空间;
  • 显然,让操作系统来申请空间是会花费时间的,故而倘若你频繁地利用 malloc、calloc、realloc 来申请空间,频繁地打断操作系统从而导致操作系统的效率低下,并且会浪费大量的时间;
  • 再者,会导致该内存中的内存碎片较多,便会导致空间的利用率下降;

在平时写代码中,倘若难以避免多次使用 malloc、calloc、realloc,而产生较多的内存碎片,那么针对较多的内存碎片,该怎么办呢?  

在程序设计中,软件工程里有一个叫内存池的概念;

什么叫作内存池?(了解)

该程序首先会向内存申请一块相对来说可以满足当前程序所需的一块区域,而由于此块区域已分配给当前的程序,程序内部自己便会以内存池的方式来维护此空间;

当该程序想要使用一块空间,便会使用该内存池中的空间,不过用完之后便要将内存归还给内存池;正是程序自己在维护此块内存区域,于是将这块区域称为内存池;

内存池可以解决内存碎片,空间使用效率低下的问题;

三、常见的动态内存错误

1、对NULL指针的解引用操作

简单来说,就是在利用 malloc、 calloc 、 realloc 函数动态开辟内存空间的时候没有去判断他们有没有开辟空间成功,而直接去使用这些函数的返回值;

错误代码如下:

修改后正确的代码如下:

2、对动态开辟空间的越界访问

只用向内存申请了的空间该程序才有使用该空间的权限,该程序去访问、使用不属于该程序的空间便就是非法访问;

同理,对动态开辟所空间进行越界访问便也是非法访问;

3、对非动态开辟的空间使用 free 来释放

free 释放的空间是位于堆区上动态开辟的空间,即利用函数 malloc、calloc、realloc 所开辟的空间;倘若对非动态开辟的空间而利用free 释放是存在问题的;

4、使用 free 释放一块动态开辟空间的一部分

(释放动态内存开辟的空间,没有完全释放)

例子如下:

此时 pf 已经不再指向 malloc 所动态开辟空间的起始地址,free 无法释放动态开辟空间的一部分(硬性规定);

free 所释放的空间必须从动态所开辟空间的起始地址开始;

故而用于接收 malloc 所开辟空间的返回值的指针 pf 在使用的过程中不可被修改,准确来说得保证有个指针指向这块动态开辟得空间,以确保free 可以次空间释放;

  • 你可能会想到能不能用const 来修饰指针pf ,即效果如下:

显然也是不可行得,因为利用free 将空间释放之后,为避免野指针得将指向这块空间得指针及时置空,但是倘若用 const 来修饰 指针pf 便无法执行操作 pf=NULL;

5、对同一块动态内存的多次释放

错误代码如下:

针对此错误有两种解决方案:

方案一:确保动态开辟的空间只利用 free 释放一次

方案二:在使用free 释放空间之后及时置空

方案一:

方案二:

  • 为何 free(NULL); 不会产生太大的影响?

因为NULL 为空指针,空指针代表着没有明确的指向空间,而free 是将某个动态开辟的空间所释放;free (NULL); free 的参数为 NULL,便就意味着free 不知道释放哪块空间--> 不用释放空间;故而不会产生太大的影响;

6、动态开辟的空间忘记释放而造成内存泄漏

案例一:在函数中跳过了释放空间的代码

分析:在 test 函数中利用malloc 动态开辟的空间只能在 test 函数中被释放,因为用来接收malloc 返回值的指针pf是个局部变量,出了其作用域那么pf 便会被销毁即pf 所占用的内存空间会还给操作系统;故而倘若在test 函数之中未能将 pf 所指向的空间给释放了那么在后面便也没有可能能释放该空间;

案例二:使用封装的专门动态开辟内存空间的函数之后忘记释放空间

四、C\C++ 程序的内存开辟

内存分为内核空间、栈、内存映射段、堆、数据段、代码段

  • 内核空间是用来跑操作系统内核的一块空间,故而用户不可以对此空间进行访问;
  • 栈区(Stack):用来栈区主要存放运行函数而分配的局部变量、函数参数、返回地址等的空间,在使用栈区中的空间时默认先使用高地址处的空间(与环境有关);在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放-->函数栈帧的创建与销毁。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  • 堆区(Heap):前面学的利用malloc calloc realloc 动态开辟所申请的空间;即堆区中的空间一般是由程序员分配并释放,如若程序员不释放,程序结束的时候可能由OS回收,分配类似于链表;
  • 数据段是我们之前接触的静态区,是用来存放静态变量以及全局变量的空间,程序结束后由系统释放;
  • 代码段:存放编译之后形成可执行程序的二进制指令的空间;

内存中的布局分布如下图所示:

注:看到这,你可能就会对之前的知识:当局部变量被static 修饰时,其生命周期会变长 理解更加深刻了;

  • 因为局部变量存放在栈区,而栈区上的空间会随着函数的执行而创建或者销毁,也就是说当出了此局部变量的作用域,该局部变量便会销毁;而静态变量是存放在数据段的,数据段中的数据存在于整个程序的运行期间 ,并且在程序结束后才会由系统释放;故而当局部变量被static 修饰的时候,其生命周期会变长;

五、柔性数组

在C99标准中,结构中的最后一个元素允许为未知大小的数组,这便叫做柔性数组的成员;柔性数组在结构体变量中有且只有一个;

写法:

注:这两种写法选其一便可,在一些编译器上倘若使用写法一会报错那便使用写法二;

1、柔性数组的特点

  • 结构体中的柔性数组成员前面必须至少有一个其他成员
  • sizeof 返回的这种结构的大小不包括柔性数组所占的内存;
  • 包含柔性数组成员的结构体需要用malloc 对其进行动态内存的分配,并且所分配的内存应该大于结构体的大小以适应柔性数组的预期大小;

具体分析:

结构体中柔性数组成员的前面必须至少有一个其他成员;因为柔性数组所占用的内存空间的大小是不确定的,故而sizeof 返回的这种包含柔性数组的结构的大小时不会计算柔性数组的大小;

显然,"柔性"体现在可曲可伸,那么其空间的开辟必然会利用到动态内存开辟的相关知识;

2、柔性数组的使用

柔性数组作为结构体的成员,能否如同普通的结构体变量一样创建呢?

如下图所示:

显然这种写法是不可以的,因为sizeof(struct S) 的结果为4byte ,那么struct S s = {0};创建的结构体变量s 便只会向栈区申请 4byte 的内存空间,4 byte 大小的空间只能放下成员 i的数据,而没有给 数组a 任何的空间;故而此种写法不可行;

如何做?需要利用 malloc 进行动态内存的开辟;

代码如下:

#include<stdio.h>
#include<stdlib.h>struct S
{int i;int a[];
};int main()
{//struct S s ={0 }; 这是错误的写法struct S* pf = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 10);if (pf == NULL){perror("malloc");return EXIT_FAILURE;}//使用pf->i = 4;int j = 0;for (j = 0; j < 10; j++){(pf->a)[j] = j;}for (j = 0; j < 10; j++){printf("%d ", (pf->a)[j]);}//释放空间free(pf);pf = NULL;return 0;
}

代码运行结果如下:

图解如下:

倘若利用malloc 开辟的这10 个整型的空间不够用,还可以利用realloc 对内存进行调整;

看到此处你可能会在想:只要知道数组的起始地址,其元素类型以及元素个数,我们便可以访问数组所占用的这块空间;那么在结构体中,我们将数组的起始地址作为一个成员,再将malloc 动态开辟的空间的地址传给结构体中保存数组起始地址的指针当中;和前面的代码一样,让整个结构体在堆区上申请空间来存放数据,所以此处会用到两次malloc ,一次用来为整个结构体申请空间,一次是为数组申请空间;

图解如下:

代码如下:

#include<stdio.h>
#include<stdlib.h>struct S
{int i;int* a;
};int main()
{//为结构体开辟空间struct S* ps = (struct S*)malloc(sizeof(struct S));if (ps == NULL){perror("struct S malloc");return 1;}//为数组开辟空间int* pf = (int*)malloc(sizeof(int) * 10);if (pf == NULL){perror("a malloc");return 1;}//使用……//释放free(pf);free(ps);ps = NULL;return 0;
}

这两种方式哪方式好?

利用柔性数组的好,即第一种方式好;存在两个原因:

  • 1、第二种方法malloc 的次数比第一种方法malloc 的次数多;malloc 的次数越多,那么free的次数也会越多,那么忘记释放空间的可能性便会越大,即内存泄漏的可能性越大且更容易出错;
  • 2、如若在内存中频繁地进行malloc ,malloc 地次数越多,内存中产生内存碎片地可能性便会越大;在前面地学习中,我们已知内存碎片越多那么该程序内存地利用率与使用效率会下降;

故而,方法一优于方法二;

3、柔性数组的优势

1、方便内存释放

  • 如果我们的代码是在一个给人使用的函数中,你在里面做了两次内存分配,并把整个结构体返回给用户,用户调用free 便可以释放空间,但是用户并不知道你在这个结构体成员中又进行了一次内存开辟,那么用户便不会释放该空间,因为用户根本不知道;所以,如果我们要把结构体的内存以及其成员的内存一次性分配好,那么用户也仅仅需要free 一次空间即可;

2、有利于访问速度

  • 应用连续的内存来存放数据有利于提高该数据的访问速度;

总结

1、malloc calloc realloc free 的使用

  • void* malloc(size_t size);
  • void* calloc(size_t num , size_t size);
  • void* realloc( void* ptr , size_t size);
  • void free( void * ptr);

2、malloc calloc realloc 均有可能开辟空间失败,故而在使用之前要进行判空

3、释放动态开辟的空间之后置空,可以避免很多不必要的麻烦

4、内存分为内核空间、栈区、内存映射段、堆区、数据段、代码段

5、柔性数组存在于结构体中,并且在此结构体中有且只有一个柔性数组;在柔性数组成员之前至少有一个其他类型的成员;利用sizeof 计算该结构体的大小的时候,并不会计算柔性数组成员的大小;

6、柔性数组成员所占空间的大小需利用 malloc、calloc 来开辟,可以利用realloc 来调整其大小;

这篇关于C语言 动态内存管理 #动态内存函数的介绍 #常见的动态内存错误 #C\C++ 程序的内存开辟 #、柔性数组的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1119505

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�