C++基础与深度解析 | 数组 | vector | string

news2025/1/21 21:52:31

文章目录

    • 一、数组
      • 1.一维数组
      • 2.多维数组
    • 二、vector
    • 三、string

一、数组

1.一维数组

  在C++中,数组用于存储具有相同类型和特定大小的元素集合。数组在内存中是连续存储的,并且支持通过索引快速访问元素。

数组的声明

  数组的声明指定了元素的类型和数组的长度。

// 声明一个整型数组,具有10个元素
int arr[10];

// 声明并初始化整型数组
int arr[] = {1, 2, 3, 4, 5};

注意:不要使用extern 指针来声明数组,应该extern int array[];即Unknown Bounded Array 声明

错误示例:

`file1.cpp`:
int array[3] = {1, 2, 3};

`file2.cpp`://正确写法
extern int array[];

`file2.cpp`://错误写法
extern int* array;

数组初始化

  数组的初始化可以是隐式的,也可以是显式的。

  • 缺省初始化

    在C++中,如果数组是非静态的局部数组,其元素将包含未定义的值,因为它们没有默认初始化。全局数组和静态数组将会被默认初始化为零。

    void function() {
        int localArray[10];  // 未定义的值
        static int staticArray[10];  // 初始化为0
    }
    
  • 聚合初始化( aggregate initialization )

    //使用大括号{}
    int arr[] = {1, 2, 3, 4, 5}; //根据初始化列表推导出int[5]
    

注意事项

  • 不能使用auto来声明数组类型

    auto b = {1,2,3};	//b推导出的类型为std::initializer_list<int>
    
    int c[3] = {1,2,3};
    auto d = c;		//d推导出的类型为int*,类型退化了
    
  • C++中,数组(内置数组)不能直接复制。因为它们没有像类或结构体那样的拷贝构造函数或赋值运算符

    • 赋值操作:对于数组,你不能使用简单的赋值操作(=)来复制数组。例如,以下代码是错误的:

      int arr1[10] = {1, 2, 3};
      int arr2[10];
      arr2 = arr1; // 错误:数组之间不能使用赋值操作符
      
    • 拷贝构造函数:传统数组没有拷贝构造函数,这意味着你不能通过构造函数来创建一个数组的深拷贝

    尽管传统数组不能直接通过赋值或拷贝构造函数来复制,你仍然可以通过其他方法来复制数组的内容。

    • 手动复制:使用循环手动逐个复制数组的每个元素

    • std::copy:使用C++标准库中的 <algorithm> 头文件提供的 std::copy 函数

    • 拷贝构造函数(对于数组类型的对象):如果你有一个包含数组的类或结构体,你可以定义一个拷贝构造函数来复制数组。

      struct MyStruct {
          int arr[10];
          MyStruct(const MyStruct& other) {
              std::copy(other.arr, other.arr + 10, arr);
          }
      };
      
    • C++11标准库容器:如 std::arraystd::vector 提供了拷贝构造函数和赋值运算符来复制其内容。

      std::array<int, 10> arr1 = {{1, 2, 3}};
      std::array<int, 10> arr2 = arr1; // 使用std::array的拷贝构造函数
          
      std::vector<int> vec1 = {1, 2, 3};
      std::vector<int> vec2 = vec1; // 使用std::vector的拷贝构造函数
      
  • 元素个数必须是常量表达式(编译期可计算的值)

  • 字符串数组的特殊性

    char str[] = "Hello";	//char[6]
    char str[] = {'H', 'e', 'l', 'l', 'o'}; //char[5]
    char str[] = {'H', 'e', 'l', 'l', 'o', '\0'}; //char[6]
    

    使用字符串字面量初始化字符数组

    char str[] = "Hello"; // char[6]
    

    在这行代码中,"Hello" 是一个字符串字面量,它包含了字符串 "Hello" 以及一个隐式的空字符 \0,用作字符串的终止符。因此,当你使用这个字符串字面量来初始化字符数组 str 时,数组的大小是6个字符,包括5个可见字符和一个空字符。

    使用字符字面量初始化字符数组

    char str[] = {'H', 'e', 'l', 'l', 'o'}; // char[5]
    

    在这个例子中,使用花括号 {} 包围的字符字面量列表来初始化字符数组 str。这种方式不会自动添加空字符 \0,因此初始化后的数组 str 将包含5个字符,即 'H', 'e', 'l', 'l', 和 'o'。数组的实际大小是5个字符。

    注意事项

    • 当使用字符串字面量初始化字符数组时,编译器会自动在末尾添加一个空字符 \0,所以数组需要有足够的空间来存储这个额外的字符。
    • 当使用字符字面量列表初始化字符数组时,需要确保数组有足够的空间来存储所有字符,且不会自动添加空字符 \0。如果你需要一个以空字符终止的字符串,必须手动添加 \0
    • 字符串字面量和字符字面量列表在初始化字符数组时的行为不同,这可能会影响程序中字符串的处理和字符串函数的使用。

数组(数组到指针的隐式转换)

  数组名在大多数表达式中会被解释为指向数组首元素的指针。使用数组对象时,通常情况下会产生数组到指针的隐式转换,隐式转换会丢失一部分类型信息,可以通过声明引用来避免隐式转换

int arr[10];
int* ptr = arr;  // ptr是指向数组首元素的指针,数组到指针的隐式转换

auto& b = arr;	//声明引用来避免隐式转换

由于数组名可以作为指针使用,所以可以进行指针运算。

#include <iostream>

int main()
{
    int arr[10];
    for (int* ptr = arr; ptr < arr + 10; ++ptr)  {
        // 操作数组元素
        *ptr = *ptr + 1;	//1
    }
}

指针数组与数组指针

  • 指针数组

    指针数组是一个包含多个指针的数组,每个指针可以指向不同的对象。

    语法:指针数组在声明时,指针符号 * 放在数组名的前面。

    int* arr[3];
    
  • 数组指针

    数组指针是指向数组的指针。这种指针指向整个数组,而不是数组中的单个元素。

    语法:数组指针在声明时,指针符号 * 放在数组类型后面,使用括号括起来

    // 创建一个整型数组
    int arr[] = {10, 20, 30};
    
    // 创建一个指向数组的指针
    int (*ptrToArr)[3] = &arr;
    

    ptrToArr 是一个指向数组的指针,它指向一个包含3个 int 的数组。ptrToArr 不是一个指向 int 的指针,而是指向整个数组。

注意

  • 当传递数组作为函数参数时,实际上传递的是指向数组首元素的指针,而不是整个数组。
  • 数组的生命周期和存储空间必须在数组指针使用期间保持有效。
  • 指针数组和数组指针在内存布局和访问方式上有所不同,需要根据具体需求选择合适的类型。

声明数组的引用:(C++中没有引用数组的概念)

  声明数组的引用意味着创建一个引用,它指向整个数组。数组引用在函数参数传递、大型数据结构的传递等方面非常有用,因为它们允许函数直接操作传入的数组,而不是数组的副本。

int (&arr)[5] = data; // 假设 'data' 是一个已定义的数组

数组作为函数参数时,由于数组不能直接拷贝,通常会退化为指针。为了避免这种情况,可以使用数组引用作为函数参数

void printArray(int (&arr)[5]) {
    for (int i = 0; i < 5; ++i) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

int data[5] = {1, 2, 3, 4, 5};
printArray(data);

printArray 函数接受一个数组的引用作为参数。这意味着函数可以直接访问和修改数组 data 中的元素。如果想限制数组中的元素不能修改,则void printArray(const int (&arr)[5])

注意

  • 数组引用必须在声明时被初始化。
  • 数组引用的大小(数组的长度)必须是已知的,因此它们不能指向未指定大小的数组。
  • 数组引用通常用于函数参数,以便安全地传递数组并避免数组退化为指针

数组的元素访问

  • 数组对象是一个左值(如果作为右值使用,类型会发生隐式转换:int[3] --> int*

    C++中左值与右值

    在C++中,表达式可以根据它们是否可以取得内存地址被分为两类:l-value(左值)和r-value(右值)。

    • l-value:l-value是指具有内存地址的表达式,这个内存地址可以被读取和写入。l-value通常用在赋值表达式的左边

      l-value的例子:

      • 变量:int a;
      • 数组元素:int arr[10]; arr[0];
      • 函数参数:当函数参数是一个引用类型时,它是一个l-value。
      • 位域:当位域是一个对象成员时。
    • r-value:r-value是指那些没有内存地址或者有临时内存地址的表达式,它们不能被赋值,因为它们的存在时间很短暂。r-value通常用在赋值表达式的右边,比如字面量、临时对象、表达式的结果等。

      以下是一些r-value的例子:

      • 字面量:10
      • 表达式:a + b
      • 函数返回值:除非函数返回一个引用,否则返回的是一个r-value。
      int b = 20; // b是一个l-value
      int c = a + b; // a + b是一个r-value
      
  • 数组访问

    使用时数组名通常会转换成相应的指针类型(指向数组首元素的指针),本质上:arr[index] = *(arr + index),实际上是利用指针运算来访问数组元素

    int arr[5] = {1, 2, 3, 4, 5};
    int firstElement = arr[0];  // 获取第一个元素,值为1
    int thirdElement = arr[2]; // 获取第三个元素,值为3
    
  • 范围检查

    C++不提供自动的数组范围检查,所以访问数组元素时应该确保索引不会超出数组的界限。

    int arr[5];
    for (int i = 0; i < 5; ++i) {
        // 安全的访问 arr[i]
    }
    
  • 越界访问

    如果尝试访问超出数组实际大小的索引,将导致未定义行为,这可能引发程序错误,如数组越界错误、野指针引用等

获得指向数组开头与结尾的指针 :

int a[3] = {1,2,3};

获得指向数组开头的指针:a     &(a[0])  std::begin(a)  std::cbegin(a)
获得指向数组结尾的下一个元素的指针:a+3   &(a[3])  std::end(a)    std::cend(a)

std::begin(a)与std::end(a)获得的类型为int*
std::cbegin(a)与std::cend(a)获得的类型为const int*

指针算术运算

  • 增加、减少

    指向数组下一位置元素的指针

  • 比较

    如果参数比较运算的指针是指向一个数组中的两个位置 ,是可以进行比较运算的;如果指向的是不同数组,不建议这样做,可能产生未定义的行为

  • 求距离

    #include <iostream>
    
    int main()
    {
        int a[3] = {1, 2, 3};
        auto ptr = a;
        auto ptr2 = a +3;
        std::cout << ptr2 - ptr << std::endl;	//3
        std::cout << ptr - ptr2 << std::endl;	//-3
    }
    
  • 解引用

  • 指针索引

    指针索引使用方括号 [] 来指定偏移量,从而访问指针指向的内存位置之后的某个位置。

数组的其他操作

  • 求元素个数

    以下三种方法都是对数组进行操作(获取数组的定义),而不是指针

    • sizeof方法: C语言方式,有危险,不推荐
    • std::size方法(C++17及以后):推荐的方式
    • std::end() - std::begin()方法:在运行期进行指针运算,但其实数组信息在编译器就可以看到,增加了运行期负担,不推荐
    #include <iostream>
    
    int main()
    {
        int a[3];
        
        std::cout << sizeof(int) << std::endl;	//4
        std::cout << sizeof(a) << std::endl;	//12
        //sizeof()方法求元素个数
        std::cout << sizeof(a)/sizeof(int) << std::endl; //3
    
        //std::size()   C++17
        // std::cout << std::size(a) << std::endl;
        //end-begin
        std::cout << std::end(a) - std::begin(a) << std::endl;    //3
    }
    
  • 元素遍历

    • 基于元素个数
    • 基于©begin/©end
    • 基于range-based for循环:语法糖
    #include <iostream>
    
    int main()
    {
        int a[3] = {2, 3, 5};
        
        //基于元素个数--C++17
        size_t index = 0;
        while(index < std::size(a))
        {
            std::cout << a[index] << std::endl;
            index = index + 1;
        }
        
        //基于(c)begin/(c)end
        auto ptr = std::cbegin(a);
        while(ptr != std::cend(a))
        {
            std::cout << *ptr << std::endl;
            ptr = ptr + 1;
        }
        
        //基于`range-based ` for循环
        for (int x : a)
        {
            std::cout << x << std::endl;
        }
    }
    

数组–C字符串

  • C 字符串本质上是字符数组
  • C 语言提供了额外的函数来支持 C 字符串相关的操作 : strlen, strcmp…,这些操作都需要’\0’,知道C字符串到哪里结束
    • strlen():统计长度直到找到’\0’

2.多维数组

  多维数组本质上是数组的数组。其内存布局仍是连续存储。

int x[3][4];  //x是一个数组,包含3个元素,x的每个元素是一个int[4]数组。
x[0];	//类型是int(&)[4]

声明多维数组

  多维数组的声明涉及指定每个维度的大小。在C++中,通常使用方括号来声明多维数组。

// 声明一个3x3的二维整型数组
int multiArray[3][3];

// 声明一个5x10的二维整型数组
int anotherArray[5][10];

初始化多维数组

  初始化时,每个维度的元素应该用花括号 {} 包围,维度之间的元素应该用逗号 , 分隔。

  • 缺省初始化

  • 聚合初始化

    一层大括号与多层大括号

//其余元素默认初始化为0
int x2[3][4] = {1, 2, 3, 4, 5, 6};

//其余元素默认初始化为0
int x3[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};

//其余元素默认初始化为0
int x3[3][4] = {{1, 2, 3, 4}, {5, 6, 7}};

// 初始化一个3x3的二维数组
int multiArray[3][3] = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};


// 使用缺省初始化
int defaultArray[3][3] = {};

多维数组只能省略最高位的维度如:int x[][3],但是不建议这么干

多维数组索引与遍历

  • 使用多个中括号来索引,访问多维数组元素

    int value = multiArray[1][2]; // 获取第二行第三列的元素,值为6
    
  • 使用多重循环来遍历多维数组

    #include <iostream>
    
    int main()
    {
        int multiArray[3][3] = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
    
        //基于`range-based` for循环
        for (auto& p : multiArray)   //auto& 非常重要
        {
            for (auto q : p)
            {        
                std::cout << q << std::endl;
            }
        }
    }
    

    下面分析for (auto& p : multiArray)for (auto p : multiArray)的区别

    image-20240505002541618

    image-20240505002635403

指针与多维数组

  在C++中,多维数组的数组名可以被看作一个指向数组首元素的指针。可以使用指针运算来操作多维数组。

int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*ptrToRow)[3] = arr; // ptrToRow是一个指向具有3个整数的数组的指针

// 访问第一行的第二个元素
int value = (*ptrToRow)[1]; // 等于 arr[0][1],值为2
  • 多维数组可以隐式转换为指针,但只有最高维会进行转换,其它维度的信息会被保留。

    image-20240505003740863

  • 使用类型别名来简化多维数组指针的声明

    #include <iostream>
    
    using A = int[4];
    int main()
    {
        int x[3][4];
        A* ptr = x;
    }
    
  • 使用指针来遍历多维数组

    #include <iostream>
    
    
    int main()
    {
        int x2[3][4] = {};
        auto ptr = std::begin(x2);
        while(ptr != std::end(x2))
        {
            auto ptr2 = std::begin(*ptr);
            while(ptr2 != std::end(*ptr))
            {
                ptr2 = ptr2 + 1;
            }
            ptr = ptr + 1;
        }
    }
     
    

二、vector

  C++标准库中的 std::vector 是一个序列容器,是 C++ 标准库中定义的一个类模板。与内建数组相比,std::vector 提供了更多的功能,例如自动调整大小、范围检查和一些有用的成员函数(更侧重易用性)。std::vector 可复制、可在运行期动态改变元素个数(内建数组不可以)。

构造与初始化

  • 缺省初始化

  • 聚合初始化

  • 其他初始化方式–构造函数

    std::vector<T,Allocator>::vector,根据参数调用不同的构造函数

std::vector<int> vec; // 默认构造
std::vector<int> vec(10, 1); // 10个整数初始化为1
std::vector<int> vec = {1, 2, 3}; // 列表初始化

其他方法

  • 获取元素个数、判断元素是否为空、容量

    #include <iostream>
    #include <vector>
    
    int main()
    {
        std::vector<int> x(3, 1);
        //获取元素个数--3
        std::cout << x.size() <<std::endl;
        //判断元素是否为空,空返回1,不空返回0
        std::cout << x.empty() <<std::endl;
        //容量--3
        std::cout << x.capacity() <<std::endl;
    
    }
    
  • 插入元素

    vec.push_back(4); // 在末尾添加一个元素
    vec.push_back({5, 6, 7}); // 从初始列表添加多个元素
    
  • 删除元素

    vec.pop_back(); // 删除最后一个元素
    vec.erase(vec.begin() + 1); // 删除第二个元素
    
  • vector比较

    逐个比较两个vector的元素,如果元素相同,则比较下一个元素;如果第一个元素不同,则由第一个元素的大小决定;

  • 清空

    vec.clear(); // 删除所有元素,保留容量
    
  • 修改容器大小

    vec.resize(5); // 改变vec的大小为5,如果缩小则可能删除元素
    vec.resize(7, 1); // 如果需要,用1填充额外的空间以改变大小为7
    
  • 元素操作

    vec.front(); // 返回第一个元素
    vec.back(); // 返回最后一个元素
    

vector 中元素的索引与遍历

  • []at实现vector 中元素的索引

    std::vector<int> vec(10, 1); // 10个整数初始化为1
    int firstElement = vec[0]; // 获取第一个元素
    vec[1] = 20; // 设置第二个元素的值
    
    //at方法
    vec.at(2) = 30;
    
  • ©begin / ©end 函数 V.S. ©begin / ©end 方法

    #include <iostream>
    #include <vector>
    
    
    int main()
    {
        std::vector<int> x1 = {1, 2, 3};
        //begin、end函数
        auto p = std::begin(x1);
        while(p != std::end(x1))
        {
            std::cout << *p << std::endl;
            p = p + 1;
        }
    
        //begin、end方法
        auto q = x1.begin();    //返回指向第一个元素的迭代器,迭代器解引用也可以获取元素的值
        while(q != x1.end())
        {
            std::cout << *q << std::endl;
            q = q + 1;
        }
    
        //for
        for (auto x : x1)
        {
            std::cout << x << std::endl;
        }
    }
    

    vector中©begin / ©end 方法返回的是随机访问迭代器

    迭代器

    • 模拟指针行为

    • 包含多种类别,每种类别支持的操作不同

    • vector 对应随机访问迭代器

      • 解引用与下标访问

      • 移动

      • 两个迭代器相减求距离

      • 两个迭代器比较

        这两个迭代器必须指向相同的vector

vector相关的其他内容

  • 添加元素可能使迭代器失效

    image-20240505131607321

  • 多维 vector

    如:std::vector<std::vector<int>>

  • .-> 操作符

    #include <iostream>
    #include <vector>
    
    
    int main()
    {
        std::vector<int>  x = {1, 2, 3};
        std::vector<int>* ptr = &x;
    
        std::cout << x.size() << std::endl;		//3
        std::cout << ptr->size() << std::endl;  //3
    }
    
  • vector 内部定义的类型

    • size_type

      image-20240505132432117

    • iterator / const_iterator

      image-20240505132511431

注意事项

  • std::vector 会根据需要自动调整大小,但可能会引起性能开销,因为它可能需要重新分配内存和复制元素。
  • 访问 std::vector 中的元素时,要注意范围检查,避免越界访问。
  • 与原始数组相比,std::vector 更安全且易于使用,但可能会占用更多的内存,并且访问速度可能稍慢。

三、string

  std::string 是标准库中的一个类模板实例化,它提供了一个可变长度的字符序列,用于内建字符串的代替品。与内建字符串相比,更侧重于易用性。string可复制、可在运行期动态改变字符个数

TypeDefinition
std::stringstd::basic_string

参考:std::basic_string

构造与初始化

#include <string>

std::string str; // 默认构造,创建一个空字符串
std::string str("Hello, World!"); // 直接初始化
std::string str(10, 'A'); // 初始化为10个字符'A'

string其他方法

  • 访问元素

    char ch = str[0]; // 获取第一个字符,与C风格字符串不同,这是安全的
    
  • 添加和修改字符串

    str += "追加的字符串"; // 追加字符串
    str.append("追加的字符串"); // 另一种追加方式
    str.insert(1, "插入的字符串"); // 在指定位置插入字符串
    str.replace(1, 2, "新字符串"); // 替换部分字符串
    str.erase(1, 4); // 删除从索引1开始的4个字符
    str.back() = '!'; // 修改最后一个字符
    
  • 尺寸相关方法(size/length)

    #include <iostream>
    #include <vector>
    
    
    int main()
    {
        std::string str= "Hello World!";
    
        size_t size = str.size();
        size_t len = str.length(); // 获取字符串长度
    
        std::cout << size << std::endl;
        std::cout << len << std::endl;
        str.resize(10); // 改变字符串长度为10
        std::cout << str.size() << std::endl;
        std::cout << str.length() << std::endl;
    }
    
  • 查找和比较

    size_t pos = str.find("sub"); // 查找子串"sub"的位置
    bool isEqual = str.compare("anotherString"); // 比较字符串
    
  • 子字符串

    std::string sub = str.substr(1, 4); // 获取从索引1开始的4个字符的子字符串
    
  • 清空

    str.clear(); // 删除所有字符,保留容量
    
  • 交换

    str.swap(anotherStr); // 交换两个字符串的内容
    
  • 转换为C字符串,返回一个指向’\0’结束字符数组的指针

    const char* cstr = str.c_str(); // 获取C风格的字符数组
    

注意事项

  • std::string 是一个类模板的实例化,所以它不是像原始数组那样的低级类型。
  • 字符串字面量(如 "Hello")是 const char 类型的数组,它们在C++中被定义为 const,因此不能被修改。
  • 当需要C风格的字符串时,可以使用 c_str() 成员函数来获取,但要注意,这个操作可能涉及内存分配,因此不应该频繁调用。
  • std::string 提供了范围检查的访问方式,避免了数组越界的问题

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

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

相关文章

virtualBox不能创建虚拟文件夹

问题如下图&#xff0c;在点击下一步时提示不能创建虚拟机文件夹 问题原因是使用了virtualBox的安装目录&#xff0c;在全局设定中设置虚拟电脑位置&#xff0c;不再使用virtualBox的安装目录 再次点击新建&#xff0c;就可以创建了。

无线网卡网络老断网

无线网卡网络老断网 设置 Intel AX210 无线网卡 路由器华为 AX3 问题及解决 问题 无线网卡连接到 wifi &#xff0c;连接不通&#xff0c;或者连接上后网络很慢&#xff0c;延时大&#xff0c;掉包。 解决方案 调整如下界面&#xff0c;调整信道后&#xff0c;连接正常。…

亚马逊卖家,如何打造爆款,如何提高产品权重、曝光、流量?

新老卖家们要知道&#xff0c;亚马逊A9算法影响产品排名的关键因素&#xff1a;产品相关性、销售排名、产品价格、点击率、转化率、产品图片、买家评论、买家满意度、QA的答复情况、搜索结果页详细信息级别。亚马逊A9算法&#xff0c;是根据卖家提供的listing文案信息进行收录、…

常见加解密算法02 - RC4算法分析

RC4是一种广泛使用的流密码&#xff0c;它以其简洁和速度而闻名。区别于块密码&#xff0c;流密码特点在于按位或按字节来进行加密。 RC4由Ron Rivest在1987年设计&#xff0c;尽管它的命名看起来是第四版&#xff0c;实际上它是第一个对外发布的版本。 RC4算法的实施过程简洁…

动态el-form表单以及动态禁用

当右侧下拉框选中为 长期有效,那么左侧输入框为禁用状态; <el-form-item label"证明有效期" class"is-required"><div v-for"(item,index) in form.arrayDat" :key"index" style"width: 100%;display: flex;justify-co…

深度解读《深度探索C++对象模型》之虚继承的实现分析和效率评测(二)

目录 通过子类的指针存取虚基类成员的实现分析 通过第一基类的指针存取虚基类成员的实现分析 通过第二基类的指针存取虚基类成员的实现分析 通过虚基类的指针存取虚基类成员的实现分析 小结 存取虚基类成员与普通类成员的效率对比 接下来我将持续更新“深度解读《深度探索…

WS2812B-2020 智能控制LED集成光源芯片IC

一般说明 WS2812B-2020是一款智能控制LED光源&#xff0c;它的外部采用了最新的模压封装技术&#xff0c;控制电路和RGB芯片集成在一个2020组件中。其内部包括智能数字端口数据锁存器和信号整形放大驱动电路。还包括一个精密的内部振荡器和一个电压可编程恒流控制部分&…

对关系型数据库管理系统的介绍

1.数据库的相关介绍 关系型数据库管理系统&#xff1a;&#xff08;英文简称&#xff1a;RDBMS&#xff09; 为我们提供了一种存储数据的特定格式&#xff0c;所谓的数据格式就是表&#xff0c; 在数据库中一张表就称为是一种关系. 在关系型数据库中表由两部分组成&#xf…

嵌入式和单片机的区别在哪?

嵌入式和单片机是两个不同的概念&#xff0c;它们在很多方面都存在着差异。嵌入式系统是一种专用的计算机系统&#xff0c;通常用于控制和监测其他设备。它通常由微处理器、存储器、输入/输出接口和其他外围设备组成。嵌入式系统可以运行各种操作系统&#xff0c;如 Linux、Win…

TCP/UDP通信中的部分函数

UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;和TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是互联网协议套件中最常用的两种传输层协议&#xff0c;它们负责在互联网中端到端地传输数据。尽管它们服务…

web网页录音(recorder.js)并上传后端语音转文字(Vosk)

我是一个后端开发人员&#xff0c;现在都快进化成全栈了。操了&#xff0c;是谁有好的项目让我跳跳槽&#xff0c;转转行吧 写在前面&#xff0c;很重要 这是官方文档的说明 翻译如下&#xff1a; 我们有两种型号-大型号和小型号&#xff0c;小型号非常适合在移动应用程序上执…

IT行业现状与未来趋势分析

IT行业现状与未来趋势显示出持续的活力和变革&#xff0c;以下是上大学网&#xff08;www.sdaxue.com&#xff09;关于IT行业现状与未来趋势分析&#xff0c;供大家参考。 当前现状&#xff1a; 市场需求持续增长&#xff1a;随着信息时代的深入发展&#xff0c;各行各业对信息…

一条查询SQL的执行过程

1.1 假设 查询语句为&#xff1a;mysql> select * from T where ID 10 1.2 总体执行流程 1.2.1 连接器 -> 连接 作用&#xff1a;负责跟客户端建立连接、获取权限、维持和管理连接等工作流程&#xff1a; 一个用户成功建立连接后&#xff0c;如果客户端太长时间没有请…

跨ROS系统通信:使用TCP实现节点间的直连

当涉及到在机器人操作系统&#xff08;ROS&#xff09;环境中的通信时&#xff0c;标准做法通常是在同一个ROS网络内通过话题和服务进行。但在某些特定情况下&#xff0c;比如当你有两个分布在不同网络中的ROS系统时&#xff0c;标准的通信方法可能不太适用。此时&#xff0c;一…

SpringBoot集成Seata分布式事务OpenFeign远程调用

Docker Desktop 安装Seata Server seata 本质上是一个服务&#xff0c;用docker安装更方便&#xff0c;配置默认&#xff1a;file docker run -d --name seata-server -p 8091:8091 -p 7091:7091 seataio/seata-server:2.0.0与SpringBoot集成 表结构 项目目录 dynamic和dyna…

EmotiVoice 实时语音合成TTS;api接口远程调用

参考:https://github.com/netease-youdao/EmotiVoice 测试整体速度可以 docker安装: 运行容器:默认运行了两个服务,8501 一个streamlit页面,另外8000是一个api接口服务 docker run -dp 8501:8501 -p 8250:8000 syq163/emoti-voice:latest##gpu运行 (gpu运行遇到CUDA er…

山东齐鲁文化名人颜廷利:朱郭有文才,曲高‘菏’寡星光路

山东齐鲁文化名人颜廷利教授表示&#xff0c;朱郭&#xff08;谐音‘祖国’&#xff09;有文才&#xff0c;《曲高‘菏’寡》星光路… 山东菏泽歌手朱之文在2011年凭借一首《滚滚长江东逝水》一夜成名&#xff0c; 十多年之后的今天&#xff0c;菏泽市网络红人郭有才靠一首《诺…

LeetCode---循环队列

循环队列就是只有固定的内存&#xff0c;存数据&#xff0c;出数据&#xff0c;但是也和队列一样&#xff0c;先进先出。如下图所示&#xff0c;这是他的样子 在head出&#xff0c;tail进&#xff0c;但是这个如果用数组解决的话&#xff0c;就有问题&#xff0c;力扣给我们的接…

Django模型进阶-多对多关系

在Django中&#xff0c;多对多&#xff08;Many-to-Many&#xff09;关系是一种数据库关系&#xff0c;表示一个模型的实例可以与另一个模型的多个实例相关联&#xff0c;同时另一个模型的实例也可以与这个模型的多个实例相关联。换句话说&#xff0c;就是两个模型之间可以存在…

免费SSL证书获取与部署教程

在互联网时代&#xff0c;HTTPS已成为网站安全的基石&#xff0c;为用户数据传输提供加密保障。免费SSL证书的出现降低了部署HTTPS的门槛&#xff0c;尤其对于个人网站、小微企业及测试环境而言&#xff0c;它们是理想的选择。本文旨在提供一份详尽指南&#xff0c;帮助您轻松获…