Zhonghui

每个不曾起舞的日子,都是对生命的辜负

User Tools

Site Tools


程序:c:宏

C语言中的宏

在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会把它用定义时指定的文本替换掉
惯例将宏名称每个字母采用大写,这有助于区分宏与一般的变量
出现在字符串字面量中的宏名称不会被展开,因为整个字符串字面量算作一个预处理器记号
无法通过宏展开的方式创建预处理器命令。即使宏的展开结果会生成形式上有效的命令,但预处理器不会执行它
预定义的标识符__func__可以在任一函数中使用,该标识符是表示当前函数名的字符串

没有参数的宏

定义常量的名称

#define ARRAY_SIZE 100
double data[ARRAY_SIZE];

替换文本为空

作为一种状态,可以在程序的其他位置判断宏是否定义了,从而编译时期就表现出不同的行为

  • 用于头文件中防止头文件被多次包括造成重复定义
  • 在头文件中定义变量、书写函数体定义(Header-Only Lib,在一个源文件中定义宏,像STB_Image)

带参数的宏

类函数宏

function-like macro
“形参列表”是用逗号隔开的多个标识符,它们都作为宏的形参。
当定义一个宏时,必须确保宏名称与左括号之间没有空白符。
#define 宏名称( [形参列表] ) 替换文本
#define 宏名称( [形参列表 ,] ... ) 替换文本
替换文本中所有出现的形参,应该使用括号将其包围
#define DISTANCE( x, y ) ((x)>=(y) ? (x)-(y) : (y)-(x))
d = DISTANCE( a, b+0.5 );

可选参数

C99 标准允许定义有省略号的宏,省略号必须放在参数列表的后面,以表示可选参数。你可以用可选参数来调用这类宏。

字符串化运算符

一元运算符 # 常称为字符串化运算符(stringify operator 或 stringizing operator),因为它会把宏调用时的实参转换为字符串
# 的操作数必须是宏替换文本中的形参
当形参名称出现在替换文本中,并且具有前缀 # 字符时,预处理器会把与该形参对应的实参放到一对双引号中,形成一个字符串字面量
#define printDBL( exp ) printf( #exp " = %f ", exp )
printDBL( 4 * atan(1.0)); // atan()在math.h中定义
printf( "4 * atan(1.0)" " = %f ", 4 * atan(1.0)); // 等效
printf( "4 * atan(1.0) = %f ", 4 * atan(1.0)); // 等效

记号粘贴运算符

运算符是一个二元运算符,可以出现在所有宏的替换文本中
该运算符会把左、右操作数结合在一起,作为一个记号,因此,它常常被称为记号粘贴运算符(token-pasting operator)
如果结果文本中还包含有宏名称,则预处理器会继续进行宏替换
出现在 ## 运算符前后的空白符连同 ## 运算符本身一起被删除
通常,使用 ## 运算符时,至少有一个操作数是宏的形参
#define TEXT_A "Hello, world!"
#define msg(x) puts( TEXT_ ## x )
msg(A);
 
puts( TEXT_A ); // 等效
puts( "Hello, world!" ); // 等效

在宏内使用宏

在替换实参,以及执行完 # 和 ## 运算之后,预处理器会检查操作所得的替换文本,并展开其中包含的所有宏。但是,宏不可以递归地展开:如果预处理器在 A 宏的替换文本中又遇到了 A 宏的名称,或者从嵌套在 A 宏内的 B 宏内又遇到了 A 宏的名称,那么 A 宏的名称就会无法展开。

作用域和重新定义

无法再次使用 #define 命令重新定义一个已经被定义为宏的标识符,除非重新定义所使用的替换文本与已经被定义的替换文本完全相同
// 取消定义一个宏
// 当某个宏首次遇到它的 #undef 命令时,它的作用域就会结束
// 如果没有关于该宏的 #undef 命令,那么它的作用域在该翻译单元结束时终止
#undef 宏名称
// 如果上面指定的标识符并非一个已定义的宏名称,那么预处理器会忽略这个 #undef 命令
// 即使准备取消定义的宏是带有参数的,也不需要在 #undef 命令中指定参数列表

预定义的宏

  1. __FILE__ 当前源代码的文件名
  2. __LINE__ 当前代码的行号
  3. 微软、苹果或者gcc都会定义自己的宏,方便根据平台和编译器做不同的处理
// 代码来自网络
 
// check cuda error
inline void check(cudaError_t call, const char* file, const int line)
{
    if (call != cudaSuccess)
    {
        std::cout << "cuda error: " << cudaGetErrorName(call) << std::endl;
        std::cout << "at file: " << file << ", line: " << line << std::endl;
        std::cout << cudaGetErrorString(call) << std::endl;
    }
}
 
#define CHECK(call) (check(call, __FILE__, __LINE__))
/var/www/DokuWikiStick/dokuwiki/data/pages/程序/c/宏.txt · Last modified: 2024/10/14 11:22 by zhonghui