C语言和C++的类型转换

news2025/1/16 3:39:07

文章目录

  • 一、算术转换
    • 整形提升
    • 算术转换
  • 二、隐式类型转换
  • 三、显式类型转换
    • C语言旧式的类型转换
      • 1. C语言隐式类型转换
      • 2. C语言显式的类型强转
    • C++引入的四种显式类型转换
      • 1. static_cast
        • 基本数据类型之间的转换
        • 类指针之间的转换:
        • 枚举类型之间的转换
        • C 语言风格的强制类型转换和 `static_cast` 之间的差异
      • 2. const_cast
      • 3. reinterpret_cast
        • 用reinterpret_cast强转测试平台大小端
      • 4. dynamic_cast
    • 应该避免使用强制类型转换
  • 四、RTTI - 运行时类型识别
    • `typeid` 运算符:
    • `dynamic_cast`运算符
    • `decltype`关键字

一、算术转换

算术转换(arithmetic conversion)的含义是把一种算术类型转换成另外一种算术类型。算术转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换成最宽的类型。例如,如果一个运算对象的类型是 long double ,那么不论另外一个运算对象的类型是什么都会转换成 long double。 还有一种更普遍的情况,当表达式中既有浮点类型也有整数类型时,整数值将转换成相应的浮点类型

可以参考这篇文章:【C语言】深入理解C语言中的整型提升和算术转换


整形提升

整型提升是将较小的整数类型转换为较大的整数类型的过程。通常发生在以下情况:

  • 当操作数的类型比int类型更小(例如:char, short)时,会自动提升为int类型。
  • 当操作数的类型是无符号的(例如:unsigned char, unsigned short),并且可以在不丢失任何信息的情况下转换为int类型,则提升为int类型;否则,提升为unsigned int类型。

整型提升的主要目的是确保数据在计算过程中能够正确处理,避免因较小类型溢出而导致的错误。


算术转换

请添加图片描述

请添加图片描述


二、隐式类型转换

在下面这些情况下,编译器会自动地转换运算对象的类型:

  • 在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型。
  • 在条件中,非布尔值转换成布尔类型。
  • 初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型。
  • 如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型。
  • 函数调用时也会发生类型转换。

三、显式类型转换

C语言旧式的类型转换

对于C/C++,类型转换发生在下面三种情况下:

  1. 赋值运算符左右两侧类型不同
  2. 形参与实参类型不匹配
  3. 返回值类型与接收返回值类型不一致时

1. C语言隐式类型转换

#include <stdio.h>

int main()
{
	int i = 1;
	// 隐式类型转换
	double d = i;
	printf("%d, %.2f\n", i, d);
}

输出结果:
1, 1.00

2. C语言显式的类型强转

#include <stdio.h>

int main()
{
	int* p = &i;
	// 显式的强制类型转换
	int address = (int)p;
	printf("%p, %d\n", p, address);
	return 0;
}

输出结果:
00000046857EF584, -2055277180

C语言类型强转的缺陷:

  • 转换的可视性比较差
  • 所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。


C++引入的四种显式类型转换

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_castreinterpret_castconst_castdynamic_cast

转换的形式:

castname<type>(expression);

其中:

  • type是转换的目标类型而expression是要转换的值,如果type是引用类型,则结果是左值。
  • castname是static_castreinterpret_castconst_castdynamic_cast四种。

1. static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用
static_cast,它适用于基本数据类型之间的转换或者具有继承关系的类之间的转换,但它不能用于两个不相关的类型进行转换。

基本数据类型之间的转换
#include <iostream>

int main() 
{
    double doubleValue = 3.14;
    int intValue = static_cast<int>(doubleValue);

    std::cout << "Double value: " << doubleValue << std::endl;
    std::cout << "Converted to int: " << intValue << std::endl;

    return 0;
}

在这个例子中,static_cast被用于将double类型转换为int类型。这种转换会截断小数部分,只保留整数部分。

类指针之间的转换:
#include <iostream>

class Base {
public:
    virtual void print() {
        std::cout << "Base class" << std::endl;
    }
};

class Derived : public Base {
public:
    void print() override {
        std::cout << "Derived class" << std::endl;
    }
};

int main() {
    Derived derivedObj;
    Base* basePtr = &derivedObj;

    // 使用 static_cast 进行基类指针到派生类指针的转换
    Derived* derivedPtr = static_cast<Derived*>(basePtr);

    derivedPtr->print();  // 输出 "Derived class"

    return 0;
}

在这个例子中,static_cast被用于将基类指针转换为派生类指针。这种转换只在确保基类指针指向的实际对象是派生类对象的情况下才是安全的。

枚举类型之间的转换
#include <iostream>

enum class Color { RED, GREEN, BLUE };

int main() {
    int blueValue = static_cast<int>(Color::BLUE);

    std::cout << "Integer value for BLUE: " << blueValue << std::endl;

    return 0;
}

在这个例子中,static_cast被用于将枚举类型Color的成员转换为整数类型。这可以用于在需要整数值的情况下使用枚举。

C 语言风格的强制类型转换和 static_cast 之间的差异
int main()
{
	int a = 123;
	int* int_ptr = &a;
	char* char_ptr1 = int_ptr; // 编译错误
	char* char_ptr2 = (char*)int_ptr; // 没有编译错误,语法允许
	char* char_ptr3 = static_cast<char*> (int_ptr); // 编译错误:类型转换无效
}
  • char* char_ptr1 = int_ptr; 使用的是 C 语言风格的强制类型转换,它将 int* 类型的指针直接赋值给 char* 类型的指针,这在语法上是允许的,但它存在潜在的问题。这样的强制类型转换可能会导致未定义行为,因为在将整数指针转换为字符指针时,可能会造成指针间隔不一致,访问越界等问题。

  • char* char_ptr2 = (char*)int_ptr; 中使用的是 C 语言风格的强制类型转换,这是合法的,但仍然存在潜在的问题。这种转换通常被视为一种“原始”转换,可能会导致指针的类型不匹配,从而引发运行时错误。

  • char* char_ptr3 = static_cast<char*>(int_ptr); 使用了 static_cast 进行类型转换,这种转换是相对更为安全的,但在这个特定的情况下它会产生编译错误。static_cast 对于不同类型的指针之间的转换会进行更为严格的检查,确保类型之间有合理的关系,而在这个例子中,由于 int*char* 之间的关系并非直接的继承关系,因此编译器会报错。


2. const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值。

  • 如果指针或引用指向或绑定的对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。
  • 然而如果该对象是一个常量,再使用 const_cast 执行写操作,虽然不会编译报错,但是会产生未定义的后果。
#include <iostream>

int main() 
{
    int nonConstValue = 42;
    const int* constPtr = &nonConstValue;

    // 使用 const_cast 删除 const 属性
    int* nonConstPtr = const_cast<int*>(constPtr);

    // 修改变量的值
    *nonConstPtr = 55;

    // 输出修改后的值
    std::cout << "Modified value: " << nonConstValue << std::endl;

    return 0;
}

在修改之前,你需要确保指针所指的对象或引用被绑定的对象本身是可修改的!

使用const_cast的场景一般不能使用static_cast:

    int* nonConstPtr1 = const_cast<int*>(constPtr);
    //int* nonConstPtr2 = static_cast<int*>(constPtr); //编译错误:static_cast 无法丢掉常量或其他类型限定符

在使用 const_cast 进行类型转换时,尖括号中的类型必须符合以下要求:

  1. 转换方向匹配: 转换的方向应当是合法的。如果原始对象是 const 类型,那么 const_cast 的尖括号中的类型应当是非 const 类型。如果原始对象不是 const 类型,那么 const_cast 的尖括号中的类型应当是 const 类型。
    例如,如果有一个 const int* 类型的指针,你可以使用 const_cast<int*>(constIntPtr) 来将其转换为 int* 类型。

实际上不管怎么修改,都是符合语法的,不会报错:

int Value = 42;

// 四种指针
int* int_Ptr = &Value;
const int* const_int_Ptr = &Value;
int const* int_const_Ptr = &Value;
const int* const const_int_const_Ptr = &Value;

// 使用 const_cast 修改 const 属性

// 1. int* -> int*
int* Ptr1 = const_cast<int*>(int_Ptr);

// 2. int* -> const int*
const int* Ptr2 = const_cast<const int*>(int_Ptr);

// 3. const int* -> int*
int* Ptr3 = const_cast<int*>(const_int_Ptr);

// 4. const int* -> const int*
const int* Ptr4 = const_cast<const int*>(const_int_Ptr);

// 5. int const* -> int*
int* Ptr5 = const_cast<int*>(int_const_Ptr);

// 6. int const* -> const int*
const int* Ptr6 = const_cast<const int*>(int_const_Ptr);

// 7. const int* const -> int*
int* Ptr7 = const_cast<int*>(const_int_const_Ptr);

// 8. const int* const -> const int*
const int* Ptr8 = const_cast<const int*>(const_int_const_Ptr);

// 9. int* const -> int*
int* Ptr9 = const_cast<int*>(int_const_Ptr);

// 10. int* const -> const int*
const int* Ptr10 = const_cast<const int*>(int_const_Ptr);

// 11. const int* const -> int* const
int* const Ptr11 = const_cast<int* const>(const_int_const_Ptr);

// 12. const int* const -> const int* const
const int* const Ptr12 = const_cast<const int* const>(const_int_const_Ptr);

// 13. const int* const -> int const*
int const* Ptr13 = const_cast<int const*>(const_int_const_Ptr);

// 14. const int* const -> const int const*
const int const* Ptr14 = const_cast<const int const*>(const_int_const_Ptr);

// 15. int const* const -> int*
int* Ptr15 = const_cast<int*>(int_const_Ptr);

// 16. int const* const -> const int*
const int* Ptr16 = const_cast<const int*>(int_const_Ptr);
  1. 底层类型匹配: 尖括号中的类型必须是原始类型的底层类型。底层类型是指指针或引用所指向的实际类型。
    例如,如果有一个 const int* 类型的指针,使用 const_cast<double*>(constIntPtr) 来将其转换为 double* 类型是不合法的,因为 intdouble 不是相同的底层类型:
const int value = 42;
const int* ptr = &value;
int* ptr2 = const_cast<double*>(ptr); // 编译错误

请添加图片描述

3. reinterpret_cast

reinterpret—cast 通常为运算对象的位模式提供较低层次上的重新解释。举个例子,假设有如下的转换:

int a = 123;
int* ip = &a;
char* pc1 = reinterpret_cast<char*>(ip);
char* pc2 = static_cast<char*>(ip);     //这里使用static_cast会报错:类型转化无效
char* pc3 = const_cast<char*>(ip);      //const_cast不能修改基础类型

我们必须牢记 pc 所指的真实对象是一个 int 而非字符,如果把 pc 当成普通的字符指针使用就可能在运行时发生错误。例如:

cout << pc << endl;

可能导致异常的运行时行为。

用reinterpret_cast强转测试平台大小端

大端存储模式:是指把数据的低位保存在内存的高地址中,把数据的高位,保存在内存的低地址中;
小端存储模式:是指把数据的低位保存在内存的低地址中,把数据的高位,保存在内存的高地址中。

#include \<iostream>

int main() 
{
   int intValue = 0x12345678;

   // 使用 reinterpret_cast 将 int* 转化为 char*
   char* charPtr = reinterpret_cast<char*>(&intValue);

   // 输出每个字节的十六进制表示
   std::cout << "Memory content: ";
   for (int i = 0; i < sizeof(int); ++i) 
   {
       std::cout << std::hex << static_cast\<int>(charPtr[i]) << " ";
   }
   std::cout << std::endl;

   // 判断平台是大端还是小端
   if (charPtr[0] == 0x78) 
   {
       std::cout << "该平台是小端" << std::endl;
   }
   else if (charPtr[sizeof(int) - 1] == 0x78) 
   {
       std::cout << "该平台是大端" << std::endl;
   }
   else 
   {
       std::cout << "大小端未知" << std::endl;
   }

   return 0;
}

4. dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)。
使用形式如下所示:

dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)

其中:

  • e必须是一个类类型,并且通常情况下该类型应该含有虚函数。
  • 在第一种形式中,e 必须是一个有效的指针;
  • 在第二种形式中,e 必须是一个左值;
  • 在第三种形式中,e 不能是左值。

在上面的所有形式中, e 的类型必须符合以下三个条件中的任意一个:

  • e 的类型是目标 type 的公有派生类
  • e 的类型是目标 type 的公有基类
  • e 的类型就是目标 type 的类型

如果符合,且基类指针/引用指向的实际对象是派生类对象时,类型转换可以成功。否则,转换失败:

  • 如果一条 dynamic_cast语句的转换目标是指针类型并且失败了,则结果为 0 。
  • 如果转换目标是引用类型并且失败了,则 dynamic_cast 运算符将抛出一个 bad_cast 异常。

验证一下:

#include <iostream>
#include <typeinfo>

class A
{
public:
    virtual void f() {}
};

class B : public A
{
    virtual void f() override {}
};

int main()
{
    A a;
    B b;


    // 子类对象指针/引用 -> 父类指针/引用
    // dynamic_cast<type*>(e)
    A* ptrA = dynamic_cast<A*>(&b);
    if (ptrA) 
    {
        std::cout << "从B*到A*的动态转换成功。" << std::endl;
    }
    else 
    {
        std::cout << "从B*到A*的动态转换失败。" << std::endl;
    }

    // dynamic_cast<type&>(e)
    try 
    {
        A& refA = dynamic_cast<A&>(b);
        std::cout << "从B&到A&的动态转换成功。" << std::endl;
    }
    catch (const std::bad_cast& e) 
    {
        std::cout << "从B&到A&的动态转换失败。" << std::endl;
    }

    // dynamic_cast<type&&>(e) - 注意:这种用法不常见,通常情况下我们不会将右值引用作为 dynamic_cast 的目标
    try 
    {
        A&& rrefA = dynamic_cast<A&&>(std::move(b));
        std::cout << "从B&&到A&&的动态转换成功。" << std::endl;
    }
    catch (const std::bad_cast& e) 
    {
        std::cout << "从B&&到A&&的动态转换失败。" << std::endl;
    }

    // 父类对象指针/引用 -> 子类指针/引用
    // dynamic_cast<type*>(e)
    B* ptrB = dynamic_cast<B*>(&a);
    if (ptrB)
    {
        std::cout << "从A*到B*的动态转换成功。" << std::endl;
    }
    else 
    {
        std::cout << "从A*到B*的动态转换失败。" << std::endl;
    }

    // dynamic_cast<type&>(e)
    try
    {
        B& refB = dynamic_cast<B&>(a);
        std::cout << "从A&到B&的动态转换成功。" << std::endl;
    }
    catch (const std::bad_cast& e)
    {
        std::cout << "从A&到B&的动态转换失败。" << std::endl;
    }

    // dynamic_cast<type&&>(e)
    try
    {
        B&& rrefB = dynamic_cast<B&&>(std::move(a));
        std::cout << "从A&&到B&&的动态转换成功。" << std::endl;
    }
    catch (const std::bad_cast& e)
    {
        std::cout << "从A&&到B&&的动态转换失败。" << std::endl;
    }


    return 0;
}

请添加图片描述

后面三个失败的原因是括号中的指针或引用的实际指向的对象不是派生类对象,语法只允许一个动态类型是派生类指针的对象的静态类型从基类指针转化为派生类指针!

注意:

  1. dynamic_cast只能用于父类含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

向上转型:子类对象指针/引用 -> 父类指针/引用(不需要转换,因为有“赋值兼容规则”)
向下转型:父类对象指针/引用 -> 子类指针/引用(直接赋值不可以,但是用dynamic_cast转是安全的,因为有异常会抛异常或者返回空指针)


应该避免使用强制类型转换

强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是
否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用
域,以减少发生错误的机会。

强烈建议:避免使用强制类型转换


四、RTTI - 运行时类型识别

RTTI:Run-time Type identification 的简称,即:运行时类型识别。

C++通过以下方式来支持RTTI:

  1. typeid运算符
  2. dynamic_cast运算符
  3. decltype

typeid 运算符:

typeid 运算符是C++中的运算符,用于获取表达式的类型信息。它返回一个std::type_info 对象,该对象包含有关表达式类型的信息。主要用于在运行时获取对象的实际类型,特别是在处理多态类型时。

type_info 类必须定义在 <typeinfo> 头文件中,并且至少提供下表所列的操作:

type_info的操作描述
t1 == t2如果 type_info 对象 t1 和 t2 表示同一种类型,返回 true,否则返回false
t1 != t2如果 type_info 对象 t1 和 t2 表示不同的类型,返回 true,否则返回false
t.name()返回一个 C 风格字符串,表示类型名字的可打印形式。
t1.before(t2)返回一个 bool 值,表示t1是否位于t2之前。

除此之外,因为 type_info 类一般是作为一个基类出现,所以它还应该提供一个公有的虚析构函数。当编译器希望提供额外的类型信息时,通常在 type_info 的派生类中完成。type_info 类没有默认构造函数,而且它的拷贝和移动构造函数以及赋值运算符都被定义成删除的。因此,我们无法定义或拷贝 type_info类型的对象,也不能为 type_info 类型的对象赋值。创建 type_info 对象的唯一途径是使用 typeid 运算符。

示例代码:

#include <iostream>
#include <typeinfo>

class Base 
{
public:
    virtual ~Base() {}
};

class Derived : public Base {};

int main() 
{
    Base* basePtr = new Derived();

    // 使用 typeid 获取对象的类型信息
    const std::type_info& typeInfo = typeid(*basePtr);

    // 比较类型信息
    if (typeInfo == typeid(Derived)) 
    {
        std::cout << "basePtr 指向 Derived 类型的对象。" << std::endl;
    } else if (typeInfo == typeid(Base)) 
    {
        std::cout << "basePtr 指向 Base 类型的对象。" << std::endl;
    }

    delete basePtr;

    return 0;
}

由运行结果可见,typeid 运算符获取到的类型名是运行时的动态类型:
请添加图片描述

dynamic_cast运算符

上文已经介绍

decltype关键字

decltype 是C++11引入的关键字,用于获取表达式的类型而不进行实际的计算。它在编译时返回表达式的类型,并常用于模板元编程和泛型编程中,以提取变量或表达式的类型。

示例代码:

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
	decltype(t1 * t2) ret;
	cout << typeid(ret).name() << endl;
}

int main()
{
	const int x = 1;
	double y = 2.2;
	decltype(x * y) ret; // ret的类型是double
	decltype(&x) p; // p的类型是int*
	cout << typeid(ret).name() << endl;
	cout << typeid(p).name() << endl;
	F(1, 'a');
	return 0;
}

请添加图片描述

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

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

相关文章

facebook多账号运营为什么要用静态住宅ip代理?

在进行Facebook群控时&#xff0c;ip地址的管理是非常重要的&#xff0c;因为Facebook通常会检测ip地址的使用情况&#xff0c;如果发现有异常的使用行为&#xff0c;比如从同一个ip地址频繁进行登录、发布内容或者在短时间内进行大量的活动等等&#xff0c;就会视为垃圾邮件或…

神经网络学习小记录78——Keras CA(Coordinate attention)注意力机制的解析与代码详解

神经网络学习小记录78——Keras CA&#xff08;Coordinate attention&#xff09;注意力机制的解析与代码详解 学习前言代码下载CA注意力机制的概念与实现注意力机制的应用 学习前言 CA注意力机制是最近提出的一种注意力机制&#xff0c;全面关注特征层的空间信息和通道信息。…

花费200元,我用全志H616和雪糕棒手搓了一台可UI交互的视觉循迹小车

常见的视觉循迹小车都具备有路径识别、轨迹跟踪、转向避障、自主决策等基本功能&#xff0c;如果不采用红外避障的方案&#xff0c;那么想要完全满足以上这些功能&#xff0c;摄像头、电机、传感器这类关键部件缺一不可&#xff0c;由此一来小车成本也就难以控制了。 但如果&a…

欢迎来到IT时代----盘点曾经爆火全网的计算机电影

计算机专业必看的几部电影 计算机专业必看的几部电影&#xff0c;就像一场精彩的编程盛宴&#xff01;《黑客帝国》让你穿越虚拟世界&#xff0c;感受高科技的魅力&#xff1b;《社交网络》揭示了互联网巨头的创业之路&#xff0c;《源代码》带你穿越时间解救世界&#xff0c;这…

LabVIEW声速测定实验数据处理

LabVIEW声速测定实验数据处理 介绍了一个基于LabVIEW的声速测定实验数据处理系统的应用。该系统利用LabVIEW的强大数据处理和分析能力&#xff0c;通过设计友好的用户界面和高效的算法&#xff0c;有效提高了声速测定实验的数据处理效率和准确性。通过这个案例&#xff0c;可以…

2024最新uniapp基础腾讯IM

即时通信 IM 快速入门&#xff08;uniapp vue2/vue3&#xff09;-快速入门-文档中心-腾讯云

行人重识别综述

Deep Learning for Person Re-identification: A Survey and Outlook 论文地址https://arxiv.org/pdf/2001.04193 1. 摘要 we categorize it into the closed-world and open-world settings. closed-world&#xff1a;学术环境下 open-world &#xff1a;实际应用场景下 2…

瑞_23种设计模式_适配器模式

文章目录 1 适配器模式&#xff08;Adapter Pattern&#xff09;1.1 介绍1.2 概述1.3 适配器模式的结构 2 类适配器模式2.1 案例2.2 代码实现 3 对象适配器模式&#xff08;推荐&#xff09;★3.1 案例3.2 代码实现 4 拓展——JDK源码解析 &#x1f64a; 前言&#xff1a;本文章…

Java项目,营销抽奖系统设计实现

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 项目&#xff1a;https://gaga.plus 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 大家好&#xff0c;我是技术UP主&#xff0c;小傅哥。 经过这个假期的嘎嘎卷&#x1f9e8;…

VMware下安装银河麒麟V10操作系统

VMware下安装银河麒麟V10操作系统 文章目录 下载在VMware中应用编辑虚拟机设置 在麒麟系统内安装 下载 官网下载&#xff1a;https://www.kylinos.cn/ 银河麒麟、中标麒麟、开放麒麟、星光麒麟 在VMware中应用 1.新建虚拟机 2.稍后安装操作系统 3.新建虚拟机向导&#xff0…

设计模式三:工厂模式

工厂模式包括简单工厂模式、工厂方法模式和抽象工厂模式&#xff0c;其中后两者属于23中设计模式 各种模式中共同用到的实体对象类&#xff1a; //汽车类&#xff1a;宝马X3/X5/X7&#xff1b;发动机类&#xff1a;B48TU、B48//宝马汽车接口 public interface BMWCar {void s…

CSS-基础-MDN文档学习笔记

CSS构建基础 查看更多学习笔记&#xff1a;GitHub&#xff1a;LoveEmiliaForever MDN中文官网 CSS选择器 选择器是什么 CSS 选择器是 CSS 规则的第一部分&#xff0c;它用来选择HTML元素&#xff0c;选择器所选择的元素&#xff0c;叫做选择器的对象 选择器列表 如果有多…

盘点3款实用的音频文件转文字工具!

在信息爆炸的时代&#xff0c;我们每天都面临着海量的信息输入和输出。其中&#xff0c;音频信息作为一种重要的信息传播方式&#xff0c;如何高效地将其转化为文字&#xff0c;成为许多人和企业迫切的需求。本文将为您盘点几款实用的音频转文字工具&#xff0c;让声音瞬间转化…

通过闭包表解决无限极代理分销

闭包表设计 闭包表是解决分层存储一个简单而又优雅的解决方案&#xff0c;它记录了表中所有的节点关系&#xff0c;并不仅仅是直接的父子关系。   在闭包表的设计中&#xff0c;额外创建了一张节点关系表(空间换取时间)&#xff0c;它包含两列&#xff0c;每一列都是一个指向…

facebook群控如何做?使用静态住宅ip代理有什么好处?

在进行Facebook群控时&#xff0c;ip地址的管理是非常重要的&#xff0c;因为Facebook通常会检测ip地址的使用情况&#xff0c;如果发现有异常的使用行为&#xff0c;比如从同一个ip地址频繁进行登录、发布内容或者在短时间内进行大量的活动等等&#xff0c;就会视为垃圾邮件或…

我的NPI项目之Android USB 系列(一) - 遥望和USB的相识

和USB应该是老朋友了&#xff0c;从2011年接触Android开发开始&#xff0c;就天天和USB打交道了。那时候还有不 对称扁头的usb/方口的usb&#xff0c;直到如今使用广泛的防反插USB3.0 type-C。 但是&#xff0c;一直有一个不是很清楚的问题萦绕在心头&#xff0c;那就是。先有…

Vue3 学习笔记(Day1)

「写在前面」 本文为尚硅谷禹神 Vue3 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。 目录 0 课程介绍 1 Vue3 简介 2 创建 Vue3 工程 2.1 基于 vue-cli 创建 2.2 基于 vite 创建&#xff08;推荐&#xff09; 2.3 …

[word] word正反面打印应该怎么设置呢? #知识分享#学习方法#职场发展

word正反面打印应该怎么设置呢&#xff1f; word文档打印时&#xff0c;如果页数比较多&#xff0c;出于格式要求或为了节省纸张&#xff0c;通常需要正反面打印&#xff0c;那怎么操作正反双面打印呢&#xff1f;通常有两种方法打印。 1、选择“打印”对话框底部的“打印”下…

linux 安装、删除 JTAG驱动

安装 安装驱动需要sudo访问权限&#xff0c;所以得手动安装。 在petalinux安装目录下&#xff1a; 文件的路径。 cd tools/xsct/data/xicom/cable_drivers/lin64/install_script/install_drivers 然后执行文件 install_drivers。 sudo ./install_drivers安装成功。 删除 …

FFmpeg进阶-给视频添加马赛克效果

很多时候为了隐藏视频中的敏感信息如人脸、身份证号、车牌号等,我们会采用马赛克算法对视频帧中的一部分内容进行处理。这里介绍一下如何采用FFmpeg实现马赛克效果。 马赛克效果算法的原理如下: 1.分块处理:首先将图像划分为多个小块或区域 2.像素替换:对于每个小块,算法会将…