21天C语言代码训练营(第十九天)

21天C语言代码训练营(第十九天)_第1张图片
此图来自网络

宏的基本应用

今天我们要说的一个C语言编程技巧和宏定义有关。宏定义可以帮助我们防止出错,提高代码的可移植性和可读性。通过我们前面的介绍,大家都知道宏定义有这样几个最基本的功能:

  • 避免立即数带来的维护困难

我们通过下面这段代码来说明:

    #define MAX_NUM 100
    
    int arr[MAX_NUM];
    int i;
    for (i = 0; i < MAX_NUM; i++)
    {
        arr[i] = i + 1;
    }

这段代码是一种最常规的应用,一旦数组大小需要改变,只要修改第一行代码就够了,大大减少了维护成本。

  • 控制编译流程

这个也是常用技巧,通过条件编译宏控制编译过程。下面这段代码很常用。

    #ifndef __ABC_H__
    #define __ABC_H__

    ... ...

    #endif

程序中的每个头文件都应该有这样的代码。它保证省略号代表的部分只参与编译一次。

宏定义函数

除了前面两种基本用法之外,还有一种用法也比较常见。它就是利用宏定义实现一个函数功能。

这样做有什么好处呢,我们先看一段代码。这段代码通过宏定义实现了一个函数,它具有这样的功能:当我们输入两个数时,返回较大的那一个。代码如下:

    #define MAX(a, b) ((a) > (b) ? (a) : (b))

一句话就完成了这个功能。使用起来和函数一样方便:

int main()
{
    int a = MAX(1, 2);
    return 0;
}

如果我们用函数来实现这个功能,要怎么写呢?

int max(int a, int b)
{
    return (a > b ? a : b)
}

宏定义函数的优势

  • 代码简洁,方便移植

最容易发现的就是这一点,无论什么样的函数,如果改写成宏函数都是一行代码。这样一个函数如果需要复用,我们只要复制一行代码就可以完成。可以大大提高开发效率。

  • 程序开销小

函数调用会带来额外的程序开销。在执行时需要申请一定的栈空间完成调用。而使用宏函数来实现能够节省这部分开销,提高程序执行效率。

  • 更灵活

函数定义需要函数声明,因此参数类型会被限定死。而宏定义函数没有这样的问题。MAX(a, b); 这条语句可以比较任意类型的两个参数。

  • 特殊功能

普通函数是无法将参数类型像参数一样传递的,而宏定义函数可以。下面这段代码就是个典型的例子。

#define MALLOC(n, type) ((type*)malloc((n) * sizeof(type)))

这个宏函数可以通过一个类型和长度来创建一个相应类型的数组。使用代码如下:

int main()
{
    char* pBuf = MALLOC(10, char);

    return 0;
}

这种调用是不是看起来很炫呢。

  • 看起来更牛B

这个自然不必说,别人不怎么会用的东西你会用,自然B格会很高。其实,最重要的是很多资深程序员会在自己的代码里用宏函数,如果你看不懂,那就会被无情地鄙视。

使用宏定义函数需要注意

  • 参数左右加括号

这个大家应该都注意到了,由于宏定义的特殊性。如果参数两边不加括号的话,在使用时传入了表达式会影响计算效果。这是考题中常出的题型。

  • 太长可以使用换行符

为了使宏定义函数有更强的可读性,我们希望不要把所有定义都写在同一行里。这时,可以用换行连接符来做折行。例如:

#define MALLOC(n, type) \
        ((type*)malloc((n) * sizeof(type)))

这样写虽然看起来是两行,但编译器会依然把它看做一行。利用这种方法,我们可以把任何一个普通函数改造成一个宏定义函数。

  • 宏定义函数并不一定比普通函数好

宏定义函数也有它的缺点,比如:逻辑太复杂不容易理解,代码写在一行不够清晰,等等。因此,我们在权衡是否需要宏定义函数时,重点要看是否满足下列条件:

  1. 函数逻辑简单,使用频繁;
  2. 需要发挥宏定义函数的参数扩展性;
  3. 需要根据传入的数据类型进行处理。

常用的宏定义函数

下面给出一些比较常用的宏定义函数,大家可以留着以后用在自己的项目里。

1. 得到指定地址上的一个字节

#define MEM_B(x) (*((char*) (x))) 

2. 求最大值和最小值

#define MAX(x,y) (((x) > (y)) ? (x) : (y)) 
#define MIN(x,y) (((x) < (y)) ? (x) : (y)) 

3. 得到一个field在结构体(struct)中的偏移量

#define FPOS( type, field ) \ 
           ((dword) &(( type*) 0)-> field)

4. 得到一个结构体中field所占用的字节数

#define FSIZ(type, field) sizeof(((type *) 0)->field) 

5. 将一个字母转换为大写

#define UPCASE(c) (((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c)) 

6. 返回数组元素的个数

#define ARR_SIZE(a) (sizeof((a)) / sizeof((a[0]))) 

还有很多有趣的用法,还需要自己去发现。只要你拥有想象力,宏定义函数会带给你无限的可能性。只是,要记住谨防出错。

我是天花板,让我们一起在软件开发中自我迭代。
如有任何问题,欢迎与我联系。


上一篇:21天C语言代码训练营(第十八天)
下一篇:21天C语言代码训练营(第二十天)

你可能感兴趣的:(21天C语言代码训练营(第十九天))