深入探讨C存储类和存储期——Storage Duration

   🔗 《C语言趣味教程》👈 猛戳订阅!!!

—— 热门专栏《维生素C语言》的重制版 ——

  • 💭 写在前面:这是一套 C 语言趣味教学专栏,目前正在火热连载中,欢迎猛戳订阅!本专栏保证篇篇精品,继续保持本人一贯的幽默式写作风格,当然,在有趣的同时也同样会保证文章的质量,旨在能够产出 "有趣的干货" !本系列教程不管是零基础还是有基础的读者都可以阅读,可以先看看目录! 标题前带星号 (*) 的部分不建议初学者阅读,因为内容难免会超出当前章节的知识点,面向的是对 C 语言有一定基础或已经学过一遍的读者,初学者可自行选择跳过带星号的标题内容,等到后期再回过头来学习。值得一提的是,本专栏 强烈建议使用网页端阅读! 享受极度舒适的排版!你也可以展开目录,看看有没有你感兴趣的部分!希望需要学 C 语言的朋友可以耐下心来读一读。最后,可以订阅一下专栏防止找不到。

" 有趣的写作风格,还有特制的表情包,而且还干货满满!太下饭了!"

—— 沃兹基硕德

【C语言趣味教程】(7) 存储类:auto 关键字 | register 关键字 | 存储期 | 自动存储期 | 动态存储期 | 线程存储期 | 动态分配存储期 | 静态变量

📜 本章目录:

Ⅰ. 存储类(Storage Class)

0x00 引入:什么是存储类?

0x01 auto 关键字

0x01 注意:auto 只能修饰局部变量!

* 0x02 拓展阅读:C++ 中改版后的 auto

0x03 static 关键字初探

* 0x04 register 关键字

0x05 extern 关键字

Ⅱ. 存储期(Storage Duration)

0x00 引入:存储器的概念

0x01 自动存储期

0x02 静态存储期

* 0x03 动态分配存储期

* 0x04 线程存储期

Ⅲ. 静态变量(Static)

0x00 static 关键字

0x01 局部静态变量

0x02 全局静态变量

0x03 静态变量的初始值默认为0


Ⅰ. 存储类(Storage Class)

0x00 引入:什么是存储类?

❓ 你没有听说过存储类的概念?

存储类 (Storage Class) 在 C 语言标准中用来 规定变量与函数的可访问性与生命周期。

"可访问性" 的概念就是我们上一章说的作用域范围,我们先关注以下 4 种存储类别:

auto
static
register
extern

简单来说,存储类别定义了变量和函数的存储位置、生命周期和作用域。 

为什么要引出存储类的概念?


大多数教程似乎并不涉及 "存储类" 的说法概念,讲解 auto, static 等关键字的时候都是直接介绍,而不引出存储类 (即 Storage Class) 的概念。

正因如此,我们想把存储类的概念单独抽出来作为一个章节去讲解,介绍存储类的概念,介绍一些常见的存储类别 (auto, staitc, register...) 。而不是单独的介绍这些关键字,孤立疏远它们的联系。

当然了,如果你是 C 语言初学者,一开始就接触存储类的概念大有裨益,利于体系化学习。如果你掌握 C语言基础,但是似乎之前没有听说过这个概念,也不用担心。像常见的 auto, extern, register, static 等关键字就属于 "存储类",通过本章的学习,可以体系化地了解这些东西,把它们归类起来。

0x01 auto 关键字

auto 是 "自动" 的意思,代表 变量在函数开始时自动创建,在函数结束时被自动销毁。

即使用 auto 修饰的变量,是具有自动存储器的局部变量。

auto int a = 0;   // 表示a是一个自动存储类型,会在函数结束后自动销毁。

  遗憾的是,大家都懒得去用它,这是为什么呢?

因为定义在函数中的所有变量都是自带 auto 的,即局部变量都是自带 auto 的。

💭 举个例子:

auto int a = 10; (A)
int a = 10;      (B)

(A) 和 (B) 是一样的效果,我们不需要手动去加,因为 auto 是所有局部变量的默认存储类。

当使用 auto 修饰后,表示 a 是一个自动存储类型,它会在函数结束以后自动销毁。

0x01 注意:auto 只能修饰局部变量!

值得注意的是,auto 只能在函数内使用!这就意味着 auto 只能修饰局部变量。

❌ 错误演示:auto 修饰全局变量

#include <stdio.h>    

auto int a = 10;   // 全局变量

int main(void)
{
	auto int b = 20;   // 局部变量

	return 0;
}

🚩 运行结果:error E0149

此时必然会触发报错和警告:warning C4042: “a”: 有坏的存储类

* 0x02 拓展阅读:C++ 中改版后的 auto

温馨提示:以下内容涉及 C++,读者可按自身情况阅读。

 C++ 标准委员会觉得这 auto 也太尴尬了,我们得给它来一波加强。为了缓解 auto 的尴尬,C++ 标准委员会把 auto 原来的功能给废弃了。并赋予了 auto 全新的含义!

📚 游戏更新补丁(bushi):auto 现在不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器。auto 声明的变量必须由编译器在编译时推导而得。

也就是说,它可以自动推导出数据的类型:

int a = 0;
auto c = a;  // C++11给auto关键字赋予了新的意义:自动推导c的类型

你的右边是什么,它就会推导出相应的类型,任何类型都可以实现,包括但不限于:

auto ch = 'A';
auto e = 10.11;
auto pa = &a;

为了方便测试,我们来打印一下对象的类型看看:

#include<iostream>
using namespace std;

int main()
{
    int a = 0;
    auto c = a;  // 自动推导c的类型

    auto ch = 'A';
    auto e = 10.11;
    auto pa = &a;

    // typeid - 打印对象的类型
    cout << typeid(c).name() << endl;   // i
    cout << typeid(ch).name() << endl;  // c
    cout << typeid(e).name() << endl;   // d
    cout << typeid(pa).name() << endl;  // Pi

    return 0;
}

🚩 运行结果如下:

 emmm... 确实

 这时候可能有人会觉得,这一波操作好像也没啥意义啊,

直接写数据类型不香吗?int c = a,我们继续往下看~

💭 举个例子:auto 的使用场景

 

处理又臭又长的数据类型 

💬 遇到这种场景,就能体会到 auto 的香了:

#include <iostream>
#include <map>

int main(void) 
{
    std::map<std::string, std::string> dict = {{"sort", "排序"}, {"insert", "插入"}};
    std::map<std::string, std::string>::iterator it = dict.begin();
    // 这个类型又臭又长,写起来太麻烦了。。。
    
    auto it = dict.begin();   // 可以改成这样,爽!
    // ↑ 根据右边的返回值去自动推导it的类型,写起来就方便多了

    return 0;
}

 像遇到这种又臭又长的类型,而且还要经常使用。

这时候使用 auto 帮你自动推到类型,就很爽了!

📌 注意事项:使用 auto 是必须要给值的!

int i = 0;
auto j;  ❌

auto j = i;  必须给值!!

这就意味着,auto 是不能做参数的!

auto 不能作为函数的参数

void TestAuto(auto a); ❌

此处代码编译失败,auto 不能作为形参类型,因为编译器无法对 a 的类型进行推导!

auto 不能直接用来声明数组

 auto b[3] = {4,5,6};   ❌

📌 为了避免与 C++98 中的 auto 发生混淆,C++11 只保留了 auto 作为类型指示符的用法。

 auto 在实际中最常见的优势用法就是 C++11 提供的新式 for 循环,

还有 lambda 表达式等进行配合使用。我们可以继续往下看~

auto 与指针结合起来使用:

📚 改版后的 auto 非常聪明,它在推导的时候其实是非常灵活的:

int main(void)
{
    int x = 10;
    auto a = &x;  // int*
    auto* b = &x; // int*
    auto& c = x;  // int

    return 0;
}

在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型!

否则编译器将会报错,因为编译器实际只对第一个类型进行推导,

然后用推导出来的类型定义其他变量。

auto a = 1, b = 2;
auto c = 3, d = 4.0;  ❌ 该行代码会编译失败,因为c和d的初始化表达式类型不同

0x03 static 关键字初探

static 是全局变量的默认存储类,它指示编译器在程序的生命周期内保持局部变量的存在。

static int a;

而不会像 auto 那样,每次进入和离开作用域时都会进行创建和销毁。

因此,我们可以使用 static 修饰局部变量在 "延长" 局部变量的生命周期。

让它能在函数调用之间保持局部变量的值。

static 也可以作用于全局变量,当 static 修饰全局变量时会让作用于提升至声明它的文件内。

静态变量的初始化:静态变量只被初始化一次,及时函数调用多次该变量的值也不会重置。

* 0x04 register 关键字

寄存器变量通常被存储在内存中,如果运气不错,寄存器变量就可以被存储在 CPU 寄存器中。

 寄存器变量的访问速度比普通变量快的多。

register 关键字就是用来建议编译器把局部变量或函数的形参放入 CPU 的寄存器中的。

放到寄存器可以提高访问速度,如果一个变量比较常用,我们可以考虑加上 register 修饰:

register int count;

值得注意的是,register 对编译器只是一个 "建议",具体情况得看硬件和各种限制是否通过。

准确来说这是对编译器的一个请求,而不是一个命令,请求需要同意,命令无需同意。

这也是为什么在开头我说 "如果运气不错",用了 register 能不能放进去是要看天时地利人和的。

"天时地利人和,缺一不可。"

register 修饰的变量,编译器将尽可能地将其存入 CPU 的寄存器中。

并且,既然要存到寄存器中,那么 变量的字节长度应小于等于寄存器的长度。

局部变量存储在 RAM 中,而 register 可以建议编译器将某变量存到 CPU 寄存器中。

因此,如果一个变量需要频繁访问,可以使用 register 声明一下,提高程序的运行速度。

📌 注意事项:不能对 register 使用取地址 &,因为它没有内存位置。

0x05 extern 关键字

extern 关键字用于定义在其他文件中声明的全局变量或函数。

extern 让全局变量可以被各个对象模块访问。

使用 extern 关键字时,表示变量已经在别处定义,不能再初始化。

Ⅱ. 存储期(Storage Duration)

0x00 引入:存储器的概念

存储期描述了通过这些标识访问对象的生命周期,简单来说就是变量在内存中的 "存活期"。

 C 语言中有 4 种存储期,分别是:

我们下面将逐个介绍它们,对于初学者来说只需做一个简单的了解即可。

0x01 自动存储期

我们一般在函数中创建的变量 (没被 static 定义的变量),都具有 "自动存储期"。

int main(void)
{
    int a;      // 具有自动存储期
}

具有自动存储期的变量,仅存在于当前代码块内(大括号)。

如果不给自动存储期的变量初始化,会自动初始化一个不确定的随机值。

0x02 静态存储期

函数中用 static 关键字定义出来的变量,或在函数外声明定义的对象都具有 "静态存储期"。

int A;   // 具有静态存储期

int main(void)
{
    static int b;    // 具有静态存储期
}

具有静态存储期的变量,从程序开始结束该变量会一直存在,寿 (生命周期) 与天 (程序) 齐。

同时具备自动初始化为 0 的特性,即不给这个变量初始化,变量的初始值默认为 0。

* 0x03 动态分配存储期

C 语言中的动态存储期是指在程序运行时分配和释放内存的过程。

这种存储期允许程序在运行时根据需要来管理内存,以适应不同的数据结构和问题需求。

动态存储期主要通过 malloc 和 free 实现。

(该部分知识点将在动态内存分配章节补充讲解)

* 0x04 线程存储期

线程存储期 (Thread-Local Storage),它的生命周期是创建它的线程的整个执行过程。

比如并发中具有线程存储期的对象,在该线程开始执行时创建,在线程结束时销毁。

这意味着每个线程都拥有其自己的一组变量副本,这些副本在不同的线程中具有不同的值。

举个例子,我们使用线程存储期来存储线程特定的数据。

💬 代码演示:线程 ID 计数器

#include <stdio.h>
#include <pthread.h>

// 定义线程存储期变量
__thread int threadSpecificValue = 0;

// 线程执行的函数
void* threadFunction(void* arg) {
    // 每个线程可以独立地修改 threadSpecificValue 的值
    threadSpecificValue = threadSpecificValue + 1;
    printf("Thread ID: %ld, \
        threadSpecificValue: %d\n", \
        pthread_self(), \
        threadSpecificValue);
    return NULL;
}

int main() {
    pthread_t thread1, thread2;

    // 创建两个线程
    pthread_create(&thread1, NULL, threadFunction, NULL);
    pthread_create(&thread2, NULL, threadFunction, NULL);

    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    return 0;
}

💡 解读:我们定义了线程存储期变量 threadSpecificValue,每个线程都有其自己的副本。可以看到在 threadFunction 中,每个线程独立地对 threadSpecificValue 进行递增操作,并打印出线程的 ID 以及变量的值。因为每个线程都有自己的变量副本,所以每个线程的输出是独立的。(这里用的 __thread 是 GCC 编译器的扩展,用于声明线程存储期变量)

使用存储类说明符声明标识符的对象 _Thread_local(自C11开始)具有线程存储持续时间。它的生命周期是创建它的线程的整个执行过程,并且在线程启动时初始化它的存储值。每个线程都有一个独特的对象,并且在表达式中使用声明的名称是指与评估表达式的线程相关联的对象。尝试从与对象关联的线程以外的线程间接访问具有线程存储持续时间的对象的结果是实现定义的。

这边再多提一下,C++ 11 引入了 thread_local 关键字,用于声明线程局部变量。线程局部变量在每个线程中都有独立的实例,因此在不同线程中访问这些变量时不会相互干扰。

#include <stdio.h>
#include <threads.h>

thread_local int tls_var = 0;

int main() {
    thrd_t thread;
    thrd_create(&thread, another_thread, NULL);

    tls_var = 42;
    printf("Main thread TLS: %d\n", tls_var);

    thrd_join(thread, NULL);
    return 0;
}

int another_thread(void *arg) {
    tls_var = 99;
    printf("Another thread TLS: %d\n", tls_var);
    return 0;
}

 还可以使用 POSIX 线程局部存储函数,在 POSIX 线程库中,可以使用这些函数来创建和操作线程局部存储:

pthread_key_create()
pthread_setspecific()
pthread_getspecific()

线程局部存储允许每个线程维护一份独立的数据副本,这对于需要在线程之间保持独立状态的情况非常有用,例如线程特定的配置信息、日志句柄等操作。

Ⅲ. 静态变量(Static)

0x00 static 关键字

 我们可以用 static 关键字来修饰一个变量为静态变量,修饰方法如下:

static 数据类型 变量名;

如果我们想让一个变量为静态变量,我们就在其数据类型前加一个 static 关键字就行了。

静态变量可以重复赋值,默认初始化的值为 0。

静态变量会被分配在静态存储区,静态变量在数据段中,函数退出后变量值不变。

上一章中我们学习了全局变量和局部变量,静态变量也是分全局和局部的。

 分别是 静态局部变量静态全局变量,下面我们将逐个介绍。

0x01 局部静态变量

 静态局部变量的作用域和局部变量一致,在自己所处的代码块内有效。

但是生命周期被 "提升" 成全局的了,生命周期与全局变量一致,在整个程序运行期间有效。

0x02 全局静态变量

静态全局变量的作用域在定义它的源文件内。

它的生命周期和全局变量一致,在整个程序运行期间有效。

举个例子,如果我们在一个源文件中定义了静态全局变量,那么该变量只能在该源文件内使用。

0x03 静态变量的初始值默认为0

静态变量的初始值都是 0,静态局部变量和静态全局变量的初始值都是 0。

💬 代码演示:静态变量的初始值为 0

#include <stdio.h>

static A;

int main(void)
{
	static a;
	printf("静态局部变量 a = %d\n", a);
	printf("静态局部变量 A = %d\n", a);

	return 0;
}

🚩 运行结果如下:

📌 [ 笔者 ]   王亦优 | 雷向明
📃 [ 更新 ]   2023.8.27
❌ [ 勘误 ]   /* 暂无 */
📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,
              本人也很想知道这些错误,恳望读者批评指正!

📜 参考文献:

- C++reference[EB/OL]. []. http://www.cplusplus.com/reference/.

- Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

- 百度百科[EB/OL]. []. https://baike.baidu.com/.

- 维基百科[EB/OL]. []. https://zh.wikipedia.org/wiki/Wikipedia

- R. Neapolitan, Foundations of Algorithms (5th ed.), Jones & Bartlett, 2015.

- B. 比特科技. C/C++[EB/OL]. 2021[2021.8.31]

- 林锐博士. 《高质量C/C++编程指南》[M]. 1.0. 电子工业, 2001.7.24.

- 陈正冲. 《C语言深度解剖》[M]. 第三版. 北京航空航天大学出版社, 2019.

- 侯捷. 《STL源码剖析》[M]. 华中科技大学出版社, 2002.

- T. Cormen《算法导论》(第三版),麻省理工学院出版社,2009年。

- T. Roughgarden, Algorithms Illuminated, Part 1~3, Soundlikeyourself Publishing, 2018.

- J. Kleinberg&E. Tardos, Algorithm Design, Addison Wesley, 2005.

- R. Sedgewick&K. Wayne,《算法》(第四版),Addison-Wesley,2011

- S. Dasgupta,《算法》,McGraw-Hill教育出版社,2006。

- S. Baase&A. Van Gelder, Computer Algorithms: 设计与分析简介》,Addison Wesley,2000。

- E. Horowitz,《C语言中的数据结构基础》,计算机科学出版社,1993

- S. Skiena, The Algorithm Design Manual (2nd ed.), Springer, 2008.

- A. Aho, J. Hopcroft, and J. Ullman, Design and Analysis of Algorithms, Addison-Wesley, 1974.

- M. Weiss, Data Structure and Algorithm Analysis in C (2nd ed.), Pearson, 1997.

- A. Levitin, Introduction to the Design and Analysis of Algorithms, Addison Wesley, 2003. - A. Aho, J. 

- E. Horowitz, S. Sahni and S. Rajasekaran, Computer Algorithms/C++, Computer Science Press, 1997.

- R. Sedgewick, Algorithms in C: 第1-4部分(第三版),Addison-Wesley,1998

- R. Sedgewick,《C语言中的算法》。第5部分(第3版),Addison-Wesley,2002

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL—MySQL的NULL值是怎么存放的

一、引言 1、MySQL数据存放在哪个文件&#xff1f; 创建一个数据库会产生三种格式的文件&#xff0c;分别是.opt格式、.frm格式、.ibd格式。 opt格式&#xff1a;用来存储当前数据库的默认字符集和字符校验规则。 frm格式&#xff1a;该文件是用来保存每个表的元数据信息的&…

0基础入门C++之类和对象下篇

目录 1.再谈构造函数1.1构造函数赋值1.2初始化列表1.3explicit关键字 2.static成员2.1概念2.1静态成员变量2.2静态成员函数2.3特性 3.匿名对象4.友元函数4.1友元函数4.2友元类 5.内部类6.再次理解类和对象 1.再谈构造函数 首先我们先来回忆一下构造函数&#xff1a; 构造函数是…

【Java 高阶】一文精通 Spring MVC - 数据验证(七)

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

用AI + Milvus Cloud搭建着装搭配推荐系统

在上一篇文章中,我们学习了如何利用人工智能技术(例如开源 AI 向量数据库 Milvus Cloud 和 Hugging Face 模型)寻找与自己穿搭风格相似的明星。在这篇文章中,我们将进一步介绍如何通过对上篇文章中的项目代码稍作修改,获得更详细和准确的结果,文末附赠彩蛋。 注:试用此…

【头歌】构建哈夫曼树及编码

构建哈夫曼树及编码 第1关:构建哈夫曼树 任务描述 本关任务:构建哈夫曼树,从键盘读入字符个数n及这n个字符出现的频率即权值,构造带权路径最短的最优二叉树(哈夫曼树)。 相关知识 哈夫曼树的定义 设二叉树具有n个带权值的叶子结点{w1,w2,...,wn},从根结点到每个叶…

《Zookeeper》源码分析(二十三)之 客户端的命令处理过程

目录 客户端的命令处理过程1. ZooKeeper.create()2. ClientCnxn.submitRequest()3. SendThread.run()4. ClientCnxnSocket.doTransport()5. SendThread.readResponse() 客户端的命令处理过程 以创建节点命令为例&#xff0c;整个过程流程如下&#xff1a; CliCommand命令在抽…

uniapp国际化npm install vue-i18n报错

npm install vue-i18n //npmyarn add vue-i18n //yarn在vue2环境下&#xff0c;默认安装 npm install vue-i18n 的版本是 vue-i18n9.1.9&#xff0c;所以报错。 npm view vue-i18n versions --json 用以上命令查看版本&#xff1a; vue2建议5.0版本 npm install vue-i1…

Wireshark数据抓包分析之ARP协议

一、实验目的&#xff1a; 通过wireshark的数据抓包了解这个ARP协议的具体内容 二、预备知识: 1.Address Resolution Protocol协议&#xff0c;就是通过目标IP的值&#xff0c;获取到目标的mac地址的一个协议 2.ARP协议的详细工作过程&#xff0c;下面描述得非常清晰&#xff…

VScode使用SSH连接linux

1、官网下载和安装软件 https://code.visualstudio.com/Download 2、安装插件 单击左侧扩展选项&#xff0c;搜索插件安装 总共需要安装的插件如下所示 3、配置连接服务器的账号 安装完后会在左侧生成了远程连接的图标&#xff0c;单击此图标&#xff0c;然后选择设置图标…

[LitCTF 2023]Flag点击就送!

进入环境后是一个输入框&#xff0c;可以提交名字 然后就可以点击获取flag&#xff0c;结果回显提示&#xff0c;需要获取管理员 可以尝试将名字改为admin 触发报错&#xff0c;说明可能存在其他的验证是否为管理员的方式 通过抓包后&#xff0c;在cookie字段发现了 特殊的东西…

数据隐私与安全在大数据时代的挑战与应对

文章目录 数据隐私的挑战数据安全的挑战应对策略和方法1. 合规和监管2. 加密技术3. 匿名化和脱敏4. 安全意识培训5. 隐私保护技术 结论 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&…

spring框架:简介+依赖注入

目录 一、spring简介 二、创建项目 三、spring创建对象 四、SpringBean管理 1.注入实现-XML 2.注入实现-注解 一、spring简介 spring诞生与2003年&#xff0c;是一个轻量级的、IOC( Inversion Of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程)的jav…

【rust/egui】(六)看看template的app.rs:TextEdit

说在前面 rust新手&#xff0c;egui没啥找到啥教程&#xff0c;这里自己记录下学习过程环境&#xff1a;windows11 22H2rust版本&#xff1a;rustc 1.71.1egui版本&#xff1a;0.22.0eframe版本&#xff1a;0.22.0上一篇&#xff1a;这里 TextEdit 文本编辑框 其定义为&#…

有限与无限游戏 | 真北荐书

2023佛山敏捷之旅暨DevOps Meetup志愿者为进行大会的组织与准备&#xff0c;每周三晚有一个例会。 例会前等人的时间&#xff0c;涌现出一个小的分享环节。今天分享这本书&#xff1a;《有限与无限游戏》。 大家选择成为志愿者&#xff0c;是一个无限游戏。而组织活动和完成各种…

uni-app里使用webscoket

实现思路和vue中是一样的。如果想看思路可以看这篇文章&#xff1a;websocket 直接上可以运行的代码&#xff1a; 一、后端nodeJS代码&#xff1a; 1、新建项目文件夹 2、初始化项目&#xff1a; npm init -y 3、项目里安装ws npm i ws --save 4、nodeJS代码&#xff1…

STL list基本用法

目录 list的使用构造函数和赋值重载迭代器(最重要)容量相关插入删除元素操作reversesortuniqueremovesplice list的底层实际是双向链表结构 list的使用 构造函数和赋值重载 构造函数说明list()无参构造list (size_type n, const value_type& val value_type())构造的li…

Python学习笔记_进阶篇(一)_浅析tornado web框架

tornado简介 1、tornado概述 Tornado就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本。Tornado 和现在的主流 Web 服务器框架&#xff08;包括大多数 Python 的框架&#xff09;有着明显的区别&#xff1a;它是非阻塞式服务器&#xff0c;而且速度相当快。得利于…

Clock Domain Crossing(CDC)跨时钟域

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 ​跨时钟域(CDC)指的是信号由一个时钟域进入另一个时钟域,以下图为例。 ● F1属于clk1时钟域 ● Q1属于clk1时钟域的信号 ● F2属于clk2时钟域 ● Q2属于clk2时钟域的信号 ● Q1对于F2来说是…

〔017〕Stable Diffusion 之 常用模型推荐 篇

✨ 目录 &#x1f388; 模型网站&#x1f388; 仿真系列&#x1f388; 国风系列&#x1f388; 卡通动漫系列&#x1f388; 3D系列&#x1f388; 一些好用的lora模型 &#x1f388; 模型网站 由于现在大模型超级多&#xff0c;导致每种画风的模型太多&#xff0c;那么如何选择最…

数据结构(Java实现)-二叉树(下)

获取二叉树的高度 检测值为value的元素是否存在(前序遍历) 层序遍历 判断一棵树是不是完全二叉树 获取节点的路径 二叉树的最近公共祖先