直到C99编译器标准,它允许可以定义可变参数宏(variadic macros) C宏可变参数 可变参数宏: #define DEBUG(...) printf(__VA_ARGS__) /* 在1999...在ISO C的版本中,你不能省略可变参数,但是你却可以给它传递一个空的参数。...例如,下面的宏调用在ISO C里是非法的,因为字符串后面没有逗号,: LOG("A message") 虽然在GNU CPP中这种情况可以让你完全的忽略可变参数。...fprintf(stderr, format, ## __VA_ARGS__) 如果传入的可变参数被忽略或者为空时,##操作会将使得预处理器(preprocessor)去除掉它前面的逗号。...#endif //end for #ifdef _DEBUG 1) FILE 宏在预编译时会替换成当前的源文件名 2) LINE宏在预编译时会替换成当前的行号 3) FUNCTION宏在预编译时会替换成当前的函数名称
) 宏的名字中不允许有空格,而且必须遵循C变量命名规则 替换列表(replacement list)或叫 主体(body), (这个地方可以省略,说明只是定义了这个一个宏) 预处理器在程序中发现了宏的实例后...宏展开: 从宏变成最终文本的过程。...一般而言,预处理器发现程序中的宏后,会用它的等价替代文本代替宏,如果该 文本中 还包括宏,则继续替换这些宏。 如果宏存在与双引号内,则不予替换。...语言符号 从技术方面看,系统将 宏的 主体 当作语言符号(token)类型字符串,而不是字符型字符串。 C预处理器中的 语言符号 是宏定义主体中 单独的词(空格分割开的词)。...+ 宏中,# 与 ## 的用法 # 的作用 #的功能是将其后面的 宏参数 进行字符串化操作, 就是:宏变量替换后,左右各加一个双引号。
; 我们在写代码的时候,所有使用宏名称的地方,都可以理解为一个占位符。...在 C++ 中,这样的操作可以通过参数模板来实现,所谓的模板也是一种代码动态生成机制。当定义了一个函数模板后,根据调用者的实参,来动态产生多个函数。...所以,从代码的动态生成角度看,宏定义和 C++ 中的模板参数有点神似,只不过宏定义仅仅是代码扩展而已。...在前面的例子中,宏的参数传递的都是一些变量,而这里传递的宏参数是数据类型,通过宏的类型无关性,达到了“动态”创建结构体的目的: struct vector_int { int *data;...我记得侯杰老师在 C++ 的视屏中,利用可变参数模板这个语法,也实现了类似的功能。
1.1 闭包基础 在libco中,所有的闭包实现是宏,先学习一下给宏函数传递的参数个数如何推断出来,宏怎么可以做到。...a的类型 impl_typeof(1,a) ==> typeof_a& a; //主要用于创建一个和变量a的类型相同的引用。...impl_typeof_cpy(1,a) ==> typeof_a a; // 主要用于创建一个和变量a类型相同的变量。...__ ) 分析同上述的repeat,最后调用: #define impl_typeof( i,a,... ) typeof_##a & a; 这个例子中,会展开为: typeof_a & a; 继续往下分析...在我们调用的时候是: co_func(f, ref) { printf("hello co_func!
区别在于标识符列表使用,作为不同参数之间的分割符。每一个参数都是一个 token 化的列表。在宏中空白符只起到分割 token 的作用,空白符的多少对于预处理器是没有意义的。...Linux内核中do{...}while(0)意义: 辅助定义复杂的宏,避免引用的时候出错,如果不用{},if后面的语句只有第一条进行了判断。同时避免宏展开后“;”造成编译不通过...., b) \ do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) container_of宏 根据一个结构体变量中的成员变量来获取整个结构体变量的指针...这样cache在预取数据时就可以将分支后的执行语句放在cache中,提高cache的命中率 http://www.169it.com/article/17243108930910839727.html...在一些并发的场景中对变量进行优化有可能导致错误,需要时刻得到变量的最新值,所以用volatile强制访问一次进行更新。
区别在于标识符列表使用,作为不同参数之间的分割符。每一个参数都是一个 token 化的列表。在宏中空白符只起到分割 token 的作用,空白符的多少对于预处理器是没有意义的。...Linux内核中do{...}while(0)意义: 辅助定义复杂的宏,避免引用的时候出错,如果不用{},if后面的语句只有第一条进行了判断。同时避免宏展开后“;”造成编译不通过....(a, b) \ do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) container_of宏 根据一个结构体变量中的成员变量来获取整个结构体变量的指针...这样cache在预取数据时就可以将分支后的执行语句放在cache中,提高cache的命中率。...在一些并发的场景中对变量进行优化有可能导致错误,需要时刻得到变量的最新值,所以用volatile强制访问一次进行更新。
其实__COUNTER__是一个预定义的宏,这个值在编译过程中将从0开始计数,每次被调用时加1。因为唯一性,所以很多时候被用来构造独立的变量名称。有了上面的基础,再来看最后的实现宏就很简单了。...__FILE__返回当前文件的绝对路径,__LINE__返回展开该宏时在文件中的行数,__func__是改宏所在scope的函数名称。...__VA_ARGS__表示的是宏定义中的...中的所有剩余参数。我们之前说过可变参数将被统一处理,在这里展开的时候编译器会将__VA_ARGS__直接替换为输入中从第二个参数开始的剩余参数。...如果我们在申明这个宏的时候没有指定format参数,而直接使用参数列表,那么在使用中不写参数的NSLog()也将被匹配到这个宏中,导致编译无法通过。...另外你也不用担心展开以后式子里的NSLog会再次被自己展开,虽然展开式中NSLog也满足了我们的宏定义,但是宏的展开非常聪明,展开后会自身无限循环的情况,就不会再次被展开了。
在 print_num 函数中,先获取 count 参数地址,然后使用 &count + 1 就可以获取下一个参数的指针地址,使用指针变量 args 保存这个地址,并依次访问下一个地址,就可以直接打印传进来的各个实参值了...但打印的时候,我们还必须自己实现。在 V4.0 版本中,我们继续改进,使用 vprintf 函数实现我们的打印功能。vprintf 函数的声明在 stdio.h 头文件中。...预处理器在将宏展开时,会用变参列表替换掉宏定义中的所有 VA_ARGS 标识符。...printf("hello\n", ); 宏展开后,在第一个字符串参数的后面还有一个逗号,所以就产生了一个语法错误。我们需要对这个宏进行改进,使用宏连接符##,来避免这个语法错误。...宏连接符 ## 的主要作用就是连接两个字符串,我们在宏定义中可以使用 ## 来连接两个字符。预处理器在预处理阶段对宏展开时,会将## 两边的字符合并,并删除 ## 这两个字符。
https://blog.csdn.net/10km/article/details/80798072 在上一篇博客《c/c++:for each遍历 __VA_ARGS__ 中的每一个元素...在上篇博客中的例子中,可以利用这个遍历功能定义枚举(enum)类型。 进一步延伸思考,还可以利用这个能力定义结构体(struct)呀。...当然定义结构体与枚举类似是有区别的,结构体的每个成员不光需要成员名还需要指定数据类型。所以不能简单的使用上篇文章中的FL_FOREACH宏来实现。...我们需要能遍历成对参数的能力,这就是下面的宏FL_VA_FOREACH_PAIR,这个函数宏对__VA_ARGS__(必须是偶数个)中的参数以两个一组为单位进行遍历。...m2,char*, m3) 展开代码(eclipse显示编译器花了18步完成宏展开,so艰难): ?
在ReactiveCocoa 中,封装了很多非常实用的“宏”,使用这些“宏”为我们开发带来了很多的便利。 今天就来盘点一下RAC中的宏是如何实现的。...计算机语言如C语言或汇编语言有简单的宏系统,由编译器或汇编器的预处理器实现。C语言的宏预处理器的工作只是简单的文本搜索和替换,使用附加的文本处理语言如M4,C程序员可以获得更精巧的宏。...对于编译语言来说,所有的宏都是在预编译的时候被展开的,所以在lex进行词法扫描生成Token,词法分析过程之前,所有的宏都已经被展开完成了。 对于Xcode,预处理或者预编译阶段是可以直接查看的。...在ReactiveCocoa的宏中,作者定义了这么一些基础的宏,作为“元宏”,它们是构成之后复杂宏的基础。在分析常用宏之前,必须要先分析清楚这些元宏的具体实现。...由于宏展开是在预编译时期的,所以它在预编译时期获取参数个数的,其他非宏的方法都是在运行时获取参数个数的。
绝大多数情况下,“宏”这个词的使用暗示着将小命令或动作转化为一系列指令。 在RAC框架中,其宏定义的功能强大能帮助开发者更加快速、便捷地进行开发工作。...常用的比如:打破循环引用、以及KVO方法的属性监听等等。 ? 打破实例变量的循环引用 ? KVO属性监听 这一篇主要探究RAC中的宏定义强大之处究竟在哪。...在经常使用的宏定义RACObserve(TARGET, KEYPATH)观察KVO属性时,能够在KEYPATH中,代码预提示出指定TARGET中的属性 ?...RACObserve能够提示出当前self中存在的实例变量 metamacro_foreach_cxt(rac_weakify_,, __weak, __VA_ARGS__)代码实现: #define...metamacro_atN宏定义 metamacro_atN的宏定义,意思为截取掉宏定义中前N个元素,保留剩下的元素传入至metamacro_head(__VA_ARGS__)中 展开metamacro_head
C99标准规定,可以省略函数原型中的名称,但是如果省略名称,则需要用星号来代替省略的维数: int sum2d(int , int, int ar[*][*]); // 只能用在函数声明中 数组声明中的类型修饰符...分散代码与声明 解除了原先必须在block的第一条语句之前声明变量的限制:现在C99也和C++一样,可以在代码中随时声明变量了。 ...预处理程序的修改 变参宏(Variadic Macros) 宏可以带变元,在宏定义中用省略号(…)表示。内部预处理标识符__VA_ARGS__决定变元将在何处得到替换。...__VA_ARGS__) // 宏参数里面“...”的部份会展开到__VA_ARGS__处。 ...当变参部份为空时__VA_ARGS__会展开成空字符串,并且##前面那个逗号也会在展开时去掉。
在初学宏定义的时候,大家可能都会有这样一种感觉:就是完全替换么,太简单了。但如果你真这么想,那你就太天真了,不说自己编写宏,在Foundation框架中内置定义的许多宏要看明白也要费一番脑筋。...,变量自加的的结果也不对了,我们检查其展开的结果: CGFloat res = 3.1415926 * (r++) * (r++); 原来问题出在这里,宏在展开的时候,将参数替换了两次,由于参数本身是一个自加表达式...这确实难受,我们在进一步,比如对临时变量的名字做一些手脚,将其命名为极其不容易重复的名字,其实系统内置的一个宏就是专门用来构造唯一性变量名的:__COUNTER__,这个宏是一个计数器,在编译的时候会自动进行累加...在宏的展开过程中,如果替换列表中出现了要被展开的宏,则此宏不会被展开。...上面的展开原则提到了替换列表,宏在展开过程中会维护一个替换列表,展开的过程中需要从参数到宏本身,从外层宏到内层宏一层一层的替换,每次替换的时候都会将被替换的宏名放入维护的替换列表中,再下一轮替换中,如果再次出现替换列表中出现过的宏名
1、通常来讲,宏就是在预编译的用于替换的,因此,如果宏里面有enum,那么预编译是不会成功的。gcc,tcc等编译器可以使用-E选项。可以输出.i 文件,查看预编译的输出文件。...这就是ide流程好入门的原因啊,把很多工作都做了。另外,vscode是个好工具,集成了C/C++的插件鼠标点击到代码上就可以宏展开,微软的工具还是说不错的,毕竟“宇宙第一ide”是来自微软的vs。...另外就是英文网址搜索了,bing,google等等我的博客中之前也是收集过的。x macro好像还没有中文名称,又有点也有缺点,其中缺点就是不宜看懂,功力差点的,估计看不懂,需要多看几遍就看懂了。...其中宏定义添加了可变参数的功能,__VA_ARGS__,网上直接搜索“”可变参数宏“”,中文英文都很多的,所以这个功能国内开发人员是用的很多的。这里就不展开了。...都知道c++是有模板的,那么c语言的宏可以模仿c++的模板,至少是简易版本的模板,提高了开发的效率。
这个参数的名称使用GMOCK_MOCKER_宏组装 #define GMOCK_MOCKER_(arity, constness, Method) \ GTEST_CONCAT_TOKEN_(gmock...这样就尽可能的保证该变量在同一个文件中的唯一性。 ...EXPECT_CALL、ON_CALL宏 在介绍MOCK_METHOD系列宏是,我们发现其在我们mock的类中定义两个方法和一个变量: GMOCK_RESULT_(tn,...gmock##Method方法是在EXPECT_CALL宏中被调用的。 ...Action类中的Impl_成员变量来执行,而该Impl_变量就是在Action被创建时传入的。
大家都知道C++虚函数的机制,对于基类定义为虚函数的地方,子类假设覆写,在基类指针或者引用来指向子类的时候会实现动态绑定。...但假设指针去调用非虚函数,这个时候会调用C++的静态绑定,去推断当前的指针是什么类型,就去运行哪个类型的函数。...但在使用Qt的SLOT的时候,会出现一个问题须要注意,就是在connect的时候,你给当前的子类对象child设置了SLOT宏,但这个宏也在基类中实现过,举个样例 Class Base : public...的myConnect中,this指针表示你在当前Base类中,这个时候非常自然的去调用Base::say(),一開始可能这样写为了自己主动连接和断除比較方便,可是假设你写了继承子类,你非常自然的去覆写了...say这个函数,而且认为既然不是虚函数,没什么须要操心的,你可能会去用Child去连接别的对象,心理还在想着Base中say的实现方法(由于我记得我当初链接信号的时候写是在Base中写的,而且我如今没实用指针和引用
知识一、Debug 配置默认添加了 DEBUG=1 的宏定义 知识二、是否存在 DEBUG=1 完全由开发者决定,不受其它因素影响 如何查看当前的配置 点击项目名称,在弹出框中,点击 Edit Scheme...如何调整当前的配置 以 Run 为例,在模态视图中,点击 Build Configuration 右侧的 Debug 就可以切换配置 ?...DEBUG=1 宏定义对 @weakify 和 @strongify 有什么影响 如下:示例代码中定义了一个 block,该 block 用于判断入参 obj 是否和 foo、far 其中的任何一个对象相等并返回...理想的情况时,Xcode 依然编译错误。但是,现实往往是残酷的,Xcode 只提供了一个未使用变量的警告⚠️。 ?...移除 DEBUG 宏定义后,rac_keywordify 被定义为 #define rac_keywordify try { } @catch(...) {},经预处理器处理后,会转换为下面的代码
“它被拿来和谁比较” 此外,定义常量还可以用enum,在c++ 中尽量用const、enum替换#define定义常量,用inline 替换带参数的宏定义;但 #define 在底层编程中是必不可少的...参数宏定义的意义就很清楚了,查看下输出即可。 我们知道printf函数带有可变参数,函数式宏定义也可以带可变参数,同样是在参数列表中用...表示可变参数。...printf("x>y") : printf("x is %d but y is %d", x, y)); 在宏定义中,可变参数的部分用__VA_ARGS__表示,实参中对应...的几个参数可以看成一个参数替换到宏定义中...(四)、域运算符 C++中增加的作用域标识符 :: 用于对与局部变量同名的全局变量进行访问 用于表示类的静态成员,以后讲到类的时候再详谈 (五)、new、delete 运算符 (1)、new...extern “C” 可以实现C与C++混合编程,被extern "C" 修饰的变量和函数是按照C语言方式进行编译和链接的,即对C语言写的函数不进行改名,一般在 C的头文件中使用,如果头文件被C代码包含并用
结构体中的 data 成员是一个指针变量,需要单独为它申请一块空间才可以使用。而且在结构体使用之后,需要先释放 data,然后释放结构体指针 ams,顺序不能错。这样使用起来,是不是有点麻烦?...这里示例大家多多体会,在很多通讯类的处理场景中,常常见到这种用法。 2....不定参数的宏定义 宏定义的参数个数可以是不确定的,就像调用 printf 打印函数一样,在定义的时候,可以使用三个点(...)来表示可变参数,也可以在三个点的前面加上可变参数的名称。...看一下宏扩展之后的代码(__VA_ARGS__为空): printf("hello \n",); 看出问题了吧?在格式化字符串的后面多了一个逗号!...为了解决问题,预处理器给我们提供了一个方法:通过 ## 符号把这个多余的逗号给自动删掉。 于是宏定义改成下面这样就没有问题了。 #define debug3(format, ...)
,通过在宏主体中编写不平衡的开放括号,可以创建一个从宏主体内部开始但在宏主体外部结束的宏调用。...运算符优先级问题 在大多数宏定义示例中,每次出现的宏参数名称都带有括号,并且另一对括号通常会包围整个宏定义,这是编写宏最好的方式。...例如计算foo(z)的值时,将其保存在变量中,然后在min中使用该变量: //假设foo返回int类型 #define min(X, Y) ((X) < (Y) ?...* x) →(2 *(4 + y)) 当每个宏出现在另一个宏的定义中时,它们将被展开,但是当它间接出现在其自己的定义中时,则不会被展开。...如果按照给定的方式替换了参数,并且没有进行预扫描,则剩余的单个扫描将找到相同的宏调用并产生相同的结果。 预扫描处理在以下三种特殊情况下有大的作用。
领取专属 10元无门槛券
手把手带您无忧上云