STL:位图和布隆过滤器

article/2025/8/16 17:14:26

一,位图

1.1 位图的概念

      究竟什么是位图呢??我们用一道问题来引入

问题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
这40亿个数中。【腾讯】


根据这个问题,我们可以用什么方法来解决呢???我们可以很迅速地想到一下方法:

方法1:排序+二分查找

方法2:将整数丢进哈希表或者红黑树中

          看似好像都能解决这个问题,但是我们来审视一下这个问题的关键——内存

         1G=1024MB=1024*1024KB=1024*1024*1024Byte 约等于10亿Byte,也就是说40亿个数大约需要16G内存!!没有办法将数据一次性放到内存去处理。

  (1)分析方法1:如果我们将数据分在不同的文件里,我们可以用归并排序去完成文件之间的排序,但是无法使用二分查找法,因为没有办法通过下标去直接访问元素!!!

  (2)分析方法2:问题就是所需要的内存太大了,光是数据就16G了,更何况红黑树和哈希表的底层的封装还需要一定的损耗,我们如果要使用的话也只能是一部分一部分丢进容器,然后再删掉丢下一部分,这样一方面的问题是我们其实在一开始丢进容器的时候就可以去做比对了,没有必要丢到容器里,另一方面的问题就是不适应需要多次查找比对的场景。

      因此上面两种方法都是不太现实的,但是有一种数据结构可以帮助我们解决这个问题,那就是位图——通过哈希函数中的直接定址法确定整型在不在的模型(就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

    所以方法3:用位图去解决  

       数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:

       你可能会有这样的疑惑,为什么上面的排布是反着来的,即从7—>1,但其实这样才是符合我们的人为的认知的,比如1234,右边的是低位,左边才是高位。在计算机中具体是怎么排布的我们不得而知,因为那属于内存层,是我们看不到的,我们能看到的只是计算机给我们展现的虚拟层,比如说我们的监视窗口和内存窗口,每个bit位都是左高右低的排布,符合我们人的一个惯性。  而我们的左移运算符 其实就是从低位->高位  而右移运算符 其实是从高位->低位。

 1.2 位图的实现

      首先,我们的STL容器中是有位图这个容器的,名字叫做bitset。

1.2.1 基本结构

template<size_t N> //非类型模板参数   N表示需要开的位图的大小
class bitset
{
public:private:vector<char> _bits;
};

      我们需要去控制比特位,所以选择char作为我们的类型是最合适的,其中N是非类型模板参数,表明位图具体开多大。

1.2.2 构造函数

       我们具体应该开多大,自然是取决于我们的元素数量的,既然我们一个char有8个bit位,自然就可以表示8个整数,但是需要注意的一个原则是:宁可开多不可开少。因为假设是10,除以8之后余数会被干掉,因此我们最后一定还要加上一个1

bitset()
{_bits.resize(N / 8 + 1, 0); //只能开多不能开少
}

1.2.3 set

       set的作用,就是将某一位设置成1

void set(size_t x) //设置成1
{size_t i = x / 8;//计算x映射的位char在第i个数组的位置size_t j = x % 8;//计算x映射的位在这个char的第j个比特位//按位或   <<左移是低->高_bits[i] |= (1 << j);
}

size_t i = x / 8;//计算x映射的位char在第i个数组的位置
size_t j = x % 8;//计算x映射的位在这个char的第j个比特位

       接下来我们要判断的问题就是,如何将该位设置为1->利用位运算的性质 只要j对应的位置按位或上0……1……0 (1左移j位得到)即可

1.2.4 reset

         reset的作用,就是将某一位设置成0

void reset(size_t x)//设置成0
{size_t i = x / 8;//计算x映射的位char在第i个数组的位置size_t j = x % 8;//计算x映射的位在这个char的第j个比特位//按位与 _bits[i] &= ~(1 << j);
}

        接下来我们要判断的问题就是,如何将该位设置为0->利用位运算的性质 只要j对应的位置按位或上1……0……1 (1左移j位再取反得到)即可

1.2.5 test

    test的作用,就是判断某一位是否为1(即该整数在不在)

bool test(size_t x) //x在不在  判断这一位是0还是1
{size_t i = x / 8;//计算x映射的位char在第i个数组的位置size_t j = x % 8;//计算x映射的位在这个char的第j个比特位return _bits[i] & (1 << j);
}

      还要按位与上0……1……0,那么除了第j位 其他位都会是0 那么第j位如果也是0 结果就会是0,如果是1 那么结果就是非0.

1.2.6 位图开满空间的方法

bitset<-1> bs;//开满的方法
bitset<0xFFFFFFFF> bs;

       -1强转成size_t 类型的时候就是无符号整型的最大值,而0xFFFFFFFF则是16进制下的无符号整型最大值。

1.3 位图的应用

1. 快速查找某个数据是否在一个集合中

      一般适用于整型
2. 排序 + 去重

       可以将一堆元素都丢进位图中,如果对应位置原来为0的话,就变成1,如果原来位置原来为1的话,就不处理,然后我们再通过映射关系将元素还原回来,就完成了排序+去重
3. 求两个集合的交集、并集等

       这边有两种思路:

1、将两个集合分别放在两个位图中,分别完成排序和去重,然后再一个个去比对。

2、先将其中一个集合放进位图中,然后再通过另一个集合去判断,如果位图中为1,说明该数就是交集,但是为了防止集合出现重复数字,我们此时将该位置变成0(改进方法).

       两种方法都可以,但是第一种方法有两个问题,一个是空间的消耗太大,另一个就是无论这个集合多大,我们都需要将所有的位置都遍历完了才可以确定,因为我们并不清楚集合里元素的范围。 而第二种方法可以有效改进上面两个问题,不仅空间消耗减少,而且我们只需要判断集合2的元素是否在位图中出现过即可,肯定比遍历全部位置效率高。  另一方面由于集合2没有进行去重,所以可能会出现重复元素,但是通过我们的改进方法——即发现交集的时候,将原先位图的位置变成0就可以解决这个问题。  

      总结:如果数据量达到亿级别,可能用第一种比较合适,但是大多数情况下,第二种显然是更合适的!!

4.数据相对集中时的统计次数

     假设说我们已知数据只有可能出现‘a’->‘z’ 此时我们发现也就26种可能性,这个时候我们也不需要单独去封装一个位图,直接用一个整型就可以解决问题!!

      附上一道位图有关的OJ题

面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode)

class Solution {
public:bool isUnique(string astr) {if(astr.size()>26)  return false;//鸽巢原理做优化//利用位图的思想int bitmap=0;for(auto ch:astr){int i=ch-'a';//找到映射关系if((bitmap>>i)&1==1)  return false;//先判断该字符是否出现过 判断第i位是否是1//如果没出现过,将第i位的0修改为1bitmap|=(1<<i);}return true;}
};

5. 操作系统中磁盘块标记

1.4 位图的海量数据处理

1. 1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
      该问题的关键就是:我们位图模型只能判断有或者无,但是无法区分出现多少次。 

       这个时候我们就要从本质去分析,为什么位图只能判断有或者无呢??原因就是我们只有一个比特位来表示状态的有无,所以我们这个时候可以用两个比特位来表示当前的状态。但是我们如果直接用一个为位图来解决,那么计算逻辑也会随之改变。所以我们还是用之前的位图,但是为了表示多种状态,我们使用两个位图。 00表示无,01表示1次,11表示2次,10表示2次以上即可。

     根据上述的分析,我们构建出解决这一问题的类

template<size_t N> //非类型模板参数   N表示需要开的位图的大小
class twobitset //解决100亿数据判断有无  找到数据中不超过2次的整数  00-无  01一个  11两个  10两个以上
{
public:void set(size_t x) //设置成1{if (_bit1.test(x) == false && _bit2.test(x) == false)  _bit2.set(x);//00->01else if (_bit1.test(x) == false && _bit2.test(x) == true) _bit1.set(x);//01->11else if (_bit1.test(x) == true && _bit2.test(x) == true) _bit2.reset(x);//11->10//如果是11 不处理}void Print() //打印只出现一次或者两次的整数{for (size_t i = 0; i < N; ++i)if (_bit2.test(i) == true)cout << i << endl;cout << endl;}
private:bitset<N> _bit1;bitset<N> _bit2;};

2、给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

    解决方案我在1.3中已经讲过了!!

1.5 位图的优缺点

优点:速度快、节省内存

缺点:只能映射整型,如浮点数、string等等不能存储映射  

       首先我们要思考,为什么位图无法存储这两种类型的映射,原因就在于其可以表现的形式太多种了,比如说对于无符号整型来说,最多也就2^32-1种可能, 但是对于浮点数和string来说,其组合之多导致无法像整型一样在位图中可以直接使用直接定址法。

       浮点数还好,但是string平时非常常用,比如用户名,所以这个时候当数据量极大的时候,也是一个问题,所以后来出现了一种结构解决了这种问题——>布隆过滤器

二、布隆过滤器

2.1 布隆过滤器的提出

      我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那
些已经存在的记录
。 如何快速查找呢?
 

1. 用哈希表存储用户记录,缺点:浪费空间
2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理
了。

3. 将哈希与位图结合,即布隆过滤器
 

2.2 布隆过滤器的概念

        布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存
在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也
可以节省大量的内存空间。

    怎么去理解概率型数据结构呢??

        首先可以确定的是,由于字符串的排列组合种类非常多,所以我们无论如何都无法做到通过直接定址法让每个字符串正好都对应一个位置……也就是说,我们利用字符串哈希函数在位图中存储大量字符串信息是必然会造成哈希冲突的,而布隆大佬的想法并不是完全解决哈希冲突,而是想降低哈希冲突的概率。  策略就是:既然一个字符串映射一个位置容易出现哈希冲突,那么一个字符串映射多个位置出现哈希冲突的概率就会降低!!!这是一种退而求其次的策略。

所以我们要使用多个类型的字符串哈希函数来帮助我们让一个字符串映射多个位置。具体有哪些常用的哈希函数可以参照下面的文章:字符串哈希函数算法

通过上图,我们可以确定布隆过滤器的两个特点:

1、不在是准确的,因为只要有一个位置没映射上,就是不在。

2、在是不准备的,存在误判,因为可能该字符串映射的位置恰好被前面的字符串给映射了

2.3 布隆过滤器的应用场景

 既然布隆过滤器只是降低哈希冲突而不是避免哈希冲突,那么他适合的场景如下:

1、能容忍误判的场景(客户端感知不到)

比如注册时,快速判断昵称是否被使用过。

2、虽然不能容忍误判,但是可以确定不在是准确的,只需要对在的场景进行特殊处理

比如说注册时,快速判断该电话号码是否被注册过。

       通过上图我们可以发现,布隆过滤器至少对不在的场景是判断准确的,所以我们可以不需要去数据库查询,只需要对在的结果进行查询和比对即可

       同时大家会发现,上图就充分展现了为什么该解决就叫做布隆过滤器,其实就是将大量的信息进行过滤,处理其中可以解决的问题,对于少部分不能解决的问题,再到数据库中去解决。

2.4 布隆过滤器实现的基本结构

  //想把类当函数使用 就要想到仿函数  //提供三个字符串哈希函数
struct BKDRHash
{size_t operator() (const string& s) //字符串转整型{size_t hash = 0;for (auto e : s){hash += e;hash *= 31;}return hash;}
};
struct APHash
{size_t operator()(const string& s){size_t hash = 0;for (long i = 0; i < s.size(); i++){size_t ch = s[i];if ((i & 1) == 0)  hash ^= ((hash << 7) ^ ch ^ (hash >> 3));else               hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));}return hash;}
};
struct DJBHash
{size_t operator()(const string& s){size_t hash = 5381;for (auto ch : s)  hash += (hash << 5) + ch;return hash;}
};
//布隆过滤器  基本上解决的是字符串的问题 如果是整型的话 还是位图更好用   至于用多少个哈希函数 取决于实际的需求
//N表示最多会插入key的个数
template<size_t N,
size_t X=6,
class K=string,
class Hash1= BKDRHash,
class Hash2= APHash,
class Hash3= DJBHash> //K并不确定是什么   通过多种Hash函数去帮助我们映射不同的位置
class BloomFilter
{
public:private:bitset<N*X> _bits;
};

       我们一共用了6个模版参数,首先布隆过滤器大部分情况下是用来解决字符串的问题,所以我们的K默然给的是string,而后面Hash1——Hash3都是字符串哈希函数,这样一个字符串可以映射三个位置。而N表示的是要插入多少个字符串,而关于X是什么,我们来看看下面这个文章。

详解布隆过滤器的原理,使用场景和注意事项 - 知乎

 所以X在这边可以理解为我们需要多开X倍的空间,来尽可能达到误判率和效率尽可能合理。

经过计算后如果我们的哈希函数是3个的话,大概需要多开4.3倍的空间比较合理。

2.5 布隆过滤器的插入

void set(const K& key)
{size_t len = N * X;size_t hash1 = Hash1()(key) % len;_bits.set(hash1);size_t hash2 = Hash2()(key) % len;_bits.set(hash2);size_t hash3 = Hash3()(key) % len;_bits.set(hash3);//cout << hash1 << " " << hash2 << " " << hash3 << endl << endl; //看看映射的位置
}

 2.6 布隆过滤器的查找

         布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中。

bool test(const K& key)
{size_t len = N * X;size_t hash1 = Hash1()(key) % len;if (_bits.test(hash1) == false) return false;size_t hash2 = Hash2()(key) % len;if (_bits.test(hash2) == false) return false;size_t hash3 = Hash3()(key) % len;if (_bits.test(hash3) == false) return false;return true;//在可能会有误判
}

注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可
能存在,因为有些哈希函数存在一定的误判。

2.7 布隆过滤器的删除

      首先我们可以确定的是,布隆过滤器是不太支持删除的,因为我们删了一个位置,可能会影响其他位置。

      一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。

     
      但是这样会存在三种缺陷:

1、无法确认元素是否真正在布隆过滤器中

        因为无论是位图还是布隆过滤器并不是传统意义上的真的存了相对应的元素在里面,而只能反 应具体在不在。所以我们很难保证说我们当前要删除的元素是否存在布隆过滤器中,如果要删除的元素不存在布隆过滤器中,布隆过滤器是检测不出来的,这样就整个乱套了。

2、容易存在计数回绕

      既然是计数,那么我们就可以根据情况去控制一个位置大约用几个比特位来计数,比如我们用4个比特位来计数,那么就有2^4 也就是16个字符串可以映射该位置,如果用8个比特位就是2^8 也就是256个字符串可以映射该位置。但是如果我们超出了这个范围(比如有257个字符串映射了该位置),可能就会出现计数溢出导致计数回绕。

3、无法去重

     使用引用计数后,如果一个字符串不小心同时插入了两次,那么对应位置的计数都会增加,这个时候当我们下次要删除的时候,也必须要删除两次,如果只删除一次,那么还是可以找得到。

     综合上面的三种缺陷,我们可以得到一个结论就是——布隆过滤器并不适应删除的场景,尤其是第一个缺陷最为严重,严重时会导致数据丢失!!!

2.8 布隆过滤器的优点

1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关

2. 哈希函数相互之间没有关系(可多次进行哈希切割),方便硬件并行运算

3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势

5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能

6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算(哈希切割)

2.9 布隆过滤器的缺点

1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:1,再
建立一个白名单,存储可能会误判的数据。2,对在的场景进行特殊处理
)

2. 不能获取元素本身

3. 一般情况下不能从布隆过滤器中删除元素

4. 如果采用计数方式删除,可能会存在计数回绕、删除不在的元素、无法去重等问题。


2.10 布隆过滤器的海量数据处理

1、给两个文件,分别有100亿个query(本质就是一个字符串,如sql语句),我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法

总结:

1,利用一个哈希函数进行哈希切分,根据具体情况将A和B分别切割成多个小文件。

2,用一个set来读取小文件

        如果都可以插入,那么就是以下两种情况:(1)单个文件比较小,所以可以插入进去。(2)单个文件比较大,但是其中存在某个大量重复的query(此时换个哈希函数切割也是没用的,切不动)。但是由于set不支持键值冗余,所以都不会正确插入进去。 此时直接把另一个文件拿过来比对即可。

       如果插入的过程中出现抛异常(内存不够才会抛异常),那么就是以下情况:单个文件比较大,并且存在大量不同的query,此时我们就再换一个哈希函数进行切割,将该文件切得更小一点。

2.11 布隆过滤器的误判率测试代码

void test_bloomfilter2()
{srand((unsigned int)time(0));const size_t N = 10000;BloomFilter<N> bf;vector<string> v1;string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";for (size_t i = 0; i < N; ++i){v1.push_back(url + to_string(i));}for (auto& str : v1){bf.set(str);}// v2跟v1是相似字符串集,但是不一样vector<string> v2;for (size_t i = 0; i < N; ++i){string url = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";url += to_string(999999 + i);v2.push_back(url);}size_t n2 = 0;for (auto& str : v2){if (bf.test(str)){++n2;}}cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;// 不相似字符串集vector<string> v3;for (size_t i = 0; i < N; ++i){string url = "zhihu.com";//string url = "https://www.cctalk.com/m/statistics/live/16845432622875";url += to_string(i + rand());v3.push_back(url);}size_t n3 = 0;for (auto& str : v3){if (bf.test(str)){++n3;}}cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;

当X为4的时候

 当X为5的时候

当X为6的时候

 当X是7的时候

 我们发现6的时候进步是比较明显的,所以最后我选择了6

2.12 哈希切割

          给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?
    

如果是top-K的话,建个小堆去不断比对即可,最后剩下的就是top-K。

2.13 布隆过滤器的整体代码实现

  //想把类当函数使用 就要想到仿函数  //提供三个字符串哈希函数
struct BKDRHash
{size_t operator() (const string& s) //字符串转整型{size_t hash = 0;for (auto e : s){hash += e;hash *= 31;}return hash;}
};
struct APHash
{size_t operator()(const string& s){size_t hash = 0;for (long i = 0; i < s.size(); i++){size_t ch = s[i];if ((i & 1) == 0)  hash ^= ((hash << 7) ^ ch ^ (hash >> 3));else               hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));}return hash;}
};
struct DJBHash
{size_t operator()(const string& s){size_t hash = 5381;for (auto ch : s)  hash += (hash << 5) + ch;return hash;}
};//布隆过滤器  基本上解决的是字符串的问题 如果是整型的话 还是位图更好用   至于用多少个哈希函数 取决于实际的需求
//N表示最多会插入key的个数
template<size_t N,size_t X=6,class K=string,class Hash1= BKDRHash,class Hash2= APHash,class Hash3= DJBHash> //K并不确定是什么   通过多种Hash函数去帮助我们映射不同的位置
//6是测试用测试代码测试出来的
class BloomFilter
{
public:void set(const K& key){size_t len = N * X;size_t hash1 = Hash1()(key) % len;_bits.set(hash1);size_t hash2 = Hash2()(key) % len;_bits.set(hash2);size_t hash3 = Hash3()(key) % len;_bits.set(hash3);//cout << hash1 << " " << hash2 << " " << hash3 << endl << endl; //看看映射的位置}bool test(const K& key){size_t len = N * X;size_t hash1 = Hash1()(key) % len;if (_bits.test(hash1) == false) return false;size_t hash2 = Hash2()(key) % len;if (_bits.test(hash2) == false) return false;size_t hash3 = Hash3()(key) % len;if (_bits.test(hash3) == false) return false;return true;//在可能会有误判}private:bitset<N*X> _bits;
};


      


http://www.hkcw.cn/article/CtTCNDCVjM.shtml

相关文章

【C++高阶】:智能指针的全面解析

✨ 落絮无声春堕泪&#xff0c;行云有影月含羞 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;C学习 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&a…

【C++对于C语言的扩充】函数重载、引用以及内联函数

文章目录 &#x1f680;前言&#x1f680;函数重载注意&#xff1a;✈️为什么C可以实现函数重载&#xff0c;而C语言却不行呢&#xff1f; &#x1f680;引用✈️引用的特性✈️C中为什么要引入引用✈️引用与指针的区别 &#x1f680;内联函数✈️内联函数特性 &#x1f680;…

【C++指南】STL容器的安全革命:如何封装Vector杜绝越界访问与迭代器失效?

&#x1f31f; 各位看官好&#xff0c;我是egoist2023&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 使用STL的三个境界&#xff1a;能用&#xff0c;明理&#xff0c;能扩展 &#x1f44d; 如果觉得这篇文章有帮助&#…

C++ 异常处理机制与自定义异常体系

目录 1.C语言传统的处理错误的方式 &#x1f60a; 1. 终止程序 2. 返回错误码 3.实际使用中的情况 2. C异常概念&#x1f33c; 2.1 C异常的基本概念 2.2异常的抛出和匹配原则 2.3 异常的重新抛出 2.4 异常安全 2.5 异常规范 3. 自定义异常体系 &#x1f495;&#x…

C++入门看这一篇就够了——超详细讲解(120000多字详细讲解,涵盖C++大量知识)

目录 一、面向对象的思想 二、类的使用 1.类的构成 2.类的设计 三、对象的基本使用 四、类的构造函数 1.构造函数的作用 2.构造函数的特点 3.默认构造函数 3.1.合成的默认构造函数 3.2.手动定义的默认构造函数 四、自定义的重载构造函数 五、拷贝构造函数 1.手动…

【第53节】Windows编程必学之使用C++写exe压缩加密壳

目录 一、实现背景 1.1 前言 1.2 前置知识 1.3 达到目标 二、壳的实现要点 2.1 写壳怎么做 2.2 写壳的困难点 2.3 如何写壳代码 2.4 API函数的调用问题 2.5 重定位问题 2.6 信息交互问题 2.7 调试问题 2.8 关于目标程序的随机基址 2.9 关于目标程序的导入表 2.1…

C++离线查询

前言 C算法与数据结构 打开打包代码的方法兼述单元测试 概念及原理 离线算法( offline algorithms)&#xff0c;离线计算就是在计算开始前已知所有输入数据&#xff0c;输入数据不会产生变化&#xff0c;且在解决一个问题后就要立即得出结果的前提下进行的计算。 通俗的说&a…

金价又涨了!金饰克价涨至1018元,一夜涨14元

美东时间5月23日,国际贵金属期货普遍收涨,COMEX黄金期货涨1.90%,报3357.70美元/盎司,本周累计上涨4.75%。5月24日,国内金饰价格跟涨。周生生足金饰品标价1018元/克,较前一日1004元/克的价格上涨14元/克。责任编辑:zx0002

日本人准备开始吃饲料了?

日本农业水产大臣小泉进次郎十分骄傲地宣布政府将要拿出2021年所产陈米以每5公斤1800日元的价格进行售卖(合人民币差不多1斤大米9块钱)。当地专家吹捧此举将有效缓解日本米荒,并放话越是陈米吃着越香,这下日本人有口福了结果评论区直接翻车了,有网友直接贴出往年饲料米价格…

国际乒联发声明回应选举争议 谴责扰乱行为并重启会议

当地时间29日,国际乒联发布了关于2025年度代表大会期间选举事宜的声明。5月27日,在卡塔尔多哈举行的国际乒联年度股东大会上,因主席选举争议引发混乱,会议最终宣布临时暂停。声明中提到,主席选举结束后,一些既不是会员协会代表也不是执行委员会、理事会、委员会成员或受邀…

胖东来红内裤案宣判:“段某”赔偿40万元 名誉权获法院支持

2025年5月28日,许昌市魏都区人民法院公开审理了许昌市胖东来商贸集团有限公司与段某之间的名誉权纠纷案。法院判决段某在其个人抖音账号“两个小段(小)”发布书面道歉信的视频,并赔偿胖东来公司40万元经济损失。部分人大代表、政协委员、媒体记者、律师代表和企业代表旁听了…

市监总局就毕井泉被查表态 再度引发市场关注

六年多前,毕井泉因长春长生疫苗案从原国家食品药品监督管理总局局长位置引咎辞职的消息震惊了市场;六年多后,他被查的消息再次引发市场的强烈关注。据中央纪委国家监委网站5月29日消息,十四届全国政协常委、经济委员会副主任毕井泉涉嫌严重违纪违法,目前正接受中央纪委国家…

高芙评职业生涯最经典三胜 荣耀时刻回顾

近日,美国网球运动员高芙在法网接受记者采访时,回顾了自己职业生涯中的三场经典胜利。这三场比赛分别是2024年终总决赛争冠战对阵郑钦文、2019年温网第一轮对阵大威廉姆斯以及2023年美网决赛对阵萨巴伦卡。她还特别提到了此前罗马半决赛与郑钦文的那场长达三个半小时的大战,…

女子露营归来脖子惨遭“毁容” 提醒:夏季蚊虫活跃,如遇皮肤瘙痒红肿不能拖

近日,浙江30岁女子小妍露营归来后,颈部便出现刺痛和瘙痒,起初她并未在意。两天后,症状急剧加重——皮肤红肿成片,冒出红色丘疹和水疱,还伴随灼热疼痛。无独有偶,小学生骏骏在户外骑车后,小腿处的皮肤上也出现了个大包。两人来浙江省皮肤病医院就医后,均被确诊为“虫咬…

男子乘火车旅行刷新吉尼斯纪录:24小时内乘火车旅行5887.76公里

近日,吉尼斯世界纪录官网公布了一项纪录——中国男子王冬成功以24小时内5887.76公里的火车旅行距离,刷新了“24小时内乘坐火车旅行最远距离”的吉尼斯世界纪录。▲王冬刷新吉尼斯世界纪录今年39岁的王冬是四川德阳人,12年前在上海求学时的他,就曾因换乘8趟列车回家而走红网…

从外卖APP到网络协议:深入解析UDP及应用层协议

目录 1. 应用层和传输层1.1 开发中常见的自定义协议格式 2. UDP2.1 源端口号及目的端口号2.2 UDP报文长度2.3 UDP校验和(checksum) 3. 基于UDP的应用层协议 关注我&#xff0c;学习更多企业开发和面试内容~ 1. 应用层和传输层 应用层和程序员接触最密切&#xff0c;应用程序&a…

【JavaWeb】基本概念、web服务器、Tomcat、HTTP协议

目录 1. 基本概念1.1 基本概念1.2 web应用程序1.3 静态web1.4 动态web 2. web服务器3. tomcat详解3.1 安装3.2 启动3.3 配置3.3.1 配置启动的端口号3.3.2 配置主机的名称3.3.3 其他常用配置项日志配置数据源配置安全配置 3.4 发布一个网站 4. Http协议4.1 什么是http4.2 http的…

自扶正救生艇,乘风破浪,守护生命

在复杂水域救援中存在显著缺陷。遇巨浪或急流漩涡易倾覆且无法自主复位&#xff0c;使救援人员与被困者陷入二次危险。统计显示&#xff0c;激流救援中近三成五的救援人员伤亡源于船只倾覆后被困。更严重的是&#xff0c;传统救生艇倾覆后常需外部救援力量才能恢复&#xff0c;…

法国7月1日起实施最严户外禁烟令,范围包括这些

法国卫生与家庭部长卡特琳沃特兰表示,法国将在所有儿童可能出入的户外场所禁止吸烟。该禁令将于7月1日生效,范围包括海滩、公园、花园、学校外、公交车站和体育场馆等。责任编辑:zx0002

规定明令禁止自热类食品坐火车既不能带也不能托运

自热米饭发热包因高温爆炸风险被高铁禁止,单独食材包携带需密封完好且车上禁加热。各地规定不一,建议优先选择高铁餐食或非加热速食,出行前确认车站要求。一、发热包为何被禁?安全风险是主因自热米饭的发热包主要成分为镁铝粉、生石灰等,遇水后快速放热,温度可达100℃以上…