Bootstrap

嵌入式工程师面试题(一)·define和const的区别以及IIC为什么要加上拉电阻,为什么使用开漏输出

目录

1.  define和const的区别?

1.1  作用域和类型安全性

1.2  调试和可读性

1.3  预处理处理时间

1.4  符号的作用范围

2.  IIC为什么要加上拉电阻,为什么使用开漏输出

2.1  上拉电阻

2.2  开漏输出


1.  define和const的区别?

        define 和 const 是在C和C++中用来定义常量的两种不同方式,它们有以下几点区别:

① 作用域和类型安全性;

② 调试和可读性;

③ 预处理处理时间;

④ 符号的作用范围。

1.1  作用域和类型安全性

        define:define 是预处理指令,它在编译之前就会被处理,并且没有作用域限制。define 定义的常量是简单的文本替换,不会进行类型检查和作用域检查。因此,它们的作用域是全局的,且不会保留类型信息。

#define PI 3.14159

        const 常量:const 是C和C++中的关键字,用来定义常量并且具有类型。const 常量在编译时会进行类型检查,并且有作用域限制,因此更加安全和可靠。

const double PI = 3.14159;

1.2  调试和可读性

        使用 const 定义的常量在编译器处理之后保留了类型信息,因此更容易调试和理解代码。而 define 定义的常量仅是简单的文本替换,不会展示出常量的实际值或类型。

1.3  预处理处理时间

        define 是在预处理阶段处理的,它们在编译前被替换为常量值或表达式。而 const 常量是在编译阶段进行处理的,保留了编译时的类型检查和优化特性。

1.4  符号的作用范围

        define 定义的符号在整个代码中都是可见和有效的,包括所有的源文件,而 const 常量则在定义它的作用域内有效,提供了更好的封装性。

使用 define 宏定义常量

#include <iostream>
using namespace std;

// 使用 #define 宏定义常量 PI
#define PI 3.14159

int main() {
    double radius = 5.0;
    double area = PI * radius * radius;

    cout << "圆的面积为: " << area << endl;

    // 尝试修改宏定义,编译器不会报错
    #undef PI
    #define PI 3.14

    double new_area = PI * radius * radius;
    cout << "新圆的面积为: " << new_area << endl;

    return 0;
}

        #define PI 3.14159:这行代码将 PI 定义为一个宏,其值为 3.14159。在编译预处理阶段,所有出现 PI 的地方都会被简单替换为 3.14159。   

        #undef PI 和 #define PI 3.14:这两行代码分别取消了之前的宏定义 PI,然后重新定义为 3.14。这种操作是在编译预处理阶段完成的,因此不会影响运行时的变量。

        这种方法的好处是简单直接,并且可以用来定义简单的常量值。但是缺点是宏不会进行类型检查,也不会被编译器记录,因此在调试时不会显示出宏的具体值。

使用 const 常量定义常量

#include <iostream>
using namespace std;

int main() {
    const double PI = 3.14159;
    double radius = 5.0;
    double area = PI * radius * radius;

    cout << "圆的面积为: " << area << endl;

    return 0;
}

        const double PI = 3.14159;:这行代码使用 const 关键字定义了一个双精度浮点型常量 PI,其值为 3.14159,关键字 const 表示 PI 是一个只读变量,一旦初始化后,其值不能再被修改。在编译器处理时,const 常量会进行类型检查,并且有作用域限制,如上代码 PI 的作用域限制在main 函数内,这意味着它只能在 main 函数中使用,而在函数外部是不可见的。

        const 常量提供了更好的类型安全性和可读性,因为它们在代码中保留了类型信息,也可以被编译器记录和优化。

简单来说:

#define 宏定义

优点:在编译器在处理时并不关心宏的具体值或类型,其可以在任何地方重新定义或者取消定义,对于简单的常量定义或者特定的预处理需求,#define 宏定义则是一种便捷有效的选择。

缺点:宏定义不会进行类型检查,也不会保留类型信息。这意味着在使用宏定义时,可能会出现意外的文本替换,导致错误或者难以调试的问题。

const

优点:具有类型,编译器会对其进行类型检查,确保在使用时符合类型规定,避免意外的类型错误。具有明确的作用域,仅在定义的块或文件内可见,不会像宏定义那样污染全局命名空间。

缺点:如果需要在预处理阶段(编译前)就替换为常量值,const 不适合,因为它不参与预处理阶段,而是在编译器进行类型检查和符号解析时生效。

2.  IIC为什么要加上拉电阻,为什么使用开漏输出

2.1  上拉电阻

定义逻辑高电平:IIC总线上的设备是通过拉低(逻辑0)和不拉低(逻辑1)SDA和SCL线来通信的。上拉电阻确保当设备不主动拉低线时(即释放线),线路会被上拉到逻辑高电平(通常是Vcc或3.3V),从而保证了逻辑高的定义和通信的准确性。

信号线的回复位:IIC总线采用的是开漏输出,这意味着在逻辑0时设备可以拉低线路,而在逻辑1时,设备不对线路施加电平,而是释放线路。上拉电阻确保即使设备释放线路,线路上也会保持逻辑高电平,而不会漂浮。

简单来说,上拉电阻的作用是当IIC总线在空闲状态,使SDA和SCL处于高电平状态,并且使总线在处于开漏输出状态下时可以完成高低电平之间的转换

2.2  开漏输出

        开漏输出指的是设备在输出高电平时,其输出端口处于高阻抗状态(即不拉高),而在输出低电平时则拉低输出端口,这种输出的优势包括:

① 多主机系统兼容性:在I2C总线中,允许多个设备共享同一条总线。开漏输出使得多个设备可以以一种非干扰的方式共享总线,因为设备只需拉低线路来发送逻辑0,而不会直接推高电平。

② 容错能力:开漏输出增强了总线的容错能力。如果多个设备同时尝试拉低线路,总线会被拉到逻辑0。当设备释放线路时,上拉电阻确保线路上升到逻辑高电平。

③ 电平适配能力:允许设备以不同电平(例如3.3V或5V)进行通信,因为线路的逻辑高电平由上拉电阻确定,而不受单个设备的输出电平影响。

总结:根据其拉低不拉高的特性,可以通过不同的上拉电阻确定不同的逻辑高(高电平),并且设备不会主动拉高电平,只能释放总线,因此不会出现电平冲突的情况,提高了容错能力。

STM32F1之I2C通信_stm32f1 i2c-CSDN博客

;