面试速通宝典——13

news2024/10/5 18:32:43

208. class里面定义int a,如果不实现构造函数,实例化这个类,a的值是?

‌‌‌‌  答:a的值是未定义的(在C++标准中成为“未初始化”)。

解释
‌‌‌‌  在C++中,如果一个类中定义了一个成员变量(如 int a),但没有提供构造函数来初始化这个变量,那么在实例化该类时,成员变量 a 的值是未定义的。未定义的意思是它的值可能是任何东西,因为它取决于内存中该位置之前存储的内容。

例如:

class MyClass {
public:
    int a;
};

int main() {
    MyClass obj;
    std::cout << obj.a << std::endl; // a 的值未定义
    return 0;
}

‌‌‌‌  在上述代码中,obj.a 的值是未初始化的,因此它可能是任何值。

‌‌‌‌  为了避免未定义的行为,通常建议在类中提供构造函数来初始化成员变量。例如:

class MyClass {
public:
    int a;
    MyClass() : a(0) {} // 构造函数将 a 初始化为 0
};

int main() {
    MyClass obj;
    std::cout << obj.a << std::endl; // 现在 a 的值是 0
    return 0;
}

‌‌‌‌  通过提供一个构造函数,可以确保成员变量有一个已知的初始值,从而避免未定义行为带来的潜在问题。

209. unique_ptr可以作为函数返回值么?

‌‌‌‌  可以。
‌‌‌‌  当函数返回一个unique_ptr时,他会利用C++移动语义将所有权从函数内部转移给调用方

解释

‌‌‌‌  unique_ptr 可以作为函数的返回值,并且当函数返回一个 unique_ptr 时,它会利用 C++ 的移动语义将所有权从函数内部转移给调用方。我们可以通过几个步骤来理解这段话:

  1. unique_ptr 的基本概念

    • unique_ptr 是 C++11 引入的智能指针类型,位于 <memory> 头文件中。
    • 它是独占所有权的智能指针,这意味着同一时间只能有一个 unique_ptr 拥有某个对象的所有权。
  2. 移动语义

    • 移动语义是 C++11 引入的特性,允许资源的所有权从一个对象转移到另一个对象,而不是复制资源。
    • 移动语义通过移动构造函数和移动赋值运算符实现,用 std::move 函数来显式调用。
  3. 返回 unique_ptr 的函数示例

    • 当函数返回一个 unique_ptr 时,编译器会自动利用移动语义将所有权从函数内部转移给调用方,而不是复制 unique_ptr
    • 这避免了所有权的复制,并且保证 unique_ptr 的独占所有权特性。

以下是一个示例:

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass Constructor" << std::endl; }
    ~MyClass() { std::cout << "MyClass Destructor" << std::endl; }
};

std::unique_ptr<MyClass> createMyClass() {
    // 创建一个 unique_ptr 并返回
    return std::make_unique<MyClass>();
}

int main() {
    // 调用函数,接收返回的 unique_ptr
    std::unique_ptr<MyClass> ptr = createMyClass();
    // 使用 ptr 访问 MyClass 的成员
    return 0;
}

在这个例子中:

  1. createMyClass 函数创建了一个 unique_ptr 并返回它。
  2. main 函数中,ptr 接收 createMyClass 返回的 unique_ptr
  3. createMyClass 返回时,unique_ptr 的所有权从函数内部转移给 main 函数中的 ptr,而不是复制 unique_ptr

‌‌‌‌  总结来说,当函数返回一个 unique_ptr 时,利用移动语义将所有权从函数内部转移给调用方,确保了资源的独占所有权和高效的资源管理。

210. 快手直播流媒体是走长连接网关推送的嘛?

‌‌‌‌  通常会通过长连接来推送流媒体内容,这是为了确保数据传输的实时性和可靠性

211. HTTP3.0 对比HTTP 2.0 的优势

  1. 基于QUIC协议:HTTP/3使用QUIC(快速UDP互联网协议)代替TCP,使得连接建立更快,减少了握手的时间。
  2. 提高了传输效率:QUIC支持多路复用,但与HTTP/2不同,它避免了TCP的“队头阻塞”问题,使得即使部分数据丢失也不会影响其他数据的传输。
  3. 更好的错误恢复:QUIC在包级别实现了前向纠错和快速重传机制,减少了因为丢包导致的延迟。
  4. 内置TLS加密:QUIC默认内置了TLS1.3,提高了数据传输的安全性
  5. 更好的数据迁移:支持连接ID,即使用户的IP地址变化,也能无缝继续通信。

212. HTTP2.0对比HTTP1.1的优势

  1. 多路复用:HTTP/2通过在一个TCP上连接同时发送多个请求和接收多个响应消息,来消除不必要的延迟并提高页面加载速度。
  2. 头部压缩:HTTP/2引入了头部压缩机制,减小了数据包的大小并提高了传输效率。
  3. 服务器推送:HTTP/2可以让服务器把客户端需要的资源主动“推送”给客户端,减少了往返的数据交换次数。
  4. 流优先级:在HTTP/2中,客户端可以设置资源的优先级,使得重要的资源可以优先加载,从而提高了用户体验。
  5. 二进制协议:HTTP/2是二进制协议,不再是文本协议,二进制协议解析更高效,更少错误。

213. 讲一下进程、线程、协程的区别

  1. 进程:是操作系统分配资源的最小单位具备一定的独立功能,能运行在处理器上每个进程有自己的独立内存空间
  2. 线程:是程序执行的最小单位,线程是进程内部的一个实体,是比进程更小的能独立运行的基本单位,一个进程中可以有多个线程,这些线程共享进程的资源
  3. 协程:是一种用户态的轻量级线程,也就是协程的切换不需要操作系统参与,由系统用户自行调度,因此效率更高。协程可以理解为“协作的线程”。它比线程更小,因为它只使用了很少的内存大小。

214. 一个进程调用malloc最大能分配到多少内存?

‌‌‌‌  在32位系统中,最大理论限制约为2GB到4GB
‌‌‌‌  在64位系统中,这个限制远大于物理内存,但实际上受制于系统的物理内存和交换空间

215. 物理机malloc(10G)会发生什么?

‌‌‌‌  如果物理机的可用内存加上交换空间小于10GB,malloc(10G)会失效,并返回一个空指针,因为没有足够的空间来分配这么大的内存块。
‌‌‌‌  如果可用内存加上交换空间足以满足10GB的要求,malloc将成功分配内存

216. vector线程安全吗?不安全在哪?

‌‌‌‌  Vector在C++STL中是不线程安全的
‌‌‌‌  不安全的原因主要在于他的操作(如增加、删除元素等)在多线程环境下没有同步,可能会导致竞态条件。

217. 多线程下使用vector一定要加锁嘛?

‌‌‌‌  为了保证在多线程环境下对Vector的操作安全,业务代码需要进行手动的锁控制。

解释

‌‌‌‌  在多线程环境中使用 std::vector 时,是否需要加锁取决于如何访问和修改 vector。这里有几个关键点需要考虑:

  1. 只读访问

    • 如果所有线程都只是读取 vector,没有任何线程对其进行修改,那么不需要加锁。这种情况下,vector 的数据是共享的常量数据,线程之间不会互相干扰。
  2. 写操作

    • 如果有任何一个线程对 vector 进行写操作(包括插入、删除、修改元素等),那么就需要加锁。因为 vector 在写操作时可能会改变其内部状态(例如重新分配内存),这会导致数据竞争和未定义行为。
  3. 混合操作

    • 如果一些线程在读取 vector,而另一些线程在写入 vector,那么也需要加锁。加锁可以防止在读取过程中发生修改,导致数据不一致或崩溃。

总结来说,为了确保线程安全,通常需要在访问 vector 时进行加锁:

  • 只读访问:不需要加锁。
  • 写操作:需要加锁。
  • 混合操作:需要加锁。

示例代码

以下是一个简单的示例,展示了如何在多线程环境中使用 std::mutexvector 进行加锁保护:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>

std::vector<int> vec;
std::mutex mtx;

void addToVector(int val) {
    std::lock_guard<std::mutex> lock(mtx);
    vec.push_back(val);
}

void printVector() {
    std::lock_guard<std::mutex> lock(mtx);
    for (int v : vec) {
        std::cout << v << " ";
    }
    std::cout << std::endl;
}

int main() {
    std::thread t1(addToVector, 1);
    std::thread t2(addToVector, 2);
    std::thread t3(printVector);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

在这个示例中:

  1. std::mutex mtx 用于保护 vec 的访问。
  2. std::lock_guard<std::mutex> lock(mtx) 确保在作用域结束时自动释放锁。
  3. addToVectorprintVector 函数都使用锁来保护对 vec 的访问,以确保线程安全。

‌‌‌‌  这种加锁机制可以防止数据竞争,并确保在多线程环境中安全地访问和修改 vector

我再详细的解释一下这段代码。

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
  • #include <iostream>:引入标准输入输出流库,用于打印输出。
  • #include <vector>:引入 std::vector 容器。
  • #include <thread>:引入多线程库,用于创建和管理线程。
  • #include <mutex>:引入互斥量库,用于线程同步,确保对共享资源的访问是安全的。
std::vector<int> vec;
std::mutex mtx;
  • std::vector<int> vec:声明一个全局的整数向量 vec,所有线程共享。
  • std::mutex mtx:声明一个全局的互斥量 mtx,用于保护对 vec 的访问。
void addToVector(int val) {
    std::lock_guard<std::mutex> lock(mtx);
    vec.push_back(val);
}
  • void addToVector(int val):定义一个函数 addToVector,接受一个整数参数 val
  • std::lock_guard<std::mutex> lock(mtx):创建一个 std::lock_guard 对象 lock,并传递互斥量 mtx。这会立即尝试获取锁,如果成功,在 lock 的生命周期内持有锁,并在 lock 离开作用域时自动释放锁。
  • vec.push_back(val):将 val 添加到向量 vec 的末尾。由于加锁保护,这个操作是线程安全的。
void printVector() {
    std::lock_guard<std::mutex> lock(mtx);
    for (int v : vec) {
        std::cout << v << " ";
    }
    std::cout << std::endl;
}
  • void printVector():定义一个函数 printVector,没有参数。
  • std::lock_guard<std::mutex> lock(mtx):同样创建一个 std::lock_guard 对象 lock,并获取互斥量 mtx 的锁。
  • for (int v : vec):范围循环,遍历 vec 中的每个元素 v
  • std::cout << v << " ":将每个元素打印到标准输出,并以空格分隔。
  • std::cout << std::endl:在输出结束后换行。
int main() {
    std::thread t1(addToVector, 1);
    std::thread t2(addToVector, 2);
    std::thread t3(printVector);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

  • int main():程序的主入口。
  • std::thread t1(addToVector, 1):创建一个线程 t1,运行函数 addToVector,并传递参数 1。此时,addToVector(1) 将在线程 t1 中运行。
  • std::thread t2(addToVector, 2):创建另一个线程 t2,运行函数 addToVector,并传递参数 2
  • std::thread t3(printVector):创建第三个线程 t3,运行函数 printVector
  • t1.join():等待线程 t1 完成。
  • t2.join():等待线程 t2 完成。
  • t3.join():等待线程 t3 完成。

‌‌‌‌  通过 join(),确保主线程在继续执行前等待所有子线程完成,确保所有操作按预期顺序执行。整体来看,这段代码展示了如何在多线程环境下安全地对共享数据结构(std::vector)进行读写操作。

218. 两个线程同时对vector下相同索引的元素修改会发生什么?

‌‌‌‌  两个线程若同时对Vector的相同索引元素进行修改,将会导致未定义行为结果可能会是线程中的一个或两个的修改发生,或者导致数据损坏

219. C++内存序介绍一下

  1. memory_order_relax : 放宽内存顺序,不要求操作之间的顺序
  2. memory_order_consume : 较为弱的顺序要求,仅在特定的平台上有效。
  3. memory_order_acquire : 阻止操作重排序到原子操作前。
  4. memory_order_release : 阻止操作重排序到原子操作后。
  5. memory_order_acq_rel : 同时应用acquire和release。
  6. memory_order_seq_cst : 顺序一致,所有线程看到的操作顺序相同。

解释

memory_order_relaxed:放宽内存顺序,不要求操作之间的顺序

  • 特点:不施加任何同步或排序约束,只保证原子操作的原子性。
  • 用途:用于不需要跨线程同步的计数器或统计场景,性能最佳。

memory_order_consume:较为弱的顺序要求,仅在特定平台上有效

  • 特点:确保数据依赖(data dependency)的可见性,但比 memory_order_acquire 更弱。在大多数平台上,memory_order_consume 等同于 memory_order_acquire,只有少数平台(如某些ARM架构)对其有特殊优化。
  • 用途:较少使用,主要用于那些高度依赖数据依赖性传递的平台。

memory_order_acquire:阻止操作重排序到原子操作之前

  • 特点:确保在原子操作之前的所有加载和存储都不会重排序到原子操作之后。通常用于加载操作。
  • 用途:用于获取锁或其他同步机制,确保在此之前的操作在所有线程中都可见。

memory_order_release:阻止操作重排序到原子操作之后

  • 特点:确保在原子操作之后的所有加载和存储都不会重排序到原子操作之前。通常用于存储操作。
  • 用途:用于释放锁或其他同步机制,确保在此之后的操作在所有线程中都可见。

memory_order_acq_rel:同时应用 acquire 和 release

  • 特点:结合了 memory_order_acquirememory_order_release 的效果,确保在原子操作之前的所有加载和存储都不会重排序到原子操作之后,同时确保在原子操作之后的所有加载和存储也不会重排序到原子操作之前。
  • 用途:用于需要在同一个操作中既获取又释放同步的场景,比如读-修改-写操作。

memory_order_seq_cst:顺序一致,所有线程看到的操作顺序相同

  • 特点:提供最强的内存序保证,所有线程都看到相同的操作顺序。所有 memory_order_seq_cst 操作都在全局顺序中执行,确保了最强的一致性。
  • 用途:用于需要强一致性保证的场景,确保所有线程都能以相同的顺序看到原子操作。

示例:

#include <iostream>
#include <atomic>
#include <thread>

std::atomic<int> data(0);
std::atomic<bool> ready(false);

void producer() {
    data.store(42, std::memory_order_relaxed);
    ready.store(true, std::memory_order_release);
}

void consumer() {
    while (!ready.load(std::memory_order_acquire));
    std::cout << "Data: " << data.load(std::memory_order_relaxed) << std::endl;
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    
    t1.join();
    t2.join();

    return 0;
}

  • producer 线程首先将数据存储为 42,然后设置 ready 为 true。
  • consumer 线程在 ready 被设置为 true 之前,保持轮询。
  • std::memory_order_releasestd::memory_order_acquire 确保了正确的同步,确保 consumer 看到 data 的正确值。

总之,内存序在多线程编程中是非常重要的,它们提供了不同级别的同步和排序保证,以便在不同的场景下选择合适的内存序来确保数据一致性和性能。

这里我再解释一下这段代码:

#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> data(0);
std::atomic<bool> ready(false);

  • #include <iostream>:用于标准输入输出。
  • #include <atomic>:用于原子操作和原子变量。
  • #include <thread>:用于多线程编程。
  • std::atomic<int> data(0):声明一个原子整数变量 data,并初始化为 0。原子变量保证对该变量的所有操作都是原子的。
  • std::atomic<bool> ready(false):声明一个原子布尔变量 ready,并初始化为 false。

定义生产者线程函数:

void producer() {
    data.store(42, std::memory_order_relaxed);
    ready.store(true, std::memory_order_release);
}

  • data.store(42, std::memory_order_relaxed):将 data 设置为 42。使用 memory_order_relaxed 表示这个存储操作没有任何同步或排序约束,只保证原子性。
  • ready.store(true, std::memory_order_release):将 ready 设置为 true。使用 memory_order_release 确保在此操作之前的所有操作(即 data.store)不会重排序到这个操作之后。这确保了其他线程在看到 ready 为 true 时,也能看到 data 的正确值。

定义消费者线程函数:

void consumer() {
    while (!ready.load(std::memory_order_acquire));
    std::cout << "Data: " << data.load(std::memory_order_relaxed) << std::endl;
}

  • while (!ready.load(std::memory_order_acquire)):循环等待,直到 ready 为 true。使用 memory_order_acquire 确保在这个操作之后的所有操作(即 data.load)不会重排序到这个操作之前。这确保了在读取 data 之前,consumer 一定能看到 ready 为 true。
  • std::cout << "Data: " << data.load(std::memory_order_relaxed) << std::endl:打印 data 的值。使用 memory_order_relaxed 表示这个加载操作没有任何同步或排序约束,只保证原子性。

main 函数中创建和启动线程:

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    
    t1.join();
    t2.join();

    return 0;
}
  • std::thread t1(producer):创建一个线程 t1,执行 producer 函数。
  • std::thread t2(consumer):创建另一个线程 t2,执行 consumer 函数。
  • t1.join():等待线程 t1 结束。确保 main 线程在继续执行前等待 t1 完成。
  • t2.join():等待线程 t2 结束。确保 main 线程在继续执行前等待 t2 完成。

内存序的作用

  • memory_order_relaxed:在 producer 中用于 data.store 和在 consumer 中用于 data.load。这种内存序不施加任何同步或排序约束,只保证操作的原子性。它适用于不涉及跨线程同步的简单操作。
  • memory_order_release:在 producer 中用于 ready.store。这种内存序确保在此操作之前的所有操作不会重排序到此操作之后。这意味着当 ready 被设置为 true 时,data 已经被正确地设置为 42。
  • memory_order_acquire:在 consumer 中用于 ready.load。这种内存序确保在此操作之后的所有操作不会重排序到此操作之前。这意味着当 ready 为 true 时,consumer 一定能看到 data 的正确值。

总结

  • producer 线程首先将 data 设置为 42,然后设置 ready 为 true。
  • consumer 线程循环等待,直到看到 ready 为 true,之后打印 data 的值。
  • 使用 memory_order_releasememory_order_acquire 确保了线程之间的同步,使得 consumer 在读取 data 时看到正确的值。

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

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

相关文章

苹果荔枝柠檬梨柿子数据集 水果数据集 树上1500张 带标注 voc yolo 5类

苹果荔枝柠檬梨柿子数据集 水果数据集 树上1500张 带标注 voc yolo 5类 苹果荔枝柠檬梨柿子数据集 名称 苹果荔枝柠檬梨柿子数据集 (Apple, Litchi, Lemon, Pear, Persimmon Dataset) 规模 图像数量&#xff1a;1498张图像。类别&#xff1a;5种水果类别。标注个数&#xff…

linux基础指令的认识

在正式学习linux前&#xff0c;可以简单认识一下linux与win的区别 win&#xff1a;是图形界面&#xff0c;用户操作更简单&#xff1b;在刚开始win也是黑屏终端 指令操作&#xff0c;图形界面就是历史发展的结果。Linux&#xff1a;也存在图形界面比如desktop OS&#xff1b;但…

Redis实现点赞

部分实体类 TableId(value "id", type IdType.AUTO)private Long id;/*** 商户id*/private Long shopId;/*** 用户id*/private Long userId;/*** 用户图标*/TableField(exist false)private String icon;/*** 用户姓名*/TableField(exist false)private String …

lxml安装失败的一个解决方案

下载离线安装包&#xff0c;网址 https://pypi.org/simple/lxml/需要对应自己的python版本&#xff0c;网上教程很多&#xff0c;不再赘述 下载完成后将该文件&#xff0c;重命名&#xff08;下面是下载好的文件&#xff0c;上面是重命名的文件&#xff0c;就是在第二个版本&a…

【Linux】进程周边之优先级

目录 一、优先级 1.为什么要有进程优先级&#xff1f; 2.什么是进程优先级&#xff1f; 3.优先级的初始设定 3.1 PRI 和 NI 3.2如何修改优先级&#xff1f;&#xff08;sudo/root&#xff09; 3.2.1 概念&#xff1a; 3.2.2 如何查看进程的优先级&#xff1f; 3.3.3 或…

【深度学习】自动微分——Autodiff or Autograd?

论文 [1].CSC321 Lecture 10: Automatic Differentiation [2].Automatic Differentiation in Machine Learning:a Survey 关键点总结&#xff1a; 雅可比矩阵&#xff1a;对于多变量函数 y ⃗ f ( x ⃗ ) \vec{y} f(\vec{x}) y ​f(x )&#xff0c;其梯度矩阵&#xff08;…

GPT新功能Canvas!对普通用户最友好的功能!

原文链接&#xff1a;GPT4新功能Canvas!对普通用户最友好的功能&#xff01; ChatGPT更新了一个极度优雅&#xff0c;对普通人极度友好的功能。 而且&#xff0c;顺带&#xff0c;又碾死了一批AI赛道里&#xff0c;做AI文本编辑和Code Copilot的初创企业&#xff0c;一波端掉…

自动驾驶核心技术:感知融合、规划决策、控制执行

1、前言 简单来说&#xff0c;实现自动驾驶需要解决三个核心问题&#xff1a;“我在哪?我要去哪?我该如何去?”能完整解决这三个问题就是真正的自动驾驶。 目前&#xff0c;自动驾驶汽车关键技术主要包括环境感知、精准定位、决策与规划、控制与执行、高精地图与车联网V2X以…

ZYNQ:GPIO 之 MIO 按键中断实验

实验目的 使用底板上的 PS 端的用户按键 PS_KEY1 通过中断控制核心板上 LED2 的亮灭 简介 Zynq 芯片的 PS 部分是基于使用双核 Cortex-A9 处理器和 GIC pl390 中断控制器的 ARM 架构。中断结 构与 CPU 紧密链接&#xff0c;并接受来自 I/O 外设&#xff08;IOP&#xff09…

P4240 毒瘤之神的考验

毒瘤之神的考验 - 洛谷 定义 猜想与有关 发现上式1-1 上下两边乘gcd(i,j)有 带入1-1有 化简 n<m 经典代换Tke eT/k 然后化简不了了 这个时候我们可以把一部分看出一个整体 分析这两个函数&#xff0c;发现f(x) 可以在下预处理出来 g(x,y)有以下递推式 因此也可以在下处理…

三、流程控制

流程控制 选择结构&#xff08;if-else&#xff09;选择结构&#xff08;when&#xff09;循环结构&#xff08;for&#xff09;循环结构&#xff08;while&#xff09; 经过前面的学习&#xff0c;我们知道&#xff0c;程序都是从上往下依次运行的&#xff0c;但是&#xff0c…

事件抽取(Event Extraction, EE)

一、引言 事件抽取&#xff08;Event Extraction, EE&#xff09;是信息抽取领域中的一个重要任务&#xff0c;旨在从非结构化文本中识别和抽取事件相关的信息。事件抽取通常包括识别事件触发词、事件类型以及事件中的参与者、时间、地点等元素&#xff0c;最终将这些信息结构…

【进阶OpenCV】 (4)--图像拼接

文章目录 图像拼接1. 读取图片2. 计算图片特征点及描述符3. 建立暴力匹配器4. 特征匹配5. 透视变换6. 图像拼接 总结 图像拼接 图像拼接是一项将多张有重叠部分的图像&#xff08;这些图像可能是不同时间、不同视角或者不同传感器获得的&#xff09;拼成一幅无缝的全景图或高分…

论文翻译 | Model-tuning Via Prompts Makes NLP Models Adversarially Robust

摘要 近年来&#xff0c;NLP从业者集中于以下实践:(i)导入现成的预训练(掩码)语言模型;(ii)在CLS令牌的隐藏表示(随机初始化权重)上附加多层感知器;(iii)在下游任务(MLP-FT)上微调整个模型。这一过程在标准的NLP基准上产生了巨大的收益&#xff0c;但这些模型仍然很脆弱&#x…

mysql单表查询·3

准备好表 create table product(id int primary key,name varchar(32),price double,category varchar(32) ); # 插入数据 INSERT INTO product(id,name,price,category) VALUES(1,联想,5000,c001); INSERT INTO product(id,name,price,category) VALUES(2,海尔,3000,c001); I…

加密与安全_HOTP一次性密码生成算法

文章目录 HOTP 的基础原理HOTP 的工作流程HOTP 的应用场景HOTP 的安全性安全性增强措施Code生成HOTP可配置项校验HOTP可拓展功能计数器&#xff08;counter&#xff09;计数器在客户端和服务端的作用计数器的同步机制客户端和服务端中的计数器表现服务端如何处理计数器不同步计…

好用的苹果笔推荐!五大高品质王者款!附避坑宝典助你选购无忧!

现在平板和电容笔在一定程度上可以替代传统的笔和纸&#xff0c;很多用户在购置iPad后&#xff0c;急需找到一款好用的电容笔。但由于苹果原装笔的价格太过高昂&#xff0c;让许多人不得不选择平替电容笔&#xff01;下面我就为大家推荐五款高品质的电容笔&#xff0c;并分享几…

单细胞hdWGCNA分析学习和整理

hdWGCNA的分析逻辑是跟bulkRNA数据中的WGCNA基本一样&#xff0c;只是hdWGCNA中多了一步metacell过程&#xff0c;有助于减少无用的信息(单细胞数据有很多零值&#xff0c;会影响分析结果)。 WGCNA的基础信息可见既往推文: https://mp.weixin.qq.com/s/2Q37RcJ1pBy_WO1Es8upIg…

二分查找算法专题(2)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; 优选算法专题 对于二分查找算法不是很了解或者只了解一部分的小伙伴一定要去看下面这篇博客&#xff1a;二分查找算法的介绍与另外一种查找方…

【EcoNAS: Finding Proxies for Economical Neural Architecture Search】读后感

鄙人近日阅读了来自 CVPR2020 的一篇论文, 在这里与大家分享一下读后感, 有些东西可能不一定正确, 还望大家指正. Exploration Study 进化算法是 NAS 中一种常用的搜索算法, 使用进化算法时, 如果将每一个候选网络训练到完全收敛来评估性能的话, 需要耗费大量的资源 (时间, G…