动态内存开辟必看,一篇就能学会贯通

news2024/11/18 7:41:18

目录

为什么存在动态内存分配

动态内存函数的介绍

1.malloc函数和free函数

2.calloc函数

3.realloc函数

常见的动态内存错误

经典的笔试题

柔性数组


                                         今天来介绍一下动态内存,让我们直入主题!!!!!

 

为什么存在动态内存分配

目前我们掌握的内存开辟的方式有:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = { 0 };//在栈空间上开辟10个字节的连续空间

        但是上述的开辟空间的方式有两个特点

        1.空间开辟的大小是固定的。

        2.数组在声明的时候,必须指定数组的长度,他所需要的内存在编译时分配

        但是对空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组在编译时开辟空间的方式就不能满足,这时就需要动态内存开辟的帮助。

动态内存开辟
栈区局部变量、函数形参
堆区动态内存开辟
静态区(数据段)全局变量、静态变量

动态内存开辟:malloc()    free()   realloc()    calloc()

动态内存函数的介绍

 

1.malloc函数和free函数

C语言提供了一个动态开辟的函数

 

malloc

void* malloc(size_t size)

        这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

  1. 如果开辟成功。则返回一个指向开辟好空间的指针。

  2. 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

  3. 返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。

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

free

void free(void* ptr);

        free函数是用来释放动态开辟的内存的。

  1. 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。

  2. 如果参数ptr是NULL指针,则函数什么事情都不做。

        malloc和free都声明在stdlib.h头文件中,一般都是成对出现。

#include<stdio.h>
#include<stdlib.h>
//动态内存开辟
int main()
{
    //假设开辟10个整型的空间 - 10* sizeof(int)
    int arr[10];//栈区
    //动态内存开辟
    int* p = (int*)malloc(10*sizeof(int));//void*,最好强制类型转换
    if(p == NULL)
    {
        perror("main");//main:xxxxxxxxx
        return 0;
    }
    //使用
    int i = 0;
    for(i = 0;i < 10; i++)
    {
        *(p + i) = i;
    }
    for(i = 0;i < 10; i++)
    {
        printf("%d ",p[i]);//p[i] 等价于 *(p+i)
    }
    //回收空间
    free(p);
    p = NULL;//自己动手才能把p赋值为空指针
    return 0;
}

2.calloc函数

C语言还提供了一个函数叫calloc,calloc函数也用来动态内存分配。

void* calloc(size_t num, size_t size);

        calloc函数会初始化内存,而malloc函数不会。

  1. 函数的功能是为了num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0;

  2. 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为0。

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int*p = (int*)malloc(40);
    if(p == NULL)
        return 1;
    for(int i = 0;i < 10; i++)
    {
        printf("%d\n",*(p + i));
    }
    free(p);
    p = NULL;
    return 0;
}
7561600
0
7536976
0
1986622020
1631875685
1157652852
1413694796
1598967634
1598969170

        我们发现malloc函数确实不能初始化内存,如果用calloc函数就可解决这个个问题。

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int*p = (int*)calloc(10,sizeof(int));
    if(p == NULL)
        return 1;
    for(int i = 0;i < 10; i++)
    {
        printf("%d\n",*(p + i));
    }
    free(p);
    p = NULL;
    return 0;
}
0
0
0
0
0
0
0
0
0
0

3.realloc函数

 

  1. realloc函数的出现让动态内存管理更加灵活

  2. 有时候我们发现过去申请的空间太小了,有时候我们又觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小作灵活调整。那realloc函数

  3. 当我们需要扩大空间的时候,会有后面空间不够的情况,为了避免占用已经分配好的空间位置,realloc函数会在堆区空间充足处重新拷贝上面的内容,并把重新分配到的地址传回指针,把原来的空间还给操作系统。

  4. realloc函数可能找不到合适的空间来调整空间,就会返回NULL。

void* realloc(void* ptr, size_t size);
  1. ptr是要调整的内存空间

  2. size调整之后的新的大小

  3. 返回值为调整之后的内存起始位置

  4. 这个函数调整原内存空间大小的基础上,还会将于俺俩内存中的数据移动到新的空间。

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int* p = (int*)calloc(10, sizeof(int));
    if(p == NULL)
    {
        perror("main");
        return 1;
    }
    //使用
    int i = 0;
    for(i = 0;i < 10; i++)
    {
        *(p + i) = 5;
    }
    //这里需要p指向的空间更大,需要20个int的空间
    //realloc调整空间
    int* ptr = (int*)realloc(p, 20*sizeof(int));//因为realloc函数可能传回空指针,为了原先空间不丢失,故临时创建指针接受
    if(ptr != NULL)
    {
        p = ptr;
    }
    free(p);
    p = NULL;
    return 0;
}

情况1

        情况一是后面有充足的空间。要扩展内存就直接在原有内存之后追加空间,原有空间不发生变化。

情况2

        情况二是后面空间不足。在这种情况下,原有空间不足的拓展办法是在对空间另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址,原有空间还给操作系统。

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int*p = (int*)realloc(NULL, 40);//这里的功能类似于malloc,就是在堆区开辟40个字节
    return 0;
}

常见的动态内存错误

 

对NULL指针的解引用操作

void test()
{
    int* p = (int*)malloc(INT_MAX/4);
    *p = 20;//如果p的值是NULL,就会有问题
    free(p);
}
void test()
{
    int* p = (int*)malloc(10000000000000);
    //要对malloc函数的返回值,做判断
    int i = 0;
    for(i = 0;i < 10; i++)
    {
        *(p + i) = i;
    }
    free(p);
}

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

#include<stdio,h>
#include<stdlib.h>
int main()
{
    int* p = malloc(10 * sizeof(int));
    if(p == NULL)
    {
        return 1;
    }
    int i = 0;
    //越界访问
    for(i = 0;i < 40; i++)
    {
        *(p + i) = i;
    }
}

对非动态内存函数使用free

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int arr[10] = { 0 };
    int* p =arr;
    //使用非动态内存空间是错误的
    free(p);
    p = NULL;
    return 0;
}

使用free函数释放动态内存中一部分

int main()
{
    int* p = malloc(10 * sizeof(int));
    if(p == NULL)
    {
        return 1;
    }
    for(int i = 0;i < 5; i++)
    {
        *p++ = i;//指针p往后走,起始位置改变
    }
    free(p);
    return 0;
}

对同一块动态开辟内存进行多次释放

int main()
{
    int* p = (int*)malloc(100);
    //使用
    //释放
    free(p);
    //释放
    free(p);//两次明目张胆地释放同一块空间是绝对不可以的
    return 0;
}

        当然也可以规避这个问题,那就是把p指针赋值为NULL,那么再次free是没有问题的,看如下代码:

int main()
{
    int* p = (int*)malloc(100);
    //使用
    //释放
    free(p);
    p = NULL;
    //释放
    free(p);
    return 0;
}

动态开辟的空间忘记释放

void test()
{
    int* p = (int*)malloc(100);//内存泄漏
    if(p == NULL)
    {
        return;
    }
    //使用
}
int main()
{
    test();
    //……
    return 0;
}

动态开辟空间回收方式:

  1. 主动释放,free

  2. 程序彻底结束

经典的笔试题

 

第1题

void GetMemory(char* p)
{
    p = (char *)malloc(100);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(str);
    strcpy(str,"hello world");
    printf(str);
}
请问运行Test函数会有什么样的结果?

        p得到了str地址,所以p也存放了NULL空指针。然后申请了100字节,地址传给p,但是p是形参局部变量,p返回之后就会销毁。所以str没有指向任何一个空间,所以没有办法获得空间拷贝hello world

        str传给GetMemory函数的时候是值传递,所以GetMemory函数的形参p是str的一份临时拷贝。在GetMemory函数内部动态申请空间的地址,存放在p中,不会影响外边的str,所以当GetMemory函数返回之后,str仍然是NULL,所以strcpy会失败。

        当GetMemory函数返回之后,形参p销毁,使得动态开辟的100个字节存在内存泄漏。无法释放。

修改如下:

修改1:

char* GetMemory(char* p)
{
    p = (char *)malloc(100);
    return p;
}
void Test(void)
{
    char *str = NULL;
    str = GetMemory(str);
    strcpy(str,"hello world");
    printf(str);
    free(str);
    str = NULL;
}

修改2:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char** p)
{
    *p = (char *)malloc(100);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(&str);
    strcpy(str,"hello world");
    printf(str);
    free(str);
    str = NULL;
}
int main()
{
    Test();
    return 0;
}
hello world

第2题

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//GetMemory函数内部创建的数组是在栈区上创建的
//出了函数,p数组的空间就还给了操作系统
//返回的地址是没有实际的意义,如果通过返回的地址去访问内存就是非法访问地址
char* GetMemory(void)
{
    char p[] = "hello world";
    return p;
}//出了这个括号就销毁了,叫做返回栈空间地址的问题
void Test(void)
{
    char *str = NULL;
    str = GetMemory();
    printf(str);
}
int main()
{
    Test();
    return 0;
}

无法得到想要的结果

数组p里放了:hello world\0

str : NULL

char* GetMemory(void) { char p[] = "hello world"; return p; }//出了这个括号就销毁了

这种问题叫做返回栈空间地址的问题

第3题

void Test(void)
{
    char *str = (char*)malloc(100);
    strcpy(str, "hello");
    free(str);
    if(str != NULL)//非法访问
    {
        strcpy(str, "world");
        printf(str);
    }
}
int main()
{
    Test();
    return 0;
}

free以后的str一定要指空,如果不把str=NULL的话,就是非法访问

第4题

test.c文件中包括如下语句:
#define INT_PTR int*//INT_PTR是一个指针
typedef int* int_ptr;//typedef是类型重定义
INT_PTR a,b;//int* a,b;
int_ptr c,d;//int* c,*d;
文件中定义的四个变量,哪个变量不是指针类型:(b)

百度程序题

int *f1(void)
{
    int x = 10;//x是局部变量,处于栈区,返回地址无用
    return(&x);
}
​
int *f2(void)
{
    int *ptr;
    *ptr = 10;//ptr是一个野指针
    return ptr;
}
​

C/C+程序内存分配的几个区域:

1.栈区(stack):在执行函数时,函数内局部变量的存储单元部可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限,栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。

2.堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可以由OS回收。分配方式类似于链表。

3.数据段(静态域)(static)存放全局变量、静态数据。程序结束后由系统释放。

4.代码段:存放函数体(类成员函数和全局函数)的二进制代码。

柔性数组

也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做[柔性数组]成员。

例如:

typedef struct st_type
{
    int i;
    int a[0];//柔性数组成员,大小是未知的
}type_a;

柔性数组的特点:

  1. 结构中的柔性数组成员前必须至少有一个其他成员。

  2. sizeof返回的这种结构大小不包括柔性数组的内存。

  3. 包含柔性数组成员的结构malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的语气大小。

#include<stdio.h>
struct S
{
    int n;
    int arr[0];//大小未知
};
​
int main()
{
    struct S s = {0};
    printf("%d\n",sizeof(s));
    return 0;
}
输出结果:4//sizeof返回不包括柔性数组大小

利用malloc函数开辟空间

写法1

#include<stdio.h>
#include<stdlib.h>
struct S
{
    int n;
    int arr[0];//大小未知
};
​
int main()
{
    //期望arr的大小是10个整型
    struct S*ps = (struct S*)malloc(sizeof(struct S) + 10*sizeof(int));
    ps->n = 10;
    int i = 0;
    for(i = 0;i < 10; i++)
    {
        ps->arr[i] = i;
    }
    for(i = 0;i < 10; i++)
    {
        printf("%d ",ps->arr[i]);
    }
    //增加
    struct S* ptr = (struct S*)realloc(ps, sizeof(struct S)+20*sizeof(int));
    if(ptr != NULL)
    {
        ps = ptr;
    }
    //释放
    free(ps);
    ps = NULL;
    return 0;
}

还可以利用指针来替代柔性数组写成如下代码

写法2

#include<stdio.h>
#include<stdlib.h>
struct S
{
    itn n;
    int* arr;
};
​
int main()
{
    struct S* ps = (struct S*)malloc(szieof(struct S));
    if(ps == NULL)
    {
        return 1;
    }
    ps->arr = (int*)malloc(10* szieof(int));
    if(ps->arr == NULL)
    {
        return 1;
    }
    for(int i = 0;i < 10;i++)
    {
        ps->arr[i] = i;
    }
    //增加
    int* ptr = realloc(ps->arr, 20*sizeof(int));
    if(ptr != NULL)
    {
        ps->arr = ptr;
    }
    //先free第二块空间,如果free第一块的话就会造成地址丢失,就会造成内存泄漏
    //两次malloc需要两次free
    //释放
    free(ps->arr);
    ps->arr = NULL;
    free(ps);
    ps = NULL;
    return 0;
}

上述代码1和代码2可以完成同样的功能。但是方法1的实现有两个好处

  1. 第一个好处是:方便内存释放 如果我们的代码是在一个给别人用的函数中,你在里面做了二次存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。

  2. 第二个好处是:这样有利于访问速度 连续的内存有益于提高访问速度,也有益于减少内存碎片(其实,我个人觉得也没多高了,反正你跑不了要用做偏移量的加法来寻址)。

酷壳——CoolShell的《C语言结构体里的成员数组和指针》

内存池的概念

  内存池是一种内存分配方式。通常我们习惯直接使用new、malloc等接口申请内存,这样做的缺点在于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。

  内存池则是在真正使用内存之前,预先申请分配一定数量、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。

内存池的流程和设计

  1. 先申请一块连续的内存空间,该段内存空间能够容纳一定数量的对象。

  2. 每个对象连同一个指向下一个对象的指针一起构成一个内存节点(Memory Node)。各个空闲的内存节点通过指针形成一个链表,链表的每一个内存节点都是一块可供分配的内存空间。

  3. 某个内存节点一旦分配出去,从空闲内存节点链表中去除。

  4. 一旦释放了某个内存节点的空间,又将该节点重新加入空闲内存节点链表。

  5. 如果一个内存块的所有内存节点分配完毕,若程序继续申请新的对象空间,则会再次申请一个内存块来容纳新的对象。新申请的内存块会加入内存块链表中。

局部性原理

  1. 空间局部性:当使用一个空间的情况下,80%的可能会使用周边的空间,不会随意随机地挑选空间。效率会高一些。

  2. 一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,这是因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。——如果一个存储器的位置被引用,那么将来他附近的位置也会被引用。

  3. 时间局部性

    如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。——被引用过一次的存储器位置在未来会被多次引用(通常在循环中)。

        当然有些编译器会报错无法编译

         希望大家能点个赞支持一下!!!!蟹蟹蟹蟹!!!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Cocos新手入门】cocos creator 的研发思路和工具操作说明

本篇文章主要讲解cocos creator 的研发思路和工具操作说明 作者&#xff1a;任聪聪 日期&#xff1a;2023年1月29日 研发思路 关于cocos creator 工具说明 首先cocos creator 是一个编辑游戏界面的窗口&#xff0c;省去了我们日常开发游戏时频繁修改参数调整动画、场景的工作…

Siam R-CNN: 通过重检测进行视觉跟踪

Siam R-CNN: 通过重检测进行视觉跟踪Siam R-CNN: Visual Tracking by Re-DetectionContributionsMethodSiam RCNNVideo Hard Example MiningTracklet Dynamic Programming Algorithm实验总结更多Ai资讯&#xff1a;公主号AiCharm Siam R-CNN: Visual Tracking by Re-Detectio…

DMETL5单机版安装使用

DMETL5安装使用 1.创建dm8数据库 使用dm数据库配置助手dbca创建数据库 2.根据dmetl在线文档创建HOTEL模式 DROP USER IF EXISTS HOTEL CASCADE; DROP TABLESPACE IF EXISTS HOTEL; CREATE TABLESPACE HOTEL DATAFILE HOTEL.DBF SIZE 150 AUTOEXTEND ON NEXT 10; CREATE USE…

基于springboot的仓库管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

增鑫科技更新招股书,冲刺深交所上市,正邦集团是其主要股东

近日&#xff0c;江西增鑫科技股份有限公司&#xff08;下称“增鑫科技”&#xff09;预披露更新招股书&#xff0c;准备在深圳证券交易所主板上市。据贝多财经了解&#xff0c;增鑫科技曾于2022年7月1日递交招股书&#xff0c;此次更新了截至2022年6月30日的财务数据等信息。 …

权威报告!免费解锁IBM最新《2022-2023年Salesforce状态报告》

前不久&#xff0c;IBM发布了2022-2023年Salesforce状态报告&#xff0c;揭示了一些热门趋势&#xff0c;报告显示Salesforce仍然是许多企业客户成功战略的重要力量。 Salesforce状态报告是一项全球性的、数据驱动的调查&#xff0c;主要调查业务战略、投资和发展&#xff0c;同…

Linux常用命令——rpm命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) rpm RPM软件包的管理工具 补充说明 rpm命令是RPM软件包的管理工具。rpm原本是Red Hat Linux发行版专门用来管理Linux各项套件的程序&#xff0c;由于它遵循GPL规则且功能强大方便&#xff0c;因而广受欢迎。逐…

5.2 晶体管的高频等效模型

从晶体管的物理结构出发&#xff0c;考虑发射结和集电结电容的影响&#xff0c;就可以得到在高频信号作用下的物理模型&#xff0c;称为混合 π\pmb{π}π 模型。由于晶体管的混合 πππ 模型与 hhh 参数等效模型在低频信号作用下具有一致性&#xff0c;因此&#xff0c;可用 …

Unity Native Plugin C#和C++互相调用

官方链接 1.DLL的方式&#xff1a; C代码&#xff1a;编译成DLL&#xff0c;导入Unity #pragma once #include <map> #include <string>//导出宏定义 #define _DllExport _declspec(dllexport)//函数指针 typedef void (*NativeCallback)(const char*);extern &…

【28】C语言 | 关于指针练习(2)

目录 10、下列关于二维数组输出 11、下列关输出 12、下列代码输出什么 13、下列代码输出什么 14、下列代码输出什么 15、下列代码输出什么 16、下列代码输出什么 17、下列代码输出什么 18、杨氏矩阵 19、左旋转两个字符 10、下列关于二维数组输出 int main() {int …

文献阅读:Improving Language Understanding by Generative Pre-Training

文献阅读&#xff1a;Improving Language Understanding by Generative Pre-Training 1. 文章简介2. 模型介绍3. 实验考察 1. 训练数据2. 实验结果3. 消解实验 4. 总结 & 思考 文献链接&#xff1a;https://cdn.openai.com/research-covers/language-unsupervised/languag…

P1464 Function————C++

文章目录题目Function题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1解题思路Code运行结果题目 Function 题目描述 对于一个递归函数 w(a,b,c)w(a,b,c)w(a,b,c) 如果 a≤0a \le 0a≤0 或 b≤0b \le 0b≤0 或 c≤0c \le 0c≤0 就返回值$ 1$。如果 a>20a>20a&…

【Java】-【并发】

文章目录堆和方法区中的数据是可以被共享的堆中的数据是被栈中的变量所持用的&#xff0c;栈是线程隔离的&#xff0c;每个线程私有一个栈&#xff0c;所以栈中的数据不共享调用a方法时&#xff0c;jvm会给a方法创建一块内存区&#xff0c;让其入栈&#xff0c;这块区域被称为a…

linux(信号量)

信号量几个基本概念临界资源临界区原子性互斥信号量后台进程前台进程信号储存信号处理信号(信号捕捉)发送信号1、键盘产生&#xff1a;2、系统调用接口发送信号3、由软件条件产生信号4、硬件异常发送信号内核中的信号量**信号量在内核中的数据结构****信号集操作函数**信号的检…

YOLOv7 Falsk Web 监测平台 | YOLOv7 Falsk Web 部署

YOLOv7 Falsk Web 监测平台图片效果展示 YOLOv7 Falsk Web 监测平台视频效果展示 YOLOv7 Flask Web 检测平台 什么是Flask? 简介 Flask是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。它可以很好地结合MVC模式进行开发,开…

电子技术——MOS管的小信号模型

电子技术——MOS管的小信号模型 在上一节&#xff0c;我们已经学习过了MOS管的基本线性放大原理&#xff0c;本节我们继续深入MOS管的小信号放大&#xff0c;建立MOS管的小信号模型。 我们本节继续使用上节的电路&#xff0c;如下图所示&#xff1a; DC偏置点 根据上节的知识…

2.单例模式,工厂模式,建造者模式,原型模式

单例模式 单例模式的优点&#xff1a; 处理资源访问冲突表示全局唯一类 实现单例的关键&#xff1a; 构造函数需要是 private 访问权限的&#xff0c;这样才能避免外部通过 new 创建实例&#xff1b;考虑对象创建时的线程安全问题&#xff1b;考虑是否支持延迟加载&#xff1b…

Deformable DETR TBD范式的不二选择

TBD范式检测和跟踪是不分家的。当前,性能较优的目标检测方法大都基于Transformer来做,CNN在目标检测的表现逐渐走低。DETR是基于Transformer的目标检测开山作,其解决了霸榜的yolo系列一些令人讨厌的事情,不需要前处理和后处理,做到了真正意义上的end to end: 前处理:Anc…

测试做得好,犯错少不了【30个最容易犯的错误】谨记

最近跟一些刚刚进入软件测试行业的朋友去交流&#xff0c;发现了一个有趣的现象&#xff0c;就是对于这个行业的很多问题的认识都是一致的片面&#xff0c;当然也可以理解为误区。自己利用一点时间&#xff0c;把他们对于这个行业的认识误区都罗列出来&#xff0c;然后结合自己…

Centos7搭建Hadoop集群(V3.3.4)

Centos7搭建Hadoop集群V3.3.4一、准备工作1、配置hostname2、hosts映射3、关闭防火墙4、同步时间5、关闭selinux6、配置ssh免密登陆7、重启二、安装所需环境1、jdk安装2、hadoop安装三、修改配置hadoop-env.shcore-site.xmlhdfs-site.xmlmapred-site.xmlyarn-site.xmlworkers四…