关于魔数的分析

在嵌入式中经常碰到魔数,比如:

#define PLATDRV_MAGIC 0X60

#define LED_OFF    _IO(PLATDRV_MAGIC,0X18)

#define LED_ON      _IO(PLATDRV_MAGIC,0X19)

然后我们会调用ioctl(fd[i],LED_OFF)或者ioctl(fd[i],LED_ON)

网上资料:_IO(魔数,基数)

                  _IOR(魔数,基数,变量型)

                  _IOW(魔数,基数,变量型)

                  _IOWR(魔数,基数,变量型)

#define _IO(type,nr)    _IOC(_IOC_NONE,(type),(nr),0)

再看 _IOC() 的定义://include/asm/ioctl.h

#define _IOC(dir,type,nr,size)   

(((dir)<< _IOC_DIRSHIFT)|((type)<<_IOC_TYPESHIFT)|((nr)<<_IOC_NRSHIFT)|((size) << _IOC_SIZESHIFT))

#define _IOC_DIRSHIFT    (_IOC_SIZESHIFT+_IOC_SIZEBITS)    //16+14=30

#define _IOC_TYPESHIFT    (_IOC_NRSHIFT+_IOC_NRBITS)      //0+8=8

#define _IOC_NRSHIFT    0

#define _IOC_SIZESHIFT    (_IOC_TYPESHIFT+_IOC_TYPEBITS)//8+8=16

#define _IOC_NRBITS    8

#define _IOC_TYPEBITS    8  //

#define _IOC_SIZEBITS     14 //大小(size)字段的字位宽度,14bits

由上面的定义,往上推得到:

_IOC_TYPESHIFT = 8

_IOC_SIZESHIFT = 16

_IOC_DIRSHIFT = 30

所以#define _IOC(dir,type,nr,size)   (((dir)<<30)|((type)<<8)|((nr)<<0)((size)<<16))

cmd的大小为 32位,共分 4 个域:

bit31~bit30 2位为 “区别读写” 区,作用是区分是读取命令还是写入命令。//dir
bit29~bit16 14位为 "数据大小" 区,表示 ioctl() 中的 arg 变量传送的内存大小。//size
bit15~bit08  8位为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。//type
bit07~bit00   8位为 "区别序号" 区,是区分命令的命令顺序序号。//nr

所以#define LED_OFF    _IO(PLATDRV_MAGIC,0X18)就是_IOC(_IOC_NONE,PLATDRV_MAGIC,0X18,0)

其中_IOC_NONE为0 ,也即是:

((0<<30)|(0x60<<8)|(0x18<<0)|(0<<16))

现在我们来看ioctl(fd[i],LED_OFF)

ioctl的原型:int ioctl(int fd, int cmd, …)

其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是

一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。

在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。

怎么实现这些操作,这是每一个程序员自己的事情,因为设备都是特定的,这里也没法说。关键在于怎么样组织命令码,因为在ioctl

中命令码是唯一联系用户程序命令和驱动程序支持的途径。

在Linux核心中是这样定义一个命令码的:

https://img-blog.youkuaiyun.com/20130715085808218?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWFuZ3dlbjEyMw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

| 设备类型 | 数据尺寸 | 魔数   |  序列号 |

|      2 bit    |    14 bit   | 8 bit    |     8bit   |

这样一来,一个命令就变成了一个整数形式的命令码。但是命令码非常的不直观,所以Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或者是从命令码得到一些用户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送方向和数据传输尺寸。






<think>我们正在讨论如何避免字。字是指在代码中直接出现的、没有解释意义的字。它们会降低代码的可读性和可维护性。 根据引用[1]:字是臭名昭著的劣质代码现象之一。如果需要在不同的地点引用同一个逻辑,就会给程序员带来麻烦。一旦这些发生变化,我们就要在程序中找到所有引用的地方,并将它们全部修改。 避免字的方法: 1. 使用命名常量(宏定义或const常量)代替字。 2. 使用枚举(enum)代替一组相关的字。 3. 使用函或方法封装复杂的计算。 引用[2]给出了一个使用宏定义消除字的例子: #define PRICE (10) #define AMOUNT (10) 这样,在代码中就可以使用PRICE和AMOUNT来代替10,如果价格或量需要改变,只需要修改宏定义即可。 另外,引用[3]和[4]虽然涉及字,但它们是题目中的算法,并没有直接展示避免字的方法。不过,我们可以从这些例子中看到,如果算法中出现了字,也会使得代码难以理解。 因此,我们可以总结以下步骤: 步骤1:识别代码中的字 - 检查代码中直接出现的字(除了0和1等常见且意义明确的字外),特别是多次出现的相同字,或者具有特定含义的字(如最大值、最小值、固定步长等)。 步骤2:为字赋予有意义的名称 - 使用宏定义(#define)或常量(const)给这些字一个描述性的名称。 步骤3:将字替换为定义的常量 - 在代码中所有使用该字的地方替换为常量名。 步骤4:对于一组相关的字,使用枚举 - 例如,状态码、错误码等。 步骤5:对于复杂的计算,使用函封装 - 如果某个字是通过一系列计算得到的,那么应该用一个函来计算它,并给函一个描述性的名称。 示例: 原始代码(含有字): double area = 3.14159 * radius * radius; if (status == 1) { ... } 改进后: #define PI 3.14159 #define STATUS_ACTIVE 1 double area = PI * radius * radius; if (status == STATUS_ACTIVE) { ... } 或者使用const常量(C++中更推荐): const double PI = 3.14159; const int STATUS_ACTIVE = 1; 注意:在C语言中,const常量并不完全等同于编译时常量(在组大小等地方不能使用),所以有时还是需要用宏。但在C++中,const常量可以用于组大小等上下文。 另外,枚举的例子: enum Status { ACTIVE = 1, INACTIVE = 2 }; 然后使用: if (status == ACTIVE) { ... } 这样,代码的可读性和可维护性就大大提高了。 引用[1]强调了字带来的维护困难,而引用[2]给出了用宏定义消除字的具体代码示例。 因此,避免字的核心思想就是:用有意义的名称来代替字,使得代码自文档化,并且修改时只需改动一处。 相关问题: 1. 在C语言中,宏定义和const常量在替代字时有什么区别? 2. 如何识别代码中的字? 3. 除了字,还有哪些类似“法”的代码坏味道?如何避免?</think>### 如何在编程中避免使用字(Magic Number)是指在代码中直接出现的、未解释含义的值常量。它们会显著降低代码的可读性和可维护性。以下是避免字的实用方法: #### 1. **使用命名常量替代** [^1][^2] - **宏定义**(C语言): ```c #define MAX_RETRY_COUNT 3 // 最大重试次 #define TIMEOUT_MS 5000 // 超时时间(毫秒) for (int i = 0; i < MAX_RETRY_COUNT; i++) { if (connect(server) == SUCCESS) break; sleep(TIMEOUT_MS); } ``` - **const常量**(C++/Java): ```cpp const int MAX_BUFFER_SIZE = 1024; // 缓冲区大小 char buffer[MAX_BUFFER_SIZE]; ``` #### 2. **枚举替代状态码** [^1] 当字表示状态时,使用枚举增强可读性: ```c typedef enum { STATE_IDLE = 0, STATE_RUNNING = 1, STATE_ERROR = 2 } SystemState; SystemState current = STATE_RUNNING; ``` #### 3. **函封装复杂计算** [^3] 将涉及多个字的计算逻辑封装成函: ```c // 避免:int result = (value * 100) / 256; int calculate_ratio(int value) { const int SCALE_FACTOR = 100; const int BIT_DEPTH = 256; return (value * SCALE_FACTOR) / BIT_DEPTH; } ``` #### 4. **配置文件管理参** 将易变的值移到配置文件中: ```ini # config.ini max_connections = 50 timeout = 30 ``` 代码中读取配置: ```python import configparser config = configparser.ConfigParser() config.read('config.ini') max_conn = config.getint('DEFAULT', 'max_connections') ``` #### 5. **避免的情况示例 **原始代码(含字)**: ```c if (status == 3) { // 3代表什么? process_data(data, 100); // 100是什么? } ``` **改进后**: ```c #define STATUS_COMPLETED 3 #define DEFAULT_BATCH_SIZE 100 if (status == STATUS_COMPLETED) { process_data(data, DEFAULT_BATCH_SIZE); } ``` #### 注意事项 1. **命名规范**:常量名需全大写+下划线(如`MAX_LENGTH`) 2. **作用域控制**: - 全局常量使用`#define`或全局`const` - 局部常量使用函内`static const` 3. **文档补充**:为复杂常量添加注释说明物理含义: ```c #define EARTH_GRAVITY 9.80665 // 地球重力加速度(m/s²) ``` > 通过消除字,当需要修改值时(如将超时从5秒改为10秒),只需修改**一处定义**,避免遗漏导致逻辑错误[^1]。同时代码可读性大幅提升,新开发者能快速理解字的业务含义。 --- ### 相关问题 1. **在团队协作中如何强制避免字?** (代码审查规则/静态分析工具配置方法) 2. **字与幻字符串(Magic String)有何异同?** (比较两种代码坏味道的处理策略) 3. **如何平衡避免字与性能优化的关系?** (常量折叠/编译器优化机制分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值