【笔记1】关于C语言中对非符号数进行逐位取反(~)的重要问题

    话不多说,先放上代码

#include 
 int main()
{
 
   unsigned int a = 60;    /* 60 = 0011 1100 */  
   unsigned int b = 13;    /* 13 = 0000 1101 */
   int c = 0;           
 
   c = a & b;       /* 12 = 0000 1100 */ 
   printf("Line 1 - c 的值是 %d\n", c );
 
   c = a | b;       /* 61 = 0011 1101 */
   printf("Line 2 - c 的值是 %d\n", c );
 
   c = a ^ b;       /* 49 = 0011 0001 */
   printf("Line 3 - c 的值是 %d\n", c );
 
   c = ~a;          /*-61 = 1100 0011 */
   printf("Line 4 - c 的值是 %d\n", c );
 
   c = a << 2;     /* 240 = 1111 0000 */
   printf("Line 5 - c 的值是 %d\n", c );
 
   c = a >> 2;     /* 15 = 0000 1111 */
   printf("Line 6 - c 的值是 %d\n", c );
}

    1.重要的一个运算

     c = ~a
  • ~a 的结果是 42949672351111_1111_1111_1111_1111_1111_1100_0011),这是一个无符号整数。

  • 将 4294967235 赋值给 int 类型的 c,由于 4294967235 超出了 int 的范围,会发生截断,结果为 -61

  • 使用 %d 输出 c,结果是 -61

    2. 截断过程的详细解释    

        当将 unsigned int 类型的值赋值给 int 类型的变量时,C 语言会进行 隐式类型转换。具体过程如下:

        (1) 二进制表示的复制
  • unsigned int 的二进制表示会直接复制到 int 的存储空间中。

  • 例如,c = ~a; 会将 1111_1111_1111_1111_1111_1111_1100_0011 直接复制到 c 的存储空间中。

        (2) 符号位的解释
  • 在 int 类型中,最高位是符号位:

    • 如果符号位是 0,则表示正数。

    • 如果符号位是 1,则表示负数。

  • 1111_1111_1111_1111_1111_1111_1100_0011 的最高位是 1,因此它被解释为一个负数。

        (3) 补码的解释
  • 在 int 类型中,负数使用 补码 表示。

  • 补码的计算规则是:取反后加 1

  • 对于 1111_1111_1111_1111_1111_1111_1100_0011,其补码计算如下:

    1. 取反:0000_0000_0000_0000_0000_0000_0011_1100

    2. 加 1:0000_0000_0000_0000_0000_0000_0011_1101

  • 这个二进制数对应的十进制值是 61

  • 由于符号位是 1,因此最终结果是 -61。                                                                                                                                                                                                        3.STM32开发中对应的实际问题  

  • 在 STM32 单片机软件开发 中,类型转换 和 截断问题 是非常常见的,尤其是在处理硬件寄存器、位操作、数据存储和通信协议时。由于嵌入式系统对内存和性能的要求较高,开发者通常会使用更小的数据类型(如 uint8_tuint16_t)来节省资源,这增加了类型不匹配和截断问题的风险。
  • (1)硬件寄存器操作

    STM32 的硬件寄存器通常是以 无符号整数 类型定义的(如 uint32_t)。如果开发者错误地使用有符号整数(如 int)来操作这些寄存器,可能会导致意外的行为。

    例子:配置 GPIO 寄存器
    uint32_t *GPIO_MODER = (uint32_t*)0x40020000; // GPIO 模式寄存器地址
    int mode = 0xFFFF; // 错误:使用有符号整数
    *GPIO_MODER = mode; // 可能截断或符号扩展
  • 如果 mode 是一个负数,赋值给 GPIO_MODER 时会被解释为一个非常大的无符号整数,导致 GPIO 配置错误。

  • 解决方法:
  • 使用正确的无符号类型:

    uint32_t mode = 0xFFFF; // 正确:使用无符号整数
    *GPIO_MODER = mode;


  • (2)位操作

    在 STM32 开发中,位操作(如 &|~<<>>)非常常见,用于配置寄存器或提取数据。如果操作数的类型不匹配,可能会导致截断或符号扩展问题。

    例子:读取 ADC 数据
    uint16_t adc_value = ADC1->DR; // 从 ADC 数据寄存器读取值
    int processed_value = adc_value >> 4; // 右移操作
  • 如果 adc_value 的最高位是 1,右移操作可能会导致符号扩展(即高位填充 1),从而得到一个负数。

  • 解决方法:
  • 使用无符号类型进行位操作:

    uint16_t adc_value = ADC1->DR; // 从 ADC 数据寄存器读取值
    int processed_value = adc_value >> 4; // 右移操作


  • (3) 数据存储与通信

    在存储数据或通过通信协议(如 UART、I2C、SPI)传输数据时,如果数据类型不匹配,可能会导致数据截断或解释错误。

    例子:UART 数据传输
    uint8_t buffer[2];
    uint16_t data = 0xABCD; // 16 位数据
    buffer[0] = data >> 8;   // 高字节
    buffer[1] = data & 0xFF; // 低字节
  • 如果错误地使用有符号整数:

    int data = 0xABCD; // 错误:使用有符号整数
    buffer[0] = data >> 8;   // 可能符号扩展
    buffer[1] = data & 0xFF; // 低字节

    当 data 是负数时,右移操作会导致符号扩展,buffer[0] 的值可能不正确。

  • 解决方法:
  • 始终使用无符号整数处理数据:

    uint16_t data = 0xABCD; // 正确:使用无符号整数
    buffer[0] = data >> 8;
    buffer[1] = data & 0xFF;


  • (4)数学运算

    在嵌入式系统中,数学运算(如加法、乘法)的结果可能会超出目标类型的范围,导致截断或溢出。

    例子:计算定时器周期
    uint32_t clock_freq = 72000000; // 72 MHz
    uint32_t prescaler = 72;
    uint32_t period = clock_freq / prescaler; // 计算周期
    int target_period = period; // 错误:可能截断
  • 如果 period 的值超出 int 的范围,赋值给 target_period 时会被截断。

  • 解决方法:
  • 使用一致的无符号类型:

    uint32_t target_period = period; // 正确:使用无符号整数


  • 5. 函数参数与返回值

    在调用库函数或自定义函数时,如果参数或返回值的类型不匹配,可能会导致截断问题。

    例子:HAL 库函数
    uint16_t adc_value = HAL_ADC_GetValue(&hadc1); // 读取 ADC 值
    int processed_value = adc_value * 2; // 错误:可能溢出
  • 如果 adc_value 的值较大,adc_value * 2 可能会超出 int 的范围。

  • 解决方法:
  • 使用更大的数据类型或强制类型转换:

    uint32_t processed_value = (uint32_t)adc_value * 2; // 正确:使用更大的类型

  • 6. 枚举类型

    在 STM32 开发中,枚举类型常用于表示状态或选项。如果枚举值被错误地赋值给有符号整数,可能会导致问题。

    例子:状态机
    typedef enum {
        STATE_IDLE,
        STATE_RUNNING,
        STATE_ERROR
    } State_t;
    
    State_t state = STATE_IDLE;
    int current_state = state; // 错误:枚举值可能被解释为负数
    解决方法:
  • 使用正确的枚举类型:

    State_t current_state = state; // 正确:使用枚举类型

你可能感兴趣的:(c语言,单片机,stm32)