热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【转】C宏

http:www.cs.yale.eduhomesaspnespinewikiC%282f%29Macros.htmlSeeKernighanRitchieAppendixA12.

http://www.cs.yale.edu/homes/aspnes/pinewiki/C%282f%29Macros.html

See KernighanRitchie Appendix A12.3 for full details on macro expansion in ANSI C and http://gcc.gnu.org/onlinedocs/cpp/Macros.html for documentation on what gcc supports.

The short version: the command

 

Toggle line numbers
   1 #define FOO (12)
   2 

causes any occurrence of the word FOO in your source file to be replaced by (12) by the preprocessor. To count as a word, FOO can‘t be adjacent to other alphanumeric characters, so for example FOOD will not expand to (12)D.

 

1. Macros with arguments

To create a macro with arguments, put them in parentheses separated by commas after the macro name, e.g.

 

Toggle line numbers
   1 #define Square(x) ((x)*(x))
   2 

Now if you write Square(foo) it will expand as ((foo)*(foo)). Note the heavy use of parentheses inside the macro definition to avoid trouble with operator precedence; if instead we had written

 

Toggle line numbers
   1 #define BadSquare(x) x*x
   2 

then BadSquare(3+4) would give 3+4*3+4, which evaluates to 19, which is probably not what we intended.

 

1.1. Multiple arguments

You can have multiple arguments to a macro, e.g.

 

Toggle line numbers
   1 #define Average(x,y) (((x)+(y))/2.0)
   2 

The usual caveats about using lots of parentheses apply.

 

1.2. Perils of repeating arguments

Macros can have odd effects if their arguments perform side-effects. For example, Square(++x) expands to ((++x)*(++x)); if x starts out equal to 1, this expression may evaluate to any of 2, 6, or 9 depending on when the ++ operators are evaluated, and will definitely leave 3 in x instead of the 2 the programmer probably expects. For this reason it is generally best to avoid side-effects in macro arguments, and to mark macro names (e.g. by capitalization) to clearly distinguish them from function names, where this issue doesn‘t come up.

 

1.3. Variable-length argument lists

C99 added variadic macros that may have a variable number of arguments; these are mostly useful for dealing with variadic functions (like printf) that also take a variable number of arguments.

To define a variadic macro, define a macro with arguments where the last argument is three periods: ... . The macro __VA_ARGS__ then expands to whatever arguments matched this ellipsis in the macro call.

For example:

Toggle line numbers
   1 #include 
   2 
   3 #define Warning(...) fprintf(stderr, __VA_ARGS__)
   4 
   5 int
   6 main(int argc, char **argv)
   7 {
   8     Warning("%s: this program contains no useful code\n", argv[0]);
   9     
  10     return 1;
  11 }

It is possible to mix regular arguments with ..., as long as ... comes last:

Toggle line numbers
   1 #define Useless(format, ...) printf(format, __VA_ARGS__)
   2 

 

2. Multiple macros

One macro can expand to another; for example, after defining

 

Toggle line numbers
   1 #define FOO BAR
   2 #define BAR (12)
   3 

it will be the case that FOO will expand to BAR which will then expand to (12). For obvious reasons, it is a bad idea to have a macro expansion contain the original macro name.

 

3. Macro tricks

 

3.1. Multiple expressions in a macro

Use the comma operator, e.g.

 

Toggle line numbers
   1 #define NoisyInc(x) (puts("incrementing"), (x)++)
   2 

The comma operator evaluates both of its operands and returns the value of the one on the right-hand side.

You can also choose between alternatives using the ternary ?: operator, as in

 

Toggle line numbers
   1 #define Max(a,b) ((a) > (b) ? (a) : (b))
   2 

(but see the warning about repeated parameters above).

 

3.2. Non-syntactic macros

Suppose you get tired of writing

 

Toggle line numbers
   1     for(i = 0; i <n; i++) ...

all the time. In principle, you can write a macro

 

Toggle line numbers
   1 #define UpTo(i, n) for((i) = 0; (i) <(n); (i)++)
   2 

and then write

 

Toggle line numbers
   1     UpTo(i, 10) ...

in place of your former for loop headers. This is generally a good way to make your code completely unreadable. Such macros are called non-syntactic because they allow code that doesn‘t look like syntactically correct C.

Sometimes, however, it makes sense to use non-syntactic macros when you want something that writes to a variable without having to pass it to a function as a pointer. An example might be something like this malloc wrapper:

 

Toggle line numbers
   1 #define TestMalloc(x) ((x) = malloc(sizeof(*x)), assert(x))
   2 

(Strictly speaking, this is probably more of a "non-semantic" macro.)

Whether the confusion of having a non-syntactic macro is worth the gain in safety or code-writing speed is a judgment call that can only be made after long and painful experience. If in doubt, it‘s probably best not to do it.

 

3.3. Multiple statements in one macro

If you want to write a macro that looks like a function call but contains multiple statements, the correct way to do it is like

 

Toggle line numbers
   1 #define HiHi() do { puts("hi"); puts("hi"); } while(0)
   2 

This can safely be used in place of single statements, like this:

 

Toggle line numbers
   1     if(friendly) 
   2         HiHi();
   3     else
   4         snarl();

Note that no construct except do..while will work here; just using braces will cause trouble with the semicolon before the else, and no other compound statement besides do..while expects to be followed by a semicolon in this way.

 

3.4. String expansion

Let‘s rewrite NoisyInc to include the variable name:

 

Toggle line numbers
   1 #define BadNoisyInc2(x) (puts("Incrementing x"), x++)
   2 

Will this do what we want? No. The C preprocessor is smart enough not to expand macro parameters inside strings, so BadNoisyInc2(y) will expand to (puts("Incrementing x"), y++). Instead, we have to write

 

Toggle line numbers
   1 #define NoisyInc2(x) (puts("Incrementing " #x), x++)
   2 

Here #x expands to whatever the value of x is wrapped in double quotes. The resulting string constant is then concatenated with the adjacent string constant according to standard C string constant concatenation rules.

To concatenate things that aren‘t strings, use the ## operator, as in

 

Toggle line numbers
   1 #define FakeArray(n) fakeArrayVariableNumber ## n
   2 

This lets you write FakeArray(12) instead of fakeArrayVariableNumber12. Note that there is generally no good reason to ever do this.

Where this feature does become useful is if you want to be able to refer to part of the source code of your program. For example, here is short program that includes a macro that prints the source code and value of an expression:

 

Toggle line numbers
   1 #include 
   2 
   3 #define PrintExpr(x) (printf("%s = %d\n", #x, (x)))
   4 
   5 int
   6 main(int argc, char **argv)
   7 {
   8     PrintExpr(2+2);
   9     return 0;
  10 }

printExpr.c

When run, this program prints

2+2 = 4

Without using a macro, there is no way to capture the text string "2+2" so we can print it.

This sort of trickery is mostly used in debugging. The assert macro is a more sophisticated version, which uses the built-in macros __FILE__ (which expands to the current source file as a quoted string) and __LINE__ (which expands to the current source line number, not quoted) to not only print out an offending expression, but also the location of it in the source.

 

3.5. Big macros

Nothing restricts a macro expansion to a single line, although you must put a backslash at the end of each line to keep it going. Here is a macro that declares a specialized sorting routine for any type that supports <:

 

Toggle line numbers
   1 #define DeclareSort(prefix, type) \
   2 static int \
   3 _DeclareSort_ ## prefix ## _Compare(const void *a, const void *b) \
   4 { \
   5     const type *aa; const type *bb; \
   6     aa = a; bb = b; \
   7     if(aa 
   8     else if(bb 
   9     else return 0; \
  10 } \
  11 \
  12 void \
  13 prefix ## _sort(type *a, int n)\
  14 { \
  15     qsort(a, sizeof(type), n, _DeclareSort_ ## prefix ## _Compare); \
  16 }
  17 

A typical use might be

 

Toggle line numbers
   1 #include 
   2 
   3 /* note: must appear outside of any function, and has no trailing semicolon */
   4 DeclareSort(int, int)
   5 
   6 int
   7 main(int argc, char **argv)
   8 {
   9     int *a;
  10     int n;
  11 
  12     ...
  13 
  14     int_sort(a, n);
  15 
  16     ...
  17 }

Do this too much and you will end up reinventing C++ templates, which are a more or less equivalent mechanism for generating polymorphic code that improve on C macros like the one above by letting you omit the backslashes.

 

4. Debugging macro expansions

One problem with using a lot of macros is that you can end up with no idea what input is actually fed to the compiler after the preprocessor is done with it. You can tell gcc to tell you how everything expands using gcc -E source_file.c. If your source file contains any #include statements it is probably a good idea to send the output of gcc -E to a file so you can scroll down past the thousands of lines of text they may generate.

 

5. Can a macro call a preprocessor command?

E.g., can you write something like

 

Toggle line numbers
   1 #define DefinePlus1(x, y)  #define x ((y)+1)
   2 

or

 

Toggle line numbers
   1 #define IncludeLib(x) #include "lib/" #x
   2 

The answer is no. C preprocessor commands are only recognized in unexpanded text. If you want self-modifying macros you will need to use a fancier macro processor like m4.

【转】C 宏


推荐阅读
  • importpymysql#一、直接连接mysql数据库'''coonpymysql.connect(host'192.168.*.*',u ... [详细]
  • 微软推出Windows Terminal Preview v0.10
    微软近期发布了Windows Terminal Preview v0.10,用户可以在微软商店或GitHub上获取这一更新。该版本在2月份发布的v0.9基础上,新增了鼠标输入和复制Pane等功能。 ... [详细]
  • CentOS 7 中 iptables 过滤表实例与 NAT 表应用详解
    在 CentOS 7 系统中,iptables 的过滤表和 NAT 表具有重要的应用价值。本文通过具体实例详细介绍了如何配置 iptables 的过滤表,包括编写脚本文件 `/usr/local/sbin/iptables.sh`,并使用 `iptables -F` 清空现有规则。此外,还深入探讨了 NAT 表的配置方法,帮助读者更好地理解和应用这些网络防火墙技术。 ... [详细]
  • Framework7:构建跨平台移动应用的高效框架
    Framework7 是一个开源免费的框架,适用于开发混合移动应用(原生与HTML混合)或iOS&Android风格的Web应用。此外,它还可以作为原型开发工具,帮助开发者快速创建应用原型。 ... [详细]
  • 本文介绍了如何使用 CMD 批处理脚本进行文件操作,包括将指定目录下的 PHP 文件重命名为 HTML 文件,并将这些文件复制到另一个目录。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 两个条件,组合控制#if($query_string~*modviewthread&t(&extra(.*)))?$)#{#set$itid$1;#rewrite^ ... [详细]
  • 本文详细介绍了DMA控制器如何通过映射表处理来自外设的请求,包括映射表的设计和实现方法。 ... [详细]
  • 解决Win10下MySQL连接问题:Navicat 2003无法连接到本地MySQL服务器(10061)
    本文介绍如何在Windows 10环境下解决Navicat 2003无法连接到本地MySQL服务器的问题,包括启动MySQL服务和检查配置文件的方法。 ... [详细]
  • 本文详细介绍了如何利用Duilib界面库开发窗体动画效果,包括基本思路和技术细节。这些方法不仅适用于Duilib,还可以扩展到其他类似的界面开发工具。 ... [详细]
  • Spark中使用map或flatMap将DataSet[A]转换为DataSet[B]时Schema变为Binary的问题及解决方案
    本文探讨了在使用Spark的map或flatMap算子将一个数据集转换为另一个数据集时,遇到的Schema变为Binary的问题,并提供了详细的解决方案。 ... [详细]
  • 第二十五天接口、多态
    1.java是面向对象的语言。设计模式:接口接口类是从java里衍生出来的,不是python原生支持的主要用于继承里多继承抽象类是python原生支持的主要用于继承里的单继承但是接 ... [详细]
  • 使用Jsoup解析并遍历HTML文档时,该库能够高效地生成一个清晰、规范的解析树,即使源HTML文档存在格式问题。Jsoup具备强大的容错能力,能够处理多种异常情况,如未闭合的标签等,确保解析结果的准确性和完整性。 ... [详细]
  • 在使用Eclipse进行调试时,如果遇到未解析的断点(unresolved breakpoint)并显示“未加载符号表,请使用‘file’命令加载目标文件以进行调试”的错误提示,这通常是因为调试器未能正确加载符号表。解决此问题的方法是通过GDB的`file`命令手动加载目标文件,以便调试器能够识别和解析断点。具体操作为在GDB命令行中输入 `(gdb) file `。这一步骤确保了调试环境能够正确访问和解析程序中的符号信息,从而实现有效的调试。 ... [详细]
  • 在 LeetCode 的“有效回文串 II”问题中,给定一个非空字符串 `s`,允许删除最多一个字符。本篇深入解析了如何判断删除一个字符后,字符串是否能成为回文串,并提出了高效的优化算法。通过详细的分析和代码实现,本文提供了多种解决方案,帮助读者更好地理解和应用这一算法。 ... [详细]
author-avatar
手机用户2602914827
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有