宏定义——一种信息学竞赛中优化代码结构的工具

引言

信息学竞赛的初学者总是容易犯一些低级错误------忘记文件 IO,忘记删调试信息,变量名、函数名冲突等。同时,当竞赛选手需要写部分分或遇到一些特殊情况(比如程序需要根据不同的数据范围选择不同的计算方案)时,也需要一些工具优化代码的组织结构。这时,宏定义便成为了有效的工具。

正文

使用

‌宏定义‌是 C 语言和 C++ 中的一种预处理指令,允许程序员在源代码中定义一个标识符,以便在后续的代码中用该标识符代替特定的值或代码块。宏定义在代码编译之前会被预处理器进行处理,其作用类似于进行代码替换,有助于提高代码的可读性和可维护性。‌它的用法时这样的:

cpp 复制代码
#define <宏名>(参数) <字符串>

例如,#define int long long 能够在编译时将 int 字符串替换为 long long

常见与其配合使用的还有这些语句:

语句 含义
#ifdef <宏名> 检查宏是否被定义
#ifndef <宏名> 检查宏是否没有被定义
#endif 作为ifdefifndef的结束标志
#else 作为ifdefifndef的否定
#undef <宏名> 取消宏定义

也就是说,宏定义会对代码中的表示符进行"替换"。下面是一个使用宏定义编写 A+B 问题代码的例子:

cpp 复制代码
#include <iostream>
#define int long long // 宏定义 int
int a, b, c; // 此处将按照 long long 编译
#ifdef int
    #undef int // 宏定义 int 结束
#endif
#define ADD(x,y) x+y // 宏定义 ADD
int main() { // 此处将按照 int 编译
    #ifndef ONLINE_JUDGE
        // 如果编译时使用了 -DONLINE_JUDGE 则不会使用文件读写
        freopen("aplusb.in", "r", stdin);
        freopen("aplusb.out", "w", stdout);
    #endif
    std::cin >> a >> b;
    c = ADD(a,b); // 此处将按照 a+b 编译
    std::cout << c << std::endl;
    return 0;
}

由此可见,宏定义能够辅助编写代码,并降低因忘记文件读写等问题导致丢分的概率。善用宏定义能够在大型考试中快速调试代码,并解决一些常规方法不易解决的问题。

注意事项

  1. 运算优先级。例如,如果定义 pf(x)x*x,则 pf(a+b) 将被替换为 a+b*a+b,可能不符合预期,因此可能需要适当加入括号调整运算优先级。例如,如果想让替换结果变为 (a+b)*(a+b),定义可能需要写成 #define pf(x) (x)*(x)
  2. 传参的计算次数。例如,如果定义 #define ABS(x) (x>0)?x:-x,则 ABS((pd()))将会被替换为 ((pd())>0) ? (pd()) : -(pd()),导致 pd 被执行 \(2\) 次。如果 pd 的运算结果变化则会出错,而且程序运行时间也会大大加长。

致谢

感谢 tristiano 对文章编写的帮助。