C语言中单井号(#)和双井号(##)在宏语句中的应用

  

  作者:华清远见讲师

  在阅读Linux内核代码过程中,特别是一些预处理指令宏的时候,会看到宏语句里会包含一些# 或者是连着的##
符号,刚接触的时候觉得很一头雾水,但这些宏语句有时候绕不开,所以为了更好地读懂这些代码,很有必要仔细学些一下这些特殊符号的含义。

  # 的功能

  #
的功能是将其后面的宏参数进行字符串化操作(stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。

  1. #define WARN_IF(exp) \

  2. do { \

  3. if (exp) \

  4. fprintf(stderr, "Warning: " #exp "\n") \

  5. } while(0)

  现在在程序中以下面的方式调用这个宏:

  1. WARN_IF (divider == 0)

  那么在编译时,上面的这句话被扩展为:

  1. do { if (divider == 0) fprintf(stderr, "Warning: " "divider == 0" "\n")
} while(0)

  这样每次divider(除数)为0的时候便会在标准错误流上输出一个提示信息。

  ## 的功能

  ## 称为连接符号(concatenator),由两个#
号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元,注意这里连接的对象是token就行,而不一定是宏的变量。

  下面举个例子来看看它们是怎样工作的。假设程序中已经定义了这样一个带参数的宏:

  1. #define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d

  现在在程序中以下面的方式调用这个宏:

  1. struct _record_type LINK_MULTIPLE(name,company,position,salary)

  那么在编译时,上面的这句话被扩展为:

  1. struct _record_type name_company_position_salary

  综合举例

  最后举一个综合# 和## 的例子。假设程序中已经定义了这样一个带参数的宏:

  1. #define paster( n ) printf( "token" #n " = %d", token##n )

  现在在程序中以下面的方式调用这个宏:

  1. int token9 = 9

  2. paster( 9 )

  那么在编译时,上面的paster( 9 ) 这句话被扩展为:

  1. printf( "token" "9" " = %d", token9 )

  注意到在这个例子中, paster(9) 中的这个9 被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9 。而#n
也被”9”所替代。 可想而知,上面程序运行的结果就是在屏幕上打印出

  1. token9=9

  相信大家理解了这些特殊符号在宏语句中的含义后,对阅读内核的代码会带来帮助。

  >>>更多优秀技术博文来自华清远见嵌入式学院

时间: 2024-08-29 22:47:00

C语言中单井号(#)和双井号(##)在宏语句中的应用的相关文章

C语言宏定义时#(井号)和##(双井号)的用法

C语言中如何使用宏C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念).下面对常遇到的宏的使用问题做了简单总结. 关于#和## 在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量 通过替换后在其左右各加上一个双引号.比如下面代码中的宏: #define WARN_IF(EXP) do{ if (EXP) fprintf(stderr, "Warning: " #EXP &quo

C/C++ 中##(两个井号)和#(一个井号)用法

##(两个井号)和#(一个井号)都是什么意思 连接符 ##(两个井号) 不知道什么符 #(一个井号) ## 连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串.但它不可以是第一个或者最后一个子串.所谓的子串(token)就是指编译器能够识别的最小语法单元.具体的定义在编译原理里有详尽的解释 #符是把传递过来的参数当成字符串进行替代. 假设程序中已经定义了这样一个带参数的宏: #define PRINT( n ) printf( "token&

python的pymysql中以表名作为变量传递进查询语句中

在我们学习使用pymysql的时候有时需要把表名当做变量传递到sql语句中去执行,然而我们的固定思维模式有时却限制了我们:如:我们知道传递变量可以用占位符%s,后面加上变量就行,在pymysql中也有同样的问题,我们在执行sql语句的时候可以把变量传递进去类似: sql = 'insert into TabName + ' value (%s %s)' cursor.execute(sql, [name,age]) 如果我们的TabName也是变量的话我们就会习惯性的这样加 sql = 'ins

SQL Server 2008中SQL增强之三:Merge(在一条语句中使用Insert,Update,Delete)

SQL Server 2008提供了一个增强的SQL命令Merge,用法参看MSDN:http://msdn.microsoft.com/zh-cn/library/bb510625.aspx 功能:根据与源表联接的结果,对目标表执行插入.更新或删除操作.例如,根据在另一个表中找到的差异在一个表中插入.更新或删除行,可以对两个表进行同步. 我们看一个例子,假如,有一总产品列表,一个分店产品列表,需要从分店添加产品时更新总产品列表. 总产品表,分店产品表结构完全一致: if OBJECT_ID('

MySQL中明知道查询结果只有一个,SQL语句中使用LIMIT 1会提高查询效率

用户使用email作为用户名登陆的话,就需要查询出email对应的一条记录.每个用户的email是唯一的. SELECT * FROM t_user WHERE email=?; 上面的语句实现了查询email对应的一条用户信息,但是由于email这一列没有加索引,会导致全表扫描,效率会很低. SELECT * FROM t_user WHERE email=? LIMIT 1; 加上LIMIT 1,只要找到了对应的一条记录,就不会继续向下扫描了,效率会大大提高. 如果email是索引的话,就不

SQL Server 2008中SQL增强之三:Merge(在一条语句中使用Insert,Update,Delete) 一条语句实现两表同步(添加、删除、修改)

MERGE 目标表 USING 源表 ON 匹配条件 WHEN MATCHED THEN 语句 WHEN NOT MATCHED THEN 语句; http://www.chinaz.com/program/2010/1014/137713.shtml

LINQ语句中的.AsEnumerable() 和 .AsQueryable()的区别【转】

在写LINQ语句的时候,往往会看到.AsEnumerable() 和 .AsQueryable() .例如: string strcon ="Data Source=.\\SQLEXPRESS;Initial Catalog=Db_Example;Persist Security Info=True;User ID=sa;Password=sa";SqlConnection con =new SqlConnection(strcon);con.Open();string strsql

在aardio的函数里try...catch语句中使用return。

try{ error("错误信息"); //使用error可以抛出一个错误 //如果error函数在try语句中,程序将不会报错,而会直接跳出try语句 } catch(e){ //如果try语句后跟catch语句则可以捕获到这个错误信息e } 以上是try...catch的语法: try语句尝试执行一个语句块,遇到错误则退出try语句块而不是中断aardio程序.如果使用了catch语句块就可以捕获异常(catch语句块是可选的). 以上是在aardio使用手册中的一段话. 也许在使

C语言中的单引号和双引号含义

C语言中的单引号和双引号含义迥异, 用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值,因此,采用ASCII字符集的编译器而言,'a'的含义与0141或97严格一致. 而用双引号引起的字符串,代表的却是一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制值为零的字符'\0'初始化.