Table of contents
41. DHCP protocol (Dynamic Host Configuration Protocol)
The DHCP protocol is an application layer protocol that uses the services provided by the transport layer protocol UDP
The port of the DHCP server is
UDP 67
The port for the DHCP client is
UDP 68
The working process of the DHCP protocol and how a computer without an IP address dynamically configures an IP through a DHCP server in the network
Prerequisite: There is a DHCP server in the LAN, or there is a DHCP relay agent , and the DHCP client software runs automatically after the computer is turned on.
①DHCP client actively looking for DHCP server:DHCP DISCOVER
The DHCP client sends a DHCP discovery message by broadcasting (destination address:
255.255.255.255
source address0.0.0.0
destination port: ) to find a DHCP server;67
Because the port is 67, only the DHCP server will respond to the discovery message, and other hosts will discard the message; after the DHCP server receives the message, it will analyze the message layer by layer to obtain the client MAC, the transaction ID of the message and other information , and then query whether there is already configured network configuration information locally according to the client MAC
If it exists,
DHCP OFFER
reply in the message, if it does not exist, generate configuration information in the default wayDHCP OFFER
and reply in the message
②DHCP server sends configuration information providing message:DHCP OFFER
The DHCP server sends messages by broadcasting (destination address:
255.255.255.255
source addressDHCP服务器自己的IP地址
destination port:68
) .DHCP OFFER
Because the port is 68, only the host running the DHCP client will receive the message, and then analyze the message to obtain the transaction ID , so as to determine whether it is a message sent to itself, and discard it if not.
Provide configuration information in
DHCP OFFER
the message,IP地址
子网掩码
地址租期
默认网关
DNS服务器
etc.If there are multiple DHCP servers in the network, the client may obtain multiple
DHCP OFFER
messages, usually the DHCP client will choose the one that arrives first, and then send a DHCP request message to the DHCP server
③DHCP client sends DHCP request message :DHCP REQUEST
The DHCP client sends messages by broadcasting (destination address:
255.255.255.255
source address0.0.0.0
destination port:67
) .DHCP REQUEST
The broadcast is sent because there is no need to unicast each DHCP server to send its own selection, and the source address is
0.0.0.0
because the DHCP client has not yet set its own IP address.
DHCP REQUEST
In the message事务ID
DHCP客户的MAC地址
接收的租约中的IP地址
提供此租约的DHCP服务器的IP地址
, the DHCP server can know whether its offer has been selected after receiving the messageThen the DHCP server sends a confirmation message to the DHCP client
④ SelectedOFFERThe DHCP server sends a confirmation message to the DHCP client:DHCP ACK
The DHCP server sends a confirmation message by broadcasting (destination address:
255.255.255.255
source addressDHCP服务器自己的IP地址
destination port: ).68
After the DHCP client receivesDHCP ACKAfter receiving the message, you can use the network configuration information such as the leased IP address, and then communicate with other hosts in the network.
Then there is normal use and communication...
⑤ Renewal phase of DHCP client (destination IP:
DHCP服务器的IP地址
source IP address:租用的IP地址
port:67
)
a) 0.5 times the lease period: The DHCP client actively renews the contract, sends
DHCP REQUEST
a message, and then the DHCP response has three situations
Case 1: The DHCP server agrees to renew the contract and sends
DHCP ACK
a message, so the DHCP client has a new leaseSituation 2: The DHCP server does not agree to renew the contract and sends
DHCP NACK
a denial message, so the DHCP client immediately discards the current configuration information such as the IP address, and then re-executes the above step ①Case 3: If the DHCP server does not respond, continue to use the previous IP address configuration information and wait for the arrival of 0.875 times the lease period
b) 0.875 times the lease period: Repeat the action of 0.5 times the lease period, if the DHCP server still does not respond, then wait for the lease period to be exhausted
c) when the lease expires: Immediately give up the current configuration information such as IP address, and then re-execute step ①
⑥ The DHCP client can terminate the lease period provided by the DHCP server at any time during the lease period :DHCP RELEASE
At this time, the message is sent in broadcast mode (destination address:
255.255.255.255
source address0.0.0.0
destination port:67
)DHCP RELEASE
42. allocate and deallocate in STL
There are two levels of memory space allocation in STL: the first level configurator and the second level configurator
First first question :
为什么需要两级空间配置器?
①Frequently applying to release memory on the heap will cause a lot of memory fragmentation ;
② Calling malloc and free to apply to release memory will add a lot of additional information to the space, reducing the utilization of space ;
Then the second question :
二级空间配置器与一级空间配置器之间的协调合作关系?
①The secondary space configurator is responsible for the application of small blocks of memory , generally less than or equal to 128 bytes ; STL selects the secondary space configurator by default, and if the requested space is greater than 128 bytes, then use the primary configurator
② The first-level space configurator is responsible for the application of larger memory , generally larger than 128 bytes ;
Third question :
二级空间配置
Use an array to store a total of 16 block chain tables ranging in size from 8 to 128, and maintain 16 of them
free-list
.1. The first-level configurator directly uses the malloc, free, and relloc functions
2. The second-level configurator calls the first-level configurator according to the size of the requested block, if it is greater than 128 bytes; if it is less than or equal to 128 bytes, the first-level configurator is not used;
3.
allocate()
It is a space configuration function. First judge the size of the requested block. If it is greater than 128 bytes, it will call the first-level configurator; if it is less than or equal to 128 bytes, it will checkfree-list
whether there are available blocks, and use it directly if there is any; If you want tofree-list
apply for memory space4.
dellocate()
It is a space release function. First judge the size of the space. If it is greater than 128 bytes, it will call the first-level configurator; if it is less than or equal to 128 bytes, it will find the corresponding onefree-list
and release the memory .
43. Virtual function table, virtual function table pointer
-
The function of the virtual function in C++ is mainly to realize the function of polymorphism ; and the so-called polymorphism means that when the parent class pointer points to the subclass , using the pointer to call the same function will show various behaviors because of the different subclasses pointed to. form
-
Implementation mechanism of virtual function:
The implementation of the virtual function mechanism mainly depends on the compiler:
- If a class has a virtual function, then there will be a corresponding virtual function table (usually placed in the constant area ) of the class, which is referred to here as
虚表
the function address of the virtual function虚表
of the class.- The existence of the virtual function table is related to the class
- An object instantiated by this class has a virtual function table pointer at the head of the object storage space , referred to as
vptr
;vptr
it points to the virtual function table of this class, and the address of the virtual function of this class is stored in the virtual function table, and then there is The address of the virtual function can be used to find the virtual function and execute it. (Virtual functions are stored in the code area ) - In general, the virtual functions stored in the virtual function table will be stored in the order of declaration
If the subclass
Derive
inherits the parent classBase
: (assuming both the parent class and the subclass have virtual functions)-
The subclass
Dreive
will also have its own virtual function table . In the virtual function table, the virtual function of the parent class comes first, and the virtual function of the subclass comes after. Note that this is the virtual function table (usually stored in the constant area ) -
The head of the memory space of the subclass object is the virtual function table pointer , which points to the virtual function table it owns.
-
If the subclass rewrites the virtual function of the parent class, the address of the rewritten virtual function
子类的虚函数表
will be replaced with the function newly written by the subclass (this feature determines that if the parent class pointer points to the object of the subclass, Polymorphic effects can be achieved ) -
For the virtual function of the parent class that has not been overridden, the function pointers in the virtual function table of the subclass and the virtual function table of the parent class are the same, which is equivalent to copying, but not the same
If the subclass
Derive
inherits from multiple parent classesBase1
,Base2
,Base3
-
A subclass has its own virtual function table, and if it inherits multiple parent classes, it will have multiple virtual function tables of its own ;
-
The head of the subclass object is a pointer to the virtual function table. If there are multiple parent classes, there will be multiple virtual function table pointers , pointing to multiple virtual function tables owned by the subclass.
-
For the virtual functions newly defined by the subclass itself, the addresses of these virtual functions will be stored in the first virtual function table
-
If the subclass rewrites the virtual function of the parent class, which virtual function of the parent class is rewritten, the corresponding function address will be modified in the corresponding virtual function table
About initialization timing:
- When the virtual function table is compiled , the compiler will create it. The virtual function table exists in the constant area , and the virtual function exists in the code area ;
- The virtual function table pointer is a part of the object and is initialized when the object is constructed; the virtual function and the virtual function table belong to the class, so they will be established at compile time.
- We often say that the compiler will call a certain function balabala ( I think this expression is a bit misleading ). I think what it wants to express is: when the compiler encounters the code to create an object in the process of compiling the source file into a binary file, it will automatically generate the binary code that calls the constructor, which is the so-called compiler call ...
- If a class has a virtual function, then there will be a corresponding virtual function table (usually placed in the constant area ) of the class, which is referred to here as
-
Are the virtual function tables of the subclass and the parent class independent?
答:
- Regardless of whether the subclass has its own newly created virtual function and whether it overrides the virtual function of the parent class, the subclass has its own virtual function table ;
- Because if the subclass rewrites the virtual function or newly defines the virtual function, it will change the content of the virtual function table. If the virtual function table is shared with the parent class, then the parent class cannot call its own virtual function?
44. Related content of lvalue and rvalue
左值与右值
:
Generally, on the left side of the equal sign, the address that can be taken is the lvalue ;
Generally, temporary values, future values, and literal values without addresses are rvalues ;
左值引用与右值引用
:
Lvalue references : Lvalue references can point to lvalues, but cannot point to rvalues
原因:
Because the lvalue reference is equivalent to the alias of the variable, the lvalue reference should have the ability to modify the variable, but the rvalue has no address, so it cannot be modified, so the lvalue reference cannot point to the rvalueBut
const 左值引用
the rvalue is not modified, soconst 左值引用
it can point to the rvalue, for exampleconst int& ref_left_a = 5;
,In vector's push_back(const int& val) —vec.push_back(5) uses lvalue reference to rvalue
Rvalue reference:
&&
, can point to an rvalue, but cannot point to an lvalue . After having an rvalue reference, you can use an rvalue reference to modify the rvalue
Why can an rvalue reference point to an rvalue, and its value can be modified?
Because the process of an rvalue reference pointing to an rvalue is essentially promoting an rvalue to an lvalue, and then defining an rvalue reference to point to an lvalue through the move function.
After the rvalue reference points to an lvalue through the move function, the lvalue can be modified.
右值引用如何指向左值?
Answer: Use the move() function to convert an lvalue to an rvalue , allowing an rvalue reference to point to an lvalue ; this is why an rvalue reference can modify an rvalue.
左值引用和右值引用本身是左值!
As long as the declared lvalue references and rvalue references are themselves lvalues!
But an rvalue reference returned by a function is an rvalue
So rvalue references can be either lvalues (direct declarations:
int && ref b = 5;
) or rvalues (functions returning rvalues:std::move(a)
return values)
In short
1. An lvalue reference can only point to an lvalue, but an lvalue reference with const can point to an rvalue
2. An rvalue reference can point directly to an rvalue, or use the move() function to point to an lvalue
3. The rvalue reference as a function parameter is more flexible: According to the second item above, you can receive left and right values, and you can also modify the incoming parameters . According to the first item, although you can use const to receive rvalues, you cannot modify them. .
右值引用和move的应用场景
Mainly implement move semantics in STL and custom classes to avoid copying
Note: Is it possible to provide one
移动构造函数
to move the data of the copied person over, and the copied person will not need it later , so that deep copying can be avoided
场景1:
Used to implement the move constructor, directly move the copied data over① When there is no copy construction when there is no rvalue reference , the const lvalue reference is used
const type& val
: in this way , both lvalue and rvalue can be received .
Using lvalue reference, because the reference is used when the parameter is passed into the function, it avoids the value copy when passing the parameter once . However, when deep copying is performed inside an object, a copy operation is unavoidable .
Then if a certain value is no longer needed, the above copy operation is a repeated operation, if the copied data can be directly transferred to the new object . Then the only remaining copy operation can also be avoided, thereby improving efficiency. But if you still use the const lvalue reference , it cannot be realized, because
const
there are , so the lvalue reference cannot be modified, and it is impossible to directly transfer the copied data to the new object safely .
② At this time, rvalue references come in handy : because the parameter of the move constructor is an rvalue reference
type&& val
, the function can receive rvalues and lvalues (when receiving lvalues, usemove()
a function to convert lvalues to rvalues)
In this way, the lvalue can be modified inside the function. Because when an rvalue reference points to an lvalue , the rvalue reference can modify the contents of the lvalue.
If the parameter is an rvalue, and the rvalue reference points to the rvalue, it is also logical and grammatical to apply move semantics to a will-xvalue, and the rvalue reference can also be modified after using an rvalue reference to refer to a will - xvalue
场景2:
push_back()
In functions similar to those in vectorempalce_back()
, there will be overloading of rvalue reference parameters, and active use ofmove()
functions will trigger move semanticsIn this case, copying can be reduced and the contents of the dying object can be moved directly to the container:
vector<string> vec; string a = "hello"; // 直接push_back()左值 vec.push_back(a); // a不变 vec.push_back(move(a)); // 触发了移动语义,将a中的内容“移动”到了容器内部,a将变成一个空字符串 // 也可以直接填右值 vec.push_back("hello");
In addition,
智能指针unique_ptr
there is only a move constructor, which can only move the internal ownership of the object, and cannot copy
场景3:
It is in the forward() function, but this is not commonly used, so I don’t know much about it
45. Implementation of Binary Heap
1. Binary heap
-
The essence of the binary heap is a complete binary tree , and it is stored in an array
-
Therefore, the binary heap stored in the array has the following properties:
If it
idx = 0
is stored in the position in the arrayroot
, the relationship between the index of the parent node and the left and right child nodes is as follows:- Parent node is
i
, left child node2i + 1
right child node2i+2
If it
idx = 1
is stored in the position in the arrayroot
, the relationship between the index of the parent node and the left and right child nodes is as follows:- Parent node is
i
, left child node2i
right child node2i+1
- Parent node is
-
Maximum heap (big top heap): each node is larger than its two child nodes
-
Min heap (small top heap): each node is smaller than his two child nodes
2. Use binary heap to implement priority queue
-
In fact, the so-called priority queue is a binary heap.
-
Since it is a queue, it is natural to implement some basic functions:
1. Structure
2. Enter the queue
push()
3. Get out of the queue (get the first element of the queue and pop it up)
get_pop_front()
4、
size()
5. Is it empty?
empty()
-
In fact, there are two functions that are very important to realize the above functions, that is, the sum of nodes
上浮swim()
.下沉sink()
These two functions are the essence of the binary heap!
Show Me Code!
class PriorityQueue {
private:
int* m_pArray;
int m_length;
int m_cap;
// 节点上浮--大顶堆
void swim_max(int idx){
while(idx > 1 && m_pArray[idx] > m_pArray[idx / 2]) {
// 当前节点大于父节点
int temp = m_pArray[idx]; // 交换
m_pArray[idx] = m_pArray[idx / 2];
m_pArray[idx / 2] = temp;
idx = idx / 2; // 节点更新
}
}
// 节点上浮--小顶堆
void swim_small(int idx){
while(idx > 1 && m_pArray[idx] < m_pArray[idx / 2]) {
int temp = m_pArray[idx];
m_pArray[idx] = m_pArray[idx / 2];
m_pArray[idx / 2] = temp;
idx = idx / 2;
}
}
// 节点下沉--大顶堆
void sink_max(int idx){
while(2 * idx <= m_length) {
int l = 2 * idx;// 左节点
if(l < m_length && m_pArray[l] < m_pArray[l + 1]) // 求左右节点的最大值(大顶堆)/最小值(小顶堆)
l++;
if(m_pArray[l] < m_pArray[idx]) // 如果已经满足的条件--那么break; 下沉结束
break;
int temp = m_pArray[idx]; // 交换
m_pArray[idx] = m_pArray[l];
m_pArray[l] = temp;
idx = l; // idx = l; 更新idx
}
}
// 节点下沉--小顶堆
void sink_max(int idx){
while(2 * idx <= m_length) {
int l = 2 * idx;
if(l < m_length && m_pArray[l] > m_pArray[l + 1])
l++;
if(m_pArray[l] > m_pArray[idx])
break;
int temp = m_pArray[idx];
m_pArray[idx] = m_pArray[l];
m_pArray[l] = temp;
idx = l;
}
}
public:
// 构造,参数N表示队列或二叉堆的最大容量
PriorityQueue(int N) {
m_pArray = new int[N + 1]; // 因为从数组的下标1开始存节点的
m_length = 0;
m_cap = N;
}
// 析构
~PriorityQueue() {
delete []m_pArray;
m_pArray = nullptr;
m_length = 0;
m_cap = 0;
}
int size() {
return m_length;
}
bool empty() {
return m_length == 0;
}
// 入队--大顶堆
void push(int ele) {
if(m_length >= m_cap) return;
m_pArray[++m_length] = ele;
swim_max(m_length);
// 或者小顶堆时用 swim_min(m_length);
}
// 出队--大顶堆
int get_pop_front() {
int ret = m_pArray[1];
m_pArray[1] = m_pArray[m_length--];// 将最后的值交换到root节点
m_pArray[m_length + 1] = 0; // 尾部置空
sink_max(1);
// 或者小顶堆时用 sink_min(1);
return ret;
}
};
3. Use binary heap to implement heap sorting
- For heap sorting using a binary heap, the above priority queue can be used
- In the sorting algorithm function, create a priority queue, that is, a binary heap, and then add all array elements to the heap (queue)
- Then take each element from the priority queue in turn and put it in the original array
- return the original array
For large top heaps, sort them in descending order; for small top heaps, sort them in ascending order;
void headSort(vector<int> &nums){
PriorityQueue PQ(nums.size() + 1); // PQ是一个局部变量
//PriorityQueue* pPQ = new PQ(nums.size() + 1);
for(auto it : nums) PQ.push(it);
for(auto & it : nums) it = PQ.get_pop_front();
// delete pPQ;
}