【C语言进阶】动态内存管理详解与常见动态内存错误以及柔性数组使用与介绍
📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:C语言进阶
🎯长路漫漫浩浩,万事皆有期待
文章目录
- 1.动态内存
- 1.1 概述:
- 1.2.动态内存分配的意义:
- 2.常用的动态内存函数
- 2.1 malloc 和 free:
- 2.1.1 malloc 函数:
- 2.1.2 free 函数:
- 2.1.3 malloc 函数与 free 函数的使用:
- 2.2 calloc 函数:
- 2.3 realloc 函数:
- 3. 常见动态内存错误
- 3.1 对 NULL 指针的解引用操作
- 3.2 对动态内存空间的越界访问
- 3.3 对非动态内存空间使用 free 函数
- 3.4 使用 free 函数释放动态内存空间的一部分
- 3.5 对同一块动态内存空间多次释放
- 3.6 不释放动态内存空间(内存泄漏)
- 4. 柔性数组:
- 4.1 柔性数组的特点:
- 4.2 柔性数组的使用:
- 4.3 柔性数组的优势:
- 5.总结:
1.动态内存
1.1 概述:
在c/c++语言中,所谓动态内存分配,就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
1.2.动态内存分配的意义:
我们在之前的学习过程中,在使用各种变量与数组等等功能时,都需要从内存中开辟出一片空间用于存放我们的数据,而在之前我们掌握的内存开辟方式有:
int value = 20;
//在内存栈空间上开辟4个字节的空间
char arr[10] = { 0 };
//在栈空间上开辟10个字节的连续空间
但是我们最经常时使用的这两种内存空间的开辟方式有一些共同的特点:
1.空间开辟大小是固定的。
2.数组在申明的时候必须指定数组的长度,它所需要的内存将会在编译时分配。
换句话说,这两种内存开辟方式都是静态内存分配
。
但是我们在日常的代码编写和程序使用过程中,对于空间的需求往往不仅限于上述情况。更多的时候我们需要的空间大小只有在在程序运行的时候才能知道。如此,数组等在编译时开辟空间的方式无法满足我们的实际运行需求。于是就需要使用一种更好的内存分配方式进行处理:动态内存分配
2.常用的动态内存函数
2.1 malloc 和 free:
malloc 函数(memory allocate,即内存分配)的作用为向内存的堆区申请空间来存储数据,free 函数的作用为释放使用 malloc 函数向堆区申请的空间,并将空间归还给内存的堆区空间,通过配合使用这两个函数可以从堆区申请(释放)动态内存空间。
2.1.1 malloc 函数:
malloc 函数的使用格式为:
void* malloc (size_t size);
从它的使用格式中我们可以看出,该函数向堆区申请了一块连续的空间,同时返回的是这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个 NULL 指针,故我们在使用时一定要仔细检查 malloc 函数的返回值。
- 返回值的类型是 void* ,即 malloc 函数并不了解开辟空间的类型,至于空间的具体类型将在使用时由使用者自己决定。
- 如果参数 size 为 0,则 malloc 函数的行为是标准未定义的,将会取决于编译器。
2.1.2 free 函数:
我们可以看到,free 函数的使用格式为:
void free (void* ptr);
不同的是,与 malloc 函数恰好相反,free函数的作用为释放动态开辟的内存,同时没有返回值:
1.如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
2.如果参数 ptr 是NULL指针,则 free 函数将什么都不会做。
2.1.3 malloc 函数与 free 函数的使用:
关于两个函数的实际使用,我们直接来看下面这段 malloc 函数与 free 函数的使用实例:
#include<stdio.h>
#include<stdlib.h>
int main()
{int* ptr = NULL;//初始化指针ptr = (int*)malloc(40);//使用malloc函数动态申请40字节空间//同时因为指针ptr类型为int*,而 malloc 函数的返回类型为void*,故使用强制类型转换int* p = ptr;//在此定义指针p的原因是,在之后的使用中,指针p的指向会发生改变//若不对初始指针指向进行保存,将无法释放改变前与改变后之间的空间if (p == NULL)//mallocc函数在动态空间开辟失败时返回空指针//即若此处为空指针,说明动态内存空间申请失败{perror("malloc");//打印malloc函数动态空间申请错误原因,并结束程序return 1;}//没有结束说明指针不为空,动态空间申请成功int i = 0;for (i = 0; i < 10; i++){*p = i;//循环向动态内存空间中存入数据p++;//在此处的操作中,指针p的指向发生了改变}//使用完成后释放动态内存空间:free(ptr);//指针p指向发生改变,但指针ptr仍指向初始指针ptr = NULL;//动态内存空间归还后,重新将指针ptr置空//动态内存空间已经归还,若不将指针ptr进行置空,指针ptr将变为野指针,指向将不可控,这在程序运行中非常危险return 0;
}
对其中几个地方进行强调
:
- 在对指针进行操作前一定要保存指向初始位置的原始指针。
- 在对指向动态内存空间的指针进行使用前,一定要进行非空判断。
- 在动态内存空间使用完毕并释放后,一定要将指针进行置空操作。
- 当程序结束时若动态内存空间没有被释放,将会被操作系统自动回收。
- 但若程序不结束且申请的动态内存空间持续不归还,动态内存将不会被回收,就会导致内存泄漏问题。
2.2 calloc 函数:
calloc 函数的使用格式为:
void* calloc (size_t num, size_t size);
,calloc 函数的功能是为 num 个大小为 size 的元素开辟一块动态内存空间,并将空间内每个字节都初始化为 0。其使用方式与 malloc 函数无异:
#include<stdio.h>
#include<stdlib.h>
int main()
{int* ptr = NULL;ptr = (int*)calloc(10, sizeof(int));//使用calloc函数动态申请10个int类型大小的空间//同时因为指针ptr类型为int*,而calloc函数的返回类型为void*,故使用强制类型转换int* p = ptr;if (p == NULL){perror("calloc");return 1;}int i = 0;for (i = 0; i < 10; i++){*p = i;p++;}free(ptr);ptr = NULL;return 0;
}
并且实际作用相比较来说,与函数 malloc 的区别仅在于 calloc 函数在返回地址前会把申请的空间内每个字节都初始化为 0 ,其它方面完全相同。
2.3 realloc 函数:
realloc 函数(re - allocate,即重新分配)的作用为重新分配从堆区申请来的动态内存空间的大小。其使用格式为:
void* realloc (void* ptr, size_t size);
realloc 函数的出现,使得动态内存管理更加的灵活。例如有些时侯我们觉得前面申请的空间太小了不够用,或者我们会觉得申请的空间过大了太浪费,这个时候我们就可以通过使用 realloc 函数对之前开辟的动态内存空间的大小再次进行合理的调整。正是 realloc 函数才使得动态内存空间真正变得“ 动态 ”起来。
#include<stdio.h>
#include<stdlib.h>int main()
{int* ptr = NULL;ptr = (int*)malloc(40);int* p = ptr;if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 10; i++){*p = i;p++;}realloc(ptr, 80);//空间不够用,重新分配更大的空间//将指针ptr指向的空间扩容至80字节free(ptr);PTR = NULL;return 0;
}
但哪怕是在成功扩容时,也仍会出现两种情况:当前空间与后相邻空间之间的空间是否足够 realloc 函数进行扩容操作
1.若空间足够,则直接执行扩容操作,并在扩容完成后返回指向起始位置的指针。
2.若空间不够,则将会在堆区中重新寻找合适的空间(足以容纳下扩容后的全部空间),并将原空间内的数据全部拷贝过来,接着释放原空间,并在扩容完成后返回指向新空间起始位置的指针。
但是还有最特殊的情况,即内存堆空间中没有能够容纳整个扩容后的动态内存空间的空间时,将会返回空指针。所以,我们在想要使用扩容后的动态内存空间时,为了避免使用指针内容为空指针而造成的意外错误,在使用之前首先应当对 realloc 函数返回的指针进行非空判断,之后再拿来使用:
#include<stdio.h>
#include<stdlib.h>
int main()
{int* ptr = NULL;ptr = (int*)malloc(40);int* PTR = NULL;PTR = (int*)realloc(ptr, 80);int* p = NULL;//空间不够用,重新分配更大的空间//将指针ptr指向的空间扩容至80字节if (PTR != NULL)//使用前进行非空判断,避免出现无法预料的错误{p = PTR;}//判断非空,即扩容成功,开始使用:int i = 0;for (i = 0; i < 20; i++){*p = i;p++;}free(PTR);PTR = NULL;return 0;
}
3. 常见动态内存错误
3.1 对 NULL 指针的解引用操作
int main()
{int* p = (int*)malloc(INT_MAX);//当尝试开辟的空间过大时,将会导致动态内存开辟失败//此时malloc函数将会返回空指针,即此时指针p也是空指针*p = 20;//没有判断非空就对指针p进行解引用操作,导致产生对空指针进行了解引用操作//将会造成内存非法访问的错误free(p);p = NULL;return 0;
}
避免出现此类问题的方法是,在指针使用前对 malloc 等函数的返回值进行非空判断。
3.2 对动态内存空间的越界访问
int main()
{int* p = (int*)malloc(10 * sizeof(int));if (NULL == p){perror("malloc");return 1;}int i = 0;for (i = 0; i <= 10; i++){*(p + i) = i;//当i=10时,将会出现越界访问的错误}free(p);p = NULL;return 0;
}
避免此类错误的方式是,在使用时仔细认真的进行内存边界的检查,并选择合适的空间访问范围。
3.3 对非动态内存空间使用 free 函数
int main()
{int a = 10;//变量a所使用的内存空间不是动态内存空间int* p = &a;free(p);//释放非动态内存空间p = NULL;return 0;
}
避免此类错误的方式是,在进行空间释放时注意区分静态内存空间与动态内存空间。
3.4 使用 free 函数释放动态内存空间的一部分
int main()
{int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 10; i++){*p = i;p++;//此时指针p的指向已经发生变化,不再指向起始位置}free(p);//释放部分动态内存空间p = NULL;return 0;
}
避免此类错误的方法是,在使用指针前保存好初始指向,并在进行动态内存释放时释放完整的动态内存空间。
3.5 对同一块动态内存空间多次释放
int main()
{int* p = (int*)malloc(10 * sizeof(int));free(p);//...(在中间又进行了很多其它操作之后,忘记了已经释放过动态内存空间,并进行了重复释放)free(p);//重复释放动态内存空间p = NULL;return 0;
}
避免此类问题的方法是,在已经释放过动态内存空间之后,一定要加以注释以提醒自己避免重复释放同一块动态内存空间。
3.6 不释放动态内存空间(内存泄漏)
void test()
{int* p = (int*)malloc(10 * sizeof(int));if (p != NULL){*p = 20;//判断非空后进行使用}//使用后没有释放动态内存空间,在程序终止前该动态内存空间都将无法被释放,将会逐渐消耗计算机系统的内存
}
int main()
{test();while (1);//为了演示内存泄漏,使程序永不终止return 0;
}
避免此类问题的方法是,永远记住,使用一个释放一个,使用结束立刻释放。
4. 柔性数组:
或许很多人都未曾听过柔性数组(flexible array)
这个概念,但是它又是确实存在的,并且它的存在对于我们进行动态内存管理有着巨大的作用。在 C99 标准中,结构中的最后一个元素的大小允许是未知大小的数组,而我们就将这个未知大小的数组称为柔性数组,并将这个数组成员称为柔性数组成员:
typedef struct A
{int a;int arr1[0];//柔性数组成员
}A;
typedef struct B
{int b;int arr2[];//柔性数组成员
}B;
4.1 柔性数组的特点:
柔性数组主要有以下 3 个特点:
①. 结构中的柔性数组成员前面必须至少存在一个其他成员。
②. sizeof 返回的这种结构大小不包括柔性数组的内存。
③. 包含柔性数组成员的结构用 malloc () 函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
定义中我们可以得知,柔性数组只能作为结构的最后一个元素,并且柔性数组的大小是不确定的,因此在结构中,柔性数组之前至少要有一个其它成员的存在。
因为柔性数组的大小是未知的,因此在计算结构大小时,将不会计入柔性数组的大小:
typedef struct test
{int i;int arr[];
}test;
int main()
{test a;printf("The size of a is :%d\n", sizeof(a));return 0;
}
例如上述代码的编译运行结果显示,类型 a 的大小为 4 个字节,即只包含成员整形变量 i
因为柔性数组的大小是未知的,因此其大小是动态、可变的,故在给其分配空间时,应当使用 malloc 函数进行动态内存分配。同时因为计算结构大小时没有计入柔性数组的大小,因此分配的内存大小应当大于结构的大小,才能容纳下柔性数组的预期大小:
typedef struct test
{int i;int arr[];
}test;int main()
{test a;//定义test类型结构体atest* p = (test*)malloc(sizeof(test) + 40);//结构的大小为sizeof(test)的大小,故开辟的动态内存大小应当大于该值free(p);p = NULL;return 0;
}
4.2 柔性数组的使用:
typedef struct test
{int i;int arr[];
}test;
int main()
{test a;//定义test类型结构体atest* p = (test*)malloc(sizeof(test) + 40);//malloc函数的返回值为指针类型,故使用结构体指针test*//使用malloc函数动态分配空间:test类型结构体的大小(不包含柔性数组) + 40字节if (p == NULL){perror("malloc");//判断动态内存空间是否开辟成功return 1;}//业务处理1:p->i = 10;int i = 0;//给柔性数组元素赋值:for (i = 0; i < p->i; i++){p->arr[i] = i;}//打印柔性数组元素:for(i=0;i<p->i;i++){printf("%d ", p->arr[i]);}printf("\n");test* pp = (test*)realloc(p, sizeof(test) + 80);//使用realloc函数将结构指针指向的结构a进行扩容if (pp == NULL){perror("realloc");//判断动态内存空间是否扩容成功return 1;}//业务处理2:pp->i = 20;for (i = 0; i < pp->i; i++){pp->arr[i] = i + 9;}for (i = 0; i < pp->i; i++){printf("%d ", pp->arr[i]);}printf("\n");free(pp);pp = NULL;return 0;
}
在这个示例中,我们首先定义了 test 类型结构体 a,接着使用了 malloc 函数为结构体 a 与柔性数组分配了动态存储空间;接着在判断非空(动态内存空间分配成功)后给结构体成员 i 与柔性数组 arr 内元素赋值,并进行了打印;再接下来,我们通过使用 realloc 函数将包含柔性数组 arr 的结构体 a 扩容,并在扩容后给结构体内各成员重新赋值并打印;最后释放使用完毕的动态内存空间并将指针置空
4.3 柔性数组的优势:
但是同时我们又发现,我们使用柔性数组的目的在于希望使结构成员的空间变为动态,可大可小, 若将其写成指针,由指针成员来指向其它的空间。如下:
typedef struct test
{int i;int* p;
}test;
int main()
{test* ptr = (test*)malloc(sizeof(test));if (ptr == NULL){perror("malloc");return 1;}ptr->p = (int*)malloc(40);if (ptr->p == NULL){free(ptr);ptr = NULL;return 1;}return 0;
}
我们说,这两种方法都可以达到我们的要求,实现我们的目的,完成同样的功能。
但是使用柔性数组有三个显著的好处
:
1.方便内存释放,例如进行了二次空间分配,即使用指针指向了另一块动态内存空间,还有另外一块动态内存空间需要及时进行释放,一旦没有注意,就有可能会造成内存释放不及时,严重时甚至有可能会导致内存泄漏问题的产生。反观使用柔性数组,只需调用一次 free 函数即可完成内存空间的释放。
2.有利于提升访问速度,使用了二次空间分配,则在访问时的寻址过程中就需要花费更多的时间,而如果我们使用了柔性数组,由于开辟的空间是连续的,在寻址时便可以节省一定的时间。
3.有利于减少内存碎片,使用二次内存分配的方式所申请到的空间是不连续的,就会在内存中产生更多的空隙,就会导致内存空间中产生更多的内存碎片,而使用柔性数组所申请的空间是连续的,则作为一个个整体,就可以减少内存碎片的产生了。
5.总结:
今天我们了解了动态内存管理的相关知识,学习了动态内存的开辟、释放与动态修改以及柔性数组使用与介绍,并且对动态内存空间的各项使用注意事项有了一定的认知和了解,在对动态内存空间的使用和管理中一定要仔细认真,希望我的文章和讲解能对大家的学习提供一些帮助。
当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~
相关文章:

【C语言进阶】动态内存管理详解与常见动态内存错误以及柔性数组使用与介绍
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C语言进阶 🎯长路漫漫浩浩,万事皆有期待 文章目录1.动态内存1.1 概述…...

【C++】string的模拟实现
文章目录1. string的模拟实现1.构造函数使用new开辟空间优化成全缺省的构造函数2. C_str3. operator[]4.拷贝构造浅拷贝深拷贝5. 赋值三种情况6. 迭代器7.比较(ASCII值)大小8. reserve(扩容)9. push_back(尾插字符)10. append(尾插字符串)11. (字符/字符串)12. insert在pos位置…...

前端借助Canvas实现压缩base64图片两种方法
一、具体代码 1、利用canvas压缩图片方法一 // 第一种压缩图片方法(图片base64,图片类型,压缩比例,回调函数)// 图片类型是指 image/png、image/jpeg、image/webp(仅Chrome支持)// 该方法对以上三种图片类型都适用 压缩结果的图片base64与原类型相同// …...

用ChatGPT生成Excel公式,太方便了
ChatGPT 自去年 11 月 30 日 OpenAI 重磅推出以来,这款 AI 聊天机器人迅速成为 AI 界的「当红炸子鸡」。一经发布,不少网友更是痴迷到通宵熬夜和它对话聊天,就为了探究 ChatGPT 的应用天花板在哪里,经过试探不少人发现,…...

【Kubernetes 企业项目实战】09、Rancher 2.6 管理 k8s-v1.23 及以上版本高可用集群
目录 一、Rancher 介绍 1.1Rancher简介 1.2 Rancher 和 k8s 的区别 1.3 Rancher 企业使用案例 二、安装 Rancher 2.1 初始化环境 2.2 安装 Rancher 2.3 登录 Rancher 平台 三、通过 Rancher 管理已存在的 k8s 集群 3.1 配置 rancher 3.2 导入 k8s 四、通过 Ranc…...

在Excel中按条件筛选数据并存入新的表
案例 老板想要看去年每月领料数量大于1000的数据。手动筛选并复制粘贴出来,需要重复操作12次,实在太麻烦了,还是让Python来做吧。磨刀不误砍柴工,先整理一下思路: 1读取原表,将数量大于1000的数据所对应的行整行提取(如同在excel表中按数字筛选大于1000的) 2将提取的数…...

【面试题】MySQL索引相关知识点
1.什么是索引 索引是存储引擎快速查找记录的一种数据结构,就类似书的目录,通过目录可以快速的查找到想要查找的内容 2.索引的特点 特点:索引是基于数据引擎的,不同的数据引擎实现索引的方式不一定相同 好处:通过索引…...

MySQL索引类型及原理?一文读懂
一、什么是MySQL索引? MySQL索引是一种数据结构,用于提高数据库查询的性能。它类似于一本书的目录,通过在表中存储指向数据行的引用,使得查询数据的速度更快。 在MySQL中,索引通常是在表上定义的,它们可以…...

【C语言】字符分类函数+内存函数
目录 1.字符函数 1.1字符分类函数 1.2.字符转换函数 //统一字符串中的大小写 2.内存处理函数 2.1内存拷贝函数memcpy //模拟实现memcpy 2.2内存移动函数memmove //模拟实现memmove 2.3内存比较函数memcmp 2.4内存设置函数memset 1.字符函数 1.1字符分类函数 头文…...

高通平台开发系列讲解(SIM卡篇)SIM卡基础概念
文章目录 一、SIM卡基本定义二、卡的类型三、SIM卡的作用三、SIM卡基本硬件结构四、SIM卡的内部物理单元五、卡文件系统沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇文章将介绍SIM的相关组件。 一、SIM卡基本定义 SIM卡是一种智能卡(ICC Card/UICC Card) SIM…...

记录一次ubuntu下配置ssh登录出现的问题
现象描述: 1. 配置完服务器端公钥和本地的私钥之后,ssh登录始终会让输入密码,用ssh -vvv rootip 查看发现发送密钥之后就没反应了。 本机debug info: debug1: Trying private key: C:\Users\wangc/.ssh/id_xxxx (私钥文件) debug3…...

深度剖析数据在内存中的存储(下)(适合初学者)
上篇讲解了整形在内存中的存储方式,这篇文章就来继续讲解浮点数在内存中的存储方式。 上篇地址: (5条消息) 深度剖析数据在内存中的存储(上)_陈大大陈的博客-CSDN博客 目录: 3.浮点型在内存中的存储 3.1.浮点数的…...

智慧物联网系统源码:一个用于数据的收集、处理、可视化、设备管理、设备预警、报警的平台
项目简介: 一个用于数据的收集、处理、可视化、设备管理、设备预警、报警的平台,通过平台将所有设备连接起来,为上层应用提供设备的管理、数据收集、远程控制等核心物联网功能。 支持支持远程对设备进行实时监控、故障排查、远程控制&#…...

2023年,拥有软考证书在这些地区可以领取福利补贴
众所周知,软考的含金量很高,比如可以入户、领取技能补贴、抵扣个税、以考代评、招投标加分,入专家库… 今天小编给大家收集了拥有软考证书可以领取软考福利的地区,希望对大家有所帮助! 【深圳】 入户 ①核准类入户:…...

使用Unity在材质球上实现绘画:详细解释每一行Shader代码!
在Unity中实现在材质球上绘画可以使用下面这个步骤:创建一个基础的材质球:在Unity的项目面板中创建一个新材质球,然后将其分配给您要绘画的对象。创建一个Shader:为了实现在材质球上绘画,您需要使用一种特殊的Shader。…...

Tesseract 4.0训练字库并且识别训练后的图片
各个工具下载链接在文章底部! 重要!!自己先创建一个空文件夹(名字随意),用来保存训练后的模型 ,还需要在里面创建一个 名称为tessdata 的文件夹 ,必须叫这个名 可以先使用下载后的进行测试训练(只需要把ja…...

ChatGPT热潮背后,金融行业大模型应用路在何方?——金融行业大模型应用探索
ChatGPT近两个月以来不断引爆热点,对人工智能应用发展的热潮前所未有地高涨,ChatGPT所代表的大模型在语义理解、多轮交互、内容生成中所展现的突出能力令人惊喜。而人工智能技术在金融行业的落地应用仍然面临挑战,虽然已经让大量宝贵的人力从…...

【怎么预防sql注入,以及还有预防其他的什么网络攻击】
SQL注入是一种常见的Web攻击,通过在Web应用程序中注入恶意SQL语句来获取或修改数据库中的数据。为了防止SQL注入,开发者可以采取以下措施: 1、使用参数化查询(Prepared Statement)或存储过程(Stored Proce…...

2023年全国最新机动车签字授权人精选真题及答案4
百分百题库提供机动车签字授权人考试试题、机动车签字授权人考试预测题、机动车签字授权人考试真题、机动车签字授权人证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 11.使用转化炉原理测量氮氧化物的排气分析仪进行排气污…...

【Java】用记事本实现“HelloWorld”输出
【在进行以下操作前需要下载好JDK并配置好对应的环境变量】 一、在任意文件夹中创建一个新的文本文档文件并写入以下代码 public class Hello{public static void main (String[] args){System.out.print("Hello,World!");} } 二、修改文件名称及文件类型为 Hello.j…...

我希望早点知道的关于成长的建议
人上了年纪,往往在诸如更加闭塞,更加固执这些缺点之外,再多出来一个缺点:那就是动不动就爱给别人建议。我当然也未能免俗。有时候会听到同样悲观且固执的过来人告诉我,这些建议说了和没说效果都一样,人们在…...

【哈希表】使用方法总结
1. uthash简介开源的第三方头文件,这只是一个头文件:uthash.h。uthash还包括三个额外的头文件,主要提供链表,动态数组和字符串。utlist.h为C结构提供了链接列表宏。utarray.h使用宏实现动态数组。utstring.h实现基本的动态字符串。…...

【笑小枫-面试篇】Java基础面试题整理,努力做全网最全
写在前面 或许你只是想白嫖内容,或许你也会忽略这段文字,但我还是想弱弱的说 题目整理耗费了大量精力,希望可以给博主点赞收藏,谢谢大家啦 我呢,笑小枫,一个努力的普通人,也希望可以花1秒钟记住…...

亚马逊短期疲软,但长期前景乐观
来源:猛兽财经 作者:猛兽财经 由于投资者对亚马逊(AMZN)前景的担忧,导致该公司的股价在过去一年中下跌了39%。然而猛兽财经认为亚马逊近期面临的不利因素只是暂时的,该公司还是有充分的条件可以在医疗保健和物流领域获得重大增长机…...

webgis高德地图
webgis高德地图 首先准备工作,注册一个高德地图账号,然后在创建一个新应用生一个key跟appId 高德开放平台 接着创建一个html页面 高德配置手册 <style>* {margin: 0;padding: 0;}#...

django项目实战十(django+bootstrap实现增删改查)进阶数据统计
目录 一、echarts 1、下载 2、配置 二、实现统计分析页面--架构和柱图 1、url 2、chart.py 3、chart_list.html 4、修改url 5、新增chart_bar方法 6、修改chart_list.html 四、饼图 1、url 2、视图chart.py新增 3、修改chart_list.html 五、折线图 1、url 2、char…...

【布隆过滤器(Bloom Filter)基本概念与原理、Bloom Filter优点与缺点、以及应用场景】
布隆过滤器(Bloom Filter)基本概念与原理、Bloom Filter优点与缺点、以及应用场景 Bloom Filter 基本概念 布隆过滤器是1970年由一个叫布隆的小伙子提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在…...

unity的Rendertexture上面显示粒子特效最便捷的解决方案
一、为什么不显示 1.为什么粒子特效也不显示? 不显示是正常的,因为当前为背景的点设置为A为0时已经被剔除,当前位置粒子特效的颜色也会被剔除。 因为clip发生在融合blend之前,blend发生在所有颜色输出之后的帧缓存。 2.为什么NGUI的Unlit/Premultiplied Colored的shade…...

Docker 查询、停止、删除和重启容器
docker 列出所有容器IDdocker ps -aq[rootlocalhost conf]# docker ps -aq f81aa5f48427 06a66409d7ce 1c3d38b948ba 62233dfad35b 4b0032878886 0f6f368c4c1d 7d98a59a8012 1906ba6bfbe1 [rootlocalhost conf]#docker 查看所有运行容器docker ps -a[rootlocalhost conf]# dock…...

面试历程(3)
1、HashMap为什么要使用红黑树,不能使用平衡二叉树(AVL树) 二叉查找树具有的特性: 左子树上所有结点的值均小于或等于它的根结点的值。右子树上所有结点的值均大于或等于它的根结点的值。左、右子树也分别为二叉排序树。AVL树是严格平衡二叉树(左右两个子树的高度差的绝对…...