C语言 - 开发技巧篇

代码注释

C语言中,因为某些原因(比如调试),我们经常需要把一段代码注释掉,许多初学者一般使用/* */来注释你想暂时不需要,可能以后需要的代码,即


/*  你想注释掉的代码*/

这种做法存在一些可怕的风险,因为在C语言中,注释不允许嵌套,第一个/* 会与第一个*/结合,也就是说如果你注释的代码里面本身就存在用/* */注释,那么你注释这段代码时将会出错,举个例子:

/* int i; /定义记录变量/ */

对于上面的代码,你的本意可能是和颜色所标的一样,但是实际效果却是:

/*    int i;   /*定义记录变量*/    */  

结果程序出错,当然一般编译器会给你报错,然而这却不是一种好的习惯。

解决的办法是使用预处理指令

#if 0

你想要注释的code;

#endif

头文件防止重复包含

在C语言中,#ifndef 是一种预处理指令,用于防止头文件或其他代码被重复包含。这是通过使用预处理器的宏定义功能来实现的。以下是使用 #ifndef 及其相关指令防止头文件重复包含的常见规则和步骤:

  1. 定义唯一的宏标识符
    使用 #define 创建一个唯一的宏标识符,通常以项目或文件名的缩写开始,后跟 _H 来表示这是一个头文件。

    #ifndef MY_PROJECT_H
    #define MY_PROJECT_H
    

    这里,MY_PROJECT_H 是宏标识符,MY_PROJECT 是项目或文件的名称。

  2. 放置头文件内容
    #define 指令之后,放置头文件的所有内容。

  3. 结束宏定义
    在文件的末尾,使用 #endif 来结束宏定义的条件编译块。

    #endif // MY_PROJECT_H
    

这种模式被称为“包含守卫”或“头文件卫士”。其工作原理如下:

  • 当预处理器处理文件时,它会检查 #ifndef 后面指定的宏是否已经定义。
  • 如果宏未定义,预处理器将继续包含该头文件的内容,并将宏定义为1(或任何非零值)。
  • 如果宏已经定义(即头文件已经被包含过),#ifndef 条件不成立,预处理器将跳过该文件的所有内容,直到对应的 #endif

规则和最佳实践:

  • 唯一性:确保每个头文件的宏标识符是唯一的,以避免与其他头文件冲突。
  • 位置:将包含守卫放在头文件的最顶部和最底部,确保整个头文件内容都被保护。
  • 命名约定:使用一种一致的命名约定来定义宏标识符,使其易于识别和记忆。
  • 避免宏冲突:不要在头文件之外使用相同的宏名,避免与包含守卫冲突。

使用包含守卫是防止头文件重复包含的标准做法,可以确保编译时的效率和避免潜在的编译错误。


位操作

C语言支持的6种位操作符如下:
在这里插入图片描述

  1. 不改变其他位的值的状况下,对某几个位进行设值
    方法:先对需要设置的位用&操作符(对应位&0)进行清零操作,然后用|操作符设值(对应位|你想要设定的值)。

举例:改变 GPIOA-> BSRRL 的状态

GPIOA-> BSRRL &=0XFF0F;  //将第 4-7 位清 0
GPIOA-> BSRRL |=0X0040; //设置相应位的值,不改变其他位的值
  1. 移位操作提高代码的可读性
    位操作在单片机开发中也非常重要,我们来看看下面一行代码
    GPIOx->ODR |= (((uint32_t)0x01) << pinpos);

这个操作就是将 ODR 寄存器的第 pinpos 位设置为 1,为什么要通过左移而不是直接设置一个固定的值呢?

其实,这是为了提高代码的可读性以及可重用性。这行代码可以很直观明了的知道,是将第 pinpos 位设置为 1。

如果你写成GPIOx->ODR =0x0040; 这样的代码就不好看也不好重用了。

  1. ~取反操作使用技巧

SR 寄存器的每一位都代表一个状态,某个时刻我们希望去设置某一位的值为 0,同时其他位都保留为 1,

简单的作法是直接给寄存器设置一个值:

        TIMx->SR=0xFFF7

这样的作法设置第 3 位为 0,但是这样的作法同样不好看,并且可读性很差。看看库函数

代码中怎样使用的:

        TIMx->SR &= (uint16_t)~TIM_FLAG;

而 TIM_FLAG 是通过宏定义定义的值:

    #define TIM_FLAG  ((uint16_t)0x0001)

看这个应该很容易明白,可以直接从宏定义中看出 TIM_FLAG_ 就是设置的第 0位了,可读性非常强。


定义、声明全局变量

  • 一个全局变量或函数可以 (在多个编译单元中) 有多处 “声明”, 但是 “定义” 却只能允许出现一次。

  • 定义是分配空间并赋初值 (如果有) ,声明则是给需要调用该变量或者函数的地方声明该变量或函数,使编译不出错。

  • 最好的安排是在某个相关的 .c 文件中定义, 然后在其对应的头文件 (.h)(用extern
    修饰) 中进行外部声明, 在需要使用的时候, 只要包含对应的头文件即可。定义变量的
    .c 文件也应该包含该头文件, 以便编译器检查定义和声明的一致性。


字面值(常量)的数据类型

当一个程序内出现出现整形字面值时(比如在宏定义中#define A 3,中的3),他是属于整形家族9种不同数据中的哪一种(int,unsigned int等)?

其实我们可以在这些字面值后面添加一个后缀来改变缺省的规则,比如123L(或其小写)表明其是长整型,123u(或123U)等等。

当然,如果没有添加后缀,即其处在缺省的状态,那么它是能容纳整个整形中的最短整形。

相关推荐

  1. c语言基础C

    2024-07-21 14:58:03       24 阅读
  2. 开发语言漫谈-C语言

    2024-07-21 14:58:03       29 阅读
  3. 开发语言漫谈-C++

    2024-07-21 14:58:03       104 阅读
  4. 开发语言漫谈-C#

    2024-07-21 14:58:03       34 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 14:58:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 14:58:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 14:58:03       45 阅读
  4. Python语言-面向对象

    2024-07-21 14:58:03       55 阅读

热门阅读

  1. Try ubuntu core (by quqi99)

    2024-07-21 14:58:03       21 阅读
  2. 独孤思维:副业赚钱,易如反掌

    2024-07-21 14:58:03       16 阅读
  3. Composition API对比Options API

    2024-07-21 14:58:03       16 阅读
  4. C# 删除DataTable里符合条件的行

    2024-07-21 14:58:03       17 阅读
  5. centos7更换yum源

    2024-07-21 14:58:03       16 阅读
  6. c++应用网络编程之五Windows常用的网络IO模型

    2024-07-21 14:58:03       18 阅读
  7. opencv—常用函数学习_“干货“_12

    2024-07-21 14:58:03       18 阅读
  8. AI文章特点详细分析

    2024-07-21 14:58:03       19 阅读
  9. ubuntu24无法网络无法连接的问题

    2024-07-21 14:58:03       16 阅读
  10. mqtt协议有哪些机制

    2024-07-21 14:58:03       17 阅读