缺钾吃什么药| 成语什么争鸣| 旗袍搭配什么鞋子好看| 房颤是什么意思| 9月24号是什么星座| covu药片是什么药| crp是什么检查项目| 老是头疼是什么原因| 记性差是什么原因| 吃什么药能让月经推迟| 孕妇缺铁对胎儿有什么影响| 白马王子是什么意思| 235是什么意思| 迂回是什么意思| 摧残是什么意思| 怀孕第一个月吃什么对胎儿好| 惊雷是什么意思| 彷徨是什么意思| 无什么什么力| 花旗参有什么功效| 黄疸是什么意思| 煎中药用什么容器最好| 母亲节做什么| 九月二十九号是什么星座| 麦乳精是什么东西| 58岁属什么| 肾上腺挂什么科| 隼读什么| 三月是什么生肖| 睡觉开风扇有什么危害| 梦见自己鼻子流血是什么预兆| 女人下面有异味是什么原因| 儿童吃手指是什么原因| 什么啤酒度数高| 脚气用什么药膏最好| 羊肉馅饺子放什么菜| 头疗是什么| 药物流产后需要注意什么| 性冷淡吃什么药最好| 脚趾甲发白是什么原因| 蝉蛹是什么| 4月27日是什么星座| 下午五点半是什么时辰| 曹操是什么星座| 脂溢性皮炎有什么症状| 憨是什么意思| 手肿胀是什么原因| 氟哌酸又叫什么| 白头翁是什么| 做梦梦见老公出轨是什么意思| 铁锈红配什么颜色好看| 喝酒胃出血吃什么药| 6月13号是什么星座| 水命是什么意思| 翘首以盼什么意思| 红痣用什么药膏去除| 卵圆孔未闭是什么病| 老是肚子疼是什么原因| 喉咙痛看什么科| 什么的云朵| 张学友属什么生肖| 脑血栓有什么症状| 睾丸疝气有什么症状| 糖类抗原125偏高是什么意思| 过期茶叶有什么用途| 月经量少吃什么药调理| 什么是龙骨| 什么是有氧运动和无氧运动| 会厌炎吃什么药| 桃胶有什么功效| idc是什么意思| 龙男和什么生肖最配| 1987年什么命| 用字五行属什么| 梦见狮子是什么预兆| 先天性心脏病是什么原因造成的| 足赤是什么意思| 低压高吃什么降压药好| 警察为什么叫条子| 梦见自己打胎是什么意思| 什么颜色有助于睡眠| 泰迪狗长什么样子| 什么情况下需要会诊| 中午12点是什么时辰| 女人喝枸杞水有什么好处| 骨质疏松是什么症状| 破壁机什么牌子的最好| 两个马念什么字| swi是什么检查| 鸡肾炒什么配菜好吃| 什么东西倒立后会增加一半| 甘油三酯高有什么症状| 银属于五行属什么| 从容的反义词是什么| 手脚冰凉吃什么好| 男性生殖系统感染吃什么药| 反流性食管炎可以吃什么水果| 霉菌性中耳炎用什么药| 早上起来手发麻是什么原因| 胰腺不好有什么症状| 牙疼吃什么药止痛快| 月经什么时候来| 肩周炎吃什么药效果最好| 孕妇早上吃什么早餐好| 中央电视台台长什么级别| 1985年属什么生肖| 如获至宝是什么意思| 县委书记属于什么级别| 晚上睡不着觉吃什么药| 血沉是查什么| 可否是什么意思| 七月十六是什么星座| 带状疱疹用什么药好| 胃溃疡吃什么食物好| 紫涵女装属于什么档次| 老鹰的天敌是什么| 4.9是什么星座| 人肉搜索是什么| 心肌炎是什么病严重吗| 吃什么补胶原蛋白最快| 候和侯有什么区别| 回奶吃什么药| 6月5号是什么星座| 吃白糖有什么好处和坏处| 胃食管反流咳嗽吃什么药| 避孕药吃了有什么副作用| 世界上最大的湖是什么湖| 靥是什么意思| 眼角长脂肪粒是什么原因| 骏字五行属什么| 贲门不舒服有什么症状| 为的笔顺是什么| 半夜胃反酸水是什么原因| 掉以轻心是什么意思| 性病是什么病| 全身浮肿是什么病| 农历五月二十一是什么星座| ac是胎儿的什么| 夫妻分床睡意味着什么| 南京立秋吃什么| 宫寒吃什么药调理最好| 蝉什么时候出来| 一什么旋风| 喉咙痛可以吃什么水果| 脑内腔隙灶是什么意思| 口腔医学是干什么的| 舌炎吃什么药| 什么时候开始暑伏| 太阳线是什么意思| 双鱼和什么星座最配| 最难做的饭是什么| 7月12是什么星座| 双瞳电影到底讲了什么| 代谢是什么意思| 放电是什么意思| 梅五行属什么| 荔枝不能与什么一起吃| 非文念什么| 月经期能吃什么水果| 螃蟹的血是什么颜色的| 眼压高滴什么眼药水| 线差是什么意思| 1967属什么生肖| 脾肾亏虚的症状是什么| 流局是什么意思| 真维斯属于什么档次| 炎症反应性细胞改变是什么意思| 望闻问切什么意思| 炎是什么意思| 女性肾虚吃什么药| 肌肉拉伤挂什么科| 高血脂吃什么食物最好| 什么是零和博弈| 女孩叫兮兮是什么意思| 长水痘可以吃什么菜| 后背酸疼是什么原因| 咸鸭蛋不能和什么一起吃| 12月23日什么星座| 什么是无机盐| vup是什么意思| 尿微肌酐比值高是什么情况| 其可以组什么词| 心绞痛什么症状| 真性情是什么意思| 可以组什么词语| 黄芪喝多了有什么副作用| 什么的医生| 26岁属什么| cpi下降意味着什么| 赢弱什么意思| 北京生源是什么意思| 辐射对人体有什么伤害| 按摩头皮有什么好处| 词牌名什么意思| 备孕要注意些什么| 牙疼是什么原因引起的| 阳历7月份是什么星座| 阴唇痒用什么药| 孕妇缺铁性贫血对胎儿有什么影响| 抑郁症什么意思| 炀是什么意思| 做梦梦到掉牙齿是什么意思| ads是什么| 阳盛阴衰是什么意思| 地中海贫血是什么| 结婚 为什么| 重庆市长什么级别| 氯是什么| 此是什么意思| 出汗太多是什么原因| 梦见一个人说明什么| 化胡为佛是什么意思| 什么颜色招财并聚财| 金箔是什么| 什么是唐氏综合征| 喉炎吃什么药最有效| 走路脚心疼是什么原因| 什么食物增加血管弹性| 利妥昔单抗是治什么病| 厌男症的表现是什么| upi是什么意思| 脑溢血有什么症状| 蚊子为什么要吸血| 荷塘月色是什么菜| 欲是什么生肖| 三什么九什么成语| 腊月二十三是什么星座| 百日咳是什么| 心主什么| 为什么会突然头晕| 巨蟹座男和什么座最配对| 查抗体是做什么检查| 28年属什么生肖| 什么时间段买机票最便宜| 祭坛是什么意思| 15年什么婚| 腐女什么意思| 来大姨妈吃什么对身体好| 什么是花胶| 幽灵蛛为什么不能打死| 多普勒超声检查是什么| 书中自有颜如玉是什么意思| 一路顺风是什么生肖| 增强ct是什么| 耳机戴久了有什么危害| 月经来了喝红糖水有什么好处| 菩提根是什么材质| 酵母菌是什么菌| 围绝经期吃什么药调理| kb是什么| 阴道有豆腐渣用什么药| 例假来的是黑色的是什么原因| 盗汗吃什么药效果最快| 花生属于什么类| 疗愈是什么意思| 阑尾炎术后吃什么| 刻板印象是什么意思| 雷字五行属什么| 晚八点是什么时辰| 短效避孕药是什么| 笃行是什么意思| 透析到什么程度会死亡| 热射病什么症状| 感冒咳嗽可以吃什么水果| 百度Jump to content

黄河兰州段及刘家峡库区消防救援工作船项目6月试运行

From Wikibooks, open books for an open world
Previous: Networking in UNIX C Programming Next: Common practices
百度 果壳网创始人姬十三则表示,“在脱离系统学习的阶段之后,人们更多需要按需学习,即学即走。

Preprocessors are a way of making text processing with your C program before they are actually compiled. Before the actual compilation of every C program it is passed through a Preprocessor. The Preprocessor looks through the program trying to find out specific instructions called Preprocessor directives that it can understand. All Preprocessor directives begin with the # (hash) symbol. C++ compilers use the same C preprocessor.[1]

The preprocessor is a part of the compiler which performs preliminary operations (conditionally compiling code, including files etc...) to your code before the compiler sees it. These transformations are lexical, meaning that the output of the preprocessor is still text.

NOTE: Technically the output of the preprocessing phase for C consists of a sequence of tokens, rather than source text, but it is simple to output source text which is equivalent to the given token sequence, and that is commonly supported by compilers via a -E or /E option -- although command line options to C compilers aren't completely standard, many follow similar rules.

Directives

[edit | edit source]

Directives are special instructions directed to the preprocessor (preprocessor directive) or to the compiler (compiler directive) on how it should process part or all of your source code or set some flags on the final object and are used to make writing source code easier (more portable for instance) and to make the source code more understandable. Directives are handled by the preprocessor, which is either a separate program invoked by the compiler or part of the compiler itself.

#include

[edit | edit source]

C has some features as part of the language and some others as part of a standard library, which is a repository of code that is available alongside every standard-conformant C compiler. When the C compiler compiles your program it usually also links it with the standard C library. For example, on encountering a #include <stdio.h> directive, it replaces the directive with the contents of the stdio.h header file.

When you use features from the library, C requires you to declare what you would be using. The first line in the program is a preprocessing directive which should look like this:

#include <stdio.h>

The above line causes the C declarations which are in the stdio.h header to be included for use in your program. Usually this is implemented by just inserting into your program the contents of a header file called stdio.h, located in a system-dependent location. The location of such files may be described in your compiler's documentation. A list of standard C header files is listed below in the Headers table.

The stdio.h header contains various declarations for input/output (I/O) using an abstraction of I/O mechanisms called streams. For example there is an output stream object called stdout which is used to output text to the standard output, which usually displays the text on the computer screen.

If using angle brackets like the example above, the preprocessor is instructed to search for the include file along the development environment path for the standard includes.

#include "other.h"

If you use quotation marks (" "), the preprocessor is expected to search in some additional, usually user-defined, locations for the header file, and to fall back to the standard include paths only if it is not found in those additional locations. It is common for this form to include searching in the same directory as the file containing the #include directive.

NOTE: You should check the documentation of the development environment you are using for any vendor specific implementations of the #include directive.

Headers

[edit | edit source]

The C90 standard headers list:

Headers added since C90:

#pragma

[edit | edit source]

The pragma (pragmatic information) directive is part of the standard, but the meaning of any pragma depends on the software implementation of the standard that is used. The #pragma directive provides a way to request special behavior from the compiler. This directive is most useful for programs that are unusually large or that need to take advantage of the capabilities of a particular compiler.

Pragmas are used within the source program.

#pragma token(s)
  1. pragma is usually followed by a single token, which represents a command for the compiler to obey. You should check the software implementation of the C standard you intend on using for a list of the supported tokens. Not surprisingly, the set of commands that can appear in #pragma directives is different for each compiler; you'll have to consult the documentation for your compiler to see which commands it allows and what those commands do.

For instance one of the most implemented preprocessor directives, #pragma once when placed at the beginning of a header file, indicates that the file where it resides will be skipped if included several times by the preprocessor.

NOTE: Other methods exist to do this action that is commonly referred as using include guards.



#define

[edit | edit source]

Each #define preprocessor instruction defines a macro. For example,

   #define PI 3.14159265358979323846 /* pi */

A macro defined with a space immediately after the name is called a constant or literal. A macro defined with a parenthesis immediately after the name is called a function-like macro.[2]

WARNING: Preprocessor macros, although tempting, can produce quite unexpected results if not done right. Always keep in mind that macros are textual substitutions done to your source code before anything is compiled. The compiler does not know anything about the macros and never gets to see them. This can produce obscure errors, amongst other negative effects. Prefer to use language features, if there are equivalent. For example, use const int or enum instead of #defined constants).

That said, there are cases, where macros are very useful (see the debug macro below for an example).

The #define directive is used to define macros. Macros are used by the preprocessor to manipulate the program source code before it is compiled. Because preprocessor macro definitions are substituted before the compiler acts on the source code, any errors that are introduced by #define are difficult to trace.

By convention, macros defined using #define are named in uppercase. Although doing so is not a requirement, it is considered very bad practice to do otherwise. This allows the macros to be easily identified when reading the source code. (We mention many other common conventions for using #define in a later chapter, C Programming/Common practices).

Today, #define is primarily used to handle compiler and platform differences. E.g., a define might hold a constant which is the appropriate error code for a system call. The use of #define should thus be limited unless absolutely necessary; typedef statements and constant variables can often perform the same functions more safely.

Another feature of the #define command is that it can take arguments, making it rather useful as a pseudo-function creator. Consider the following code:

#define ABSOLUTE_VALUE( x ) ( ((x) < 0) ? -(x) : (x) )
...
int x = -1;
while( ABSOLUTE_VALUE( x ) ) {
...
}

It's generally a good idea to use extra parentheses when using complex macros. Notice that in the above example, the variable "x" is always within its own set of parentheses. This way, it will be evaluated in whole, before being compared to 0 or multiplied by -1. Also, the entire macro is surrounded by parentheses, to prevent it from being contaminated by other code. If you're not careful, you run the risk of having the compiler misinterpret your code.

Because of side-effects it is considered a very bad idea to use macro functions as described above.

int x = -10;
int y = ABSOLUTE_VALUE( x++ );

If ABSOLUTE_VALUE() were a real function 'x' would now have the value of '-9', but because it was an argument in a macro it was expanded twice and thus has a value of -8.

Example:

To illustrate the dangers of macros, consider this naive macro

#define MAX(a,b) a>b?a:b

and the code

i = MAX(2,3)+5;
j = MAX(3,2)+5;

Take a look at this and consider what the value after execution might be. The statements are turned into

int i = 2>3?2:3+5;
int j = 3>2?3:2+5;

Thus, after execution i=8 and j=3 instead of the expected result of i=j=8! This is why you were cautioned to use an extra set of parenthesis above, but even with these, the road is fraught with dangers. The alert reader might quickly realize that if a or b contains expressions, the definition must parenthesize every use of a,b in the macro definition, like this:

#define MAX(a,b) ((a)>(b)?(a):(b))

This works, provided a,b have no side effects. Indeed,

i = 2;
j = 3;
k = MAX(i++, j++);

would result in k=4, i=3 and j=5. This would be highly surprising to anyone expecting MAX() to behave like a function.

So what is the correct solution? The solution is not to use macro at all. A global, inline function, like this

inline int max(int a, int b) { 
  return a>b?a:b 
}

has none of the pitfalls above, but will not work with all types.

NOTE: The explicit inline declaration is not really necessary unless the definition is in a header file, since your compiler can inline functions for you (with gcc this can be done with -finline-functions or -O3). The compiler is often better than the programmer at predicting which functions are worth inlining. Also, function calls are not really expensive (they used to be).

The compiler is actually free to ignore the inline keyword. It is only a hint (except that inline is necessary in order to allow a function to be defined in a header file without generating an error message due to the function being defined in more than one translation unit).


(#, ##)

The # and ## operators are used with the #define macro. Using # causes the first argument after the # to be returned as a string in quotes. For example, the command

#define as_string( s ) # s

will make the compiler turn this command

puts( as_string( Hello World! ) ) ;

into

puts( "Hello World!" );

Using ## concatenates what's before the ## with what's after it. For example, the command

#define concatenate( x, y ) x ## y
...
int xy = 10;
...

will make the compiler turn

printf( "%d", concatenate( x, y ));

into

printf( "%d", xy);

which will, of course, display 10 to standard output.

It is possible to concatenate a macro argument with a constant prefix or suffix to obtain a valid identifier as in

#define make_function( name ) int my_ ## name (int foo) {}
make_function( bar )

which will define a function called my_bar(). But it isn't possible to integrate a macro argument into a constant string using the concatenation operator. In order to obtain such an effect, one can use the ANSI C property that two or more consecutive string constants are considered equivalent to a single string constant when encountered. Using this property, one can write

#define eat( what ) puts( "I'm eating " #what " today." )
eat( fruit )

which the macro-processor will turn into

puts( "I'm eating " "fruit" " today." )

which in turn will be interpreted by the C parser as a single string constant.

The following trick can be used to turn a numeric constants into string literals

#define num2str(x) str(x)
#define str(x) #x
#define CONST 23

puts(num2str(CONST));

This is a bit tricky, since it is expanded in 2 steps. First num2str(CONST) is replaced with str(23), which in turn is replaced with "23". This can be useful in the following example:

#ifdef DEBUG
#define debug(msg) fputs(__FILE__ ":" num2str(__LINE__) " - " msg, stderr)
#else
#define debug(msg)
#endif

This will give you a nice debug message including the file and the line where the message was issued. If DEBUG is not defined however the debugging message will completely vanish from your code. Be careful not to use this sort of construct with anything that has side effects, since this can lead to bugs, that appear and disappear depending on the compilation parameters.

macros

[edit | edit source]

Macros aren't type-checked and so they do not evaluate arguments. Also, they do not obey scope properly, but simply take the string passed to them and replace each occurrence of the macro argument in the text of the macro with the actual string for that parameter (the code is literally copied into the location it was called from).

An example on how to use a macro:

 #include <stdio.h>

 #define SLICES 8
 #define ADD(x) ( (x) / SLICES )

 int main(void) 
 {
   int a = 0, b = 10, c = 6;

   a = ADD(b + c);
   printf("%d\n", a);
   return 0;
 }

-- the result of "a" should be "2" (b + c = 16 -> passed to ADD -> 16 / SLICES -> result is "2")

NOTE:
It is usually bad practice to define macros in headers.

A macro should be defined only when it is not possible to achieve the same result with a function or some other mechanism. Some compilers are able to optimize code to where calls to small functions are replaced with inline code, negating any possible speed advantage. Using typedefs, enums, and inline (in C99) is often a better option.

One of the few situations where inline functions won't work -- so you are pretty much forced to use function-like macros instead -- is to initialize compile time constants (static initialization of structs). This happens when the arguments to the macro are literals that the compiler can optimize to another literal. [3]

#error

[edit | edit source]

The #error directive halts compilation. When one is encountered the standard specifies that the compiler should emit a diagnostic containing the remaining tokens in the directive. This is mostly used for debugging purposes.

Programmers use "#error" inside a conditional block, to immediately halt the compiler when the "#if" or "#ifdef" -- at the beginning of the block -- detects a compile-time problem. Normally the compiler skips the block (and the "#error" directive inside it) and the compilation proceeds.

  #error message

#warning

[edit | edit source]

Many compilers support a #warning directive. When one is encountered, the compiler emits a diagnostic containing the remaining tokens in the directive.

  #warning message

#undef

[edit | edit source]

The #undef directive undefines a macro. The identifier need not have been previously defined.

#if,#else,#elif,#endif (conditionals)

[edit | edit source]

The #if command checks whether a controlling conditional expression evaluates to zero or nonzero, and excludes or includes a block of code respectively. For example:

 #if 1
    /* This block will be included */
 #endif
 #if 0
    /* This block will not be included */
 #endif

The conditional expression could contain any C operator except for the assignment operators, the increment and decrement operators, the address-of operator, and the sizeof operator.

One unique operator used in preprocessing and nowhere else is the defined operator. It returns 1 if the macro name, optionally enclosed in parentheses, is currently defined; 0 if not.

The #endif command ends a block started by #if, #ifdef, or #ifndef.

The #elif command is similar to #if, except that it is used to extract one from a series of blocks of code. E.g.:

 #if /* some expression */
   :
   :
   :
 #elif /* another expression */
   :
 /* imagine many more #elifs here ... */
   :
 #else
 /* The optional #else block is selected if none of the previous #if or
    #elif blocks are selected */
   :
   :
 #endif /* The end of the #if block */

#ifdef,#ifndef

[edit | edit source]

The #ifdef command is similar to #if, except that the code block following it is selected if a macro name is defined. In this respect,

#ifdef NAME

is equivalent to

#if defined NAME


The #ifndef command is similar to #ifdef, except that the test is reversed:

#ifndef NAME

is equivalent to

#if !defined NAME

#line

[edit | edit source]

This preprocessor directive is used to set the file name and the line number of the line following the directive to new values. This is used to set the __FILE__ and __LINE__ macros.

Useful Preprocessor Macros for Debugging

[edit | edit source]

ANSI C defines some useful preprocessor macros and variables,[4][5] also called "magic constants", include:

__FILE__ => The name of the current file, as a string literal
__LINE__ => Current line of the source file, as a numeric literal
__DATE__ => Current system date, as a string
__TIME__ => Current system time, as a string
__TIMESTAMP__ => Date and time (non-standard)
__cplusplus => undefined when your C code is being compiled by a C compiler; 199711L when your C code is being compiled by a C++ compiler compliant with 1998 C++ standard.
__func__ => Current function name of the source file, as a string (part of C99)
__PRETTY_FUNCTION__ => "decorated" Current function name of the source file, as a string (in GCC; non-standard)

Compile-time assertions

[edit | edit source]

Compile-time assertions can help you debug faster than using only run-time assert() statements, because the compile-time assertions are all tested at compile time, while it is possible that a test run of a program may fail to exercise some run-time assert() statements.

Prior to the C11 standard, some people[6][7][8] defined a preprocessor macro to allow compile-time assertions, something like:

#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );

The static_assert.hpp Boost library defines a similar macro.[9]

Since C11, such macros are obsolete, as _Static_assert and its macro equivalent static_assert are standardized and built-in to the language.

X-Macros

[edit | edit source]

One little-known usage pattern of the C preprocessor is known as "X-Macros".[10][11][12][13][14] An X-Macro is a header file or macro. Commonly these use the extension ".def" instead of the traditional ".h". This file contains a list of similar macro calls, which can be referred to as "component macros". The include file is then referenced repeatedly in the following pattern. Here, the include file is "xmacro.def" and it contains a list of component macros of the style "foo(x, y, z)".

#define foo(x, y, z) doSomethingWith(x, y, z);
#include "xmacro.def"
#undef foo

#define foo(x, y, z) doSomethingElseWith(x, y, z);
#include "xmacro.def"
#undef foo

(etc...)

The most common usage of X-Macros is to establish a list of C objects and then automatically generate code for each of them. Some implementations also perform any #undefs they need inside the X-Macro, as opposed to expecting the caller to undefine them.

Common sets of objects are a set of global configuration settings, a set of members of a struct, a list of possible XML tags for converting an XML file to a quickly-traversable tree, or the body of an enum declaration; other lists are possible.

Once the X-Macro has been processed to create the list of objects, the component macros can be redefined to generate, for instance, accessor and/or mutator functions. Structure serializing and deserializing are also commonly done.

Here is an example of an X-Macro that establishes a struct and automatically creates serialize/deserialize functions. For simplicity, this example doesn't account for endianness or buffer overflows.

File star.def:

EXPAND_EXPAND_STAR_MEMBER(x, int)
EXPAND_EXPAND_STAR_MEMBER(y, int)
EXPAND_EXPAND_STAR_MEMBER(z, int)
EXPAND_EXPAND_STAR_MEMBER(radius, double)
#undef EXPAND_EXPAND_STAR_MEMBER

File star_table.c:

typedef struct {
  #define EXPAND_EXPAND_STAR_MEMBER(member, type) type member;
  #include "star.def"
  } starStruct;

void serialize_star(const starStruct *const star, unsigned char *buffer) {
  #define EXPAND_EXPAND_STAR_MEMBER(member, type) \
    memcpy(buffer, &(star->member), sizeof(star->member)); \
    buffer += sizeof(star->member);
  #include "star.def"
  }

void deserialize_star(starStruct *const star, const unsigned char *buffer) {
  #define EXPAND_EXPAND_STAR_MEMBER(member, type) \
    memcpy(&(star->member), buffer, sizeof(star->member)); \
    buffer += sizeof(star->member);
  #include "star.def"
  }

Handlers for individual data types may be created and accessed using token concatenation ("##") and quoting ("#") operators. For example, the following might be added to the above code:

#define print_int(val)    printf("%d", val)
#define print_double(val) printf("%g", val)

void print_star(const starStruct *const star) {
  /* print_##type will be replaced with print_int or print_double */
  #define EXPAND_EXPAND_STAR_MEMBER(member, type) \
    printf("%s: ", #member); \
    print_##type(star->member); \
    printf("\n");
  #include "star.def"
  }

Note that in this example you can also avoid the creation of separate handler functions for each datatype in this example by defining the print format for each supported type, with the additional benefit of reducing the expansion code produced by this header file:

#define FORMAT_(type) FORMAT_##type
#define FORMAT_int    "%d"
#define FORMAT_double "%g"

void print_star(const starStruct *const star) {
  /* FORMAT_(type) will be replaced with FORMAT_int or FORMAT_double */
  #define EXPAND_EXPAND_STAR_MEMBER(member, type) \
    printf("%s: " FORMAT_(type) "\n", #member, star->member);
  #include "star.def"
  }

The creation of a separate header file can be avoided by creating a single macro containing what would be the contents of the file. For instance, the above file "star.def" could be replaced with this macro at the beginning of:

File star_table.c:

#define EXPAND_STAR \
  EXPAND_STAR_MEMBER(x, int) \
  EXPAND_STAR_MEMBER(y, int) \
  EXPAND_STAR_MEMBER(z, int) \
  EXPAND_STAR_MEMBER(radius, double)

and then all calls to #include "star.def" could be replaced with a simple EXPAND_STAR statement. The rest of the above file would become:

typedef struct {
  #define EXPAND_STAR_MEMBER(member, type) type member;
  EXPAND_STAR
  #undef  EXPAND_STAR_MEMBER
  } starStruct;

void serialize_star(const starStruct *const star, unsigned char *buffer) {
  #define EXPAND_STAR_MEMBER(member, type) \
    memcpy(buffer, &(star->member), sizeof(star->member)); \
    buffer += sizeof(star->member);
  EXPAND_STAR
  #undef  EXPAND_STAR_MEMBER
  }

void deserialize_star(starStruct *const star, const unsigned char *buffer) {
  #define EXPAND_STAR_MEMBER(member, type) \
    memcpy(&(star->member), buffer, sizeof(star->member)); \
    buffer += sizeof(star->member);
  EXPAND_STAR
  #undef  EXPAND_STAR_MEMBER
  }

and the print handler could be added as well as:

#define print_int(val)    printf("%d", val)
#define print_double(val) printf("%g", val)

void print_star(const starStruct *const star) {
  /* print_##type will be replaced with print_int or print_double */
  #define EXPAND_STAR_MEMBER(member, type) \
    printf("%s: ", #member); \
    print_##type(star->member); \
    printf("\n");
  EXPAND_STAR
  #undef EXPAND_STAR_MEMBER
}

or as:

#define FORMAT_(type) FORMAT_##type
#define FORMAT_int    "%d"
#define FORMAT_double "%g"

void print_star(const starStruct *const star) {
  /* FORMAT_(type) will be replaced with FORMAT_int or FORMAT_double */
  #define EXPAND_STAR_MEMBER(member, type) \
    printf("%s: " FORMAT_(type) "\n", #member, star->member);
  EXPAND_STAR
  #undef EXPAND_STAR_MEMBER
  }

A variant which avoids needing to know the members of any expanded sub-macros is to accept the operators as an argument to the list macro:

File star_table.c:

/*
 Generic
 */
#define STRUCT_MEMBER(member, type, dummy) type member;

#define SERIALIZE_MEMBER(member, type, obj, buffer) \
  memcpy(buffer, &(obj->member), sizeof(obj->member)); \
  buffer += sizeof(obj->member);

#define DESERIALIZE_MEMBER(member, type, obj, buffer) \
  memcpy(&(obj->member), buffer, sizeof(obj->member)); \
  buffer += sizeof(obj->member);

#define FORMAT_(type) FORMAT_##type
#define FORMAT_int    "%d"
#define FORMAT_double "%g"

/* FORMAT_(type) will be replaced with FORMAT_int or FORMAT_double */
#define PRINT_MEMBER(member, type, obj) \
  printf("%s: " FORMAT_(type) "\n", #member, obj->member);

/*
 starStruct
 */

#define EXPAND_STAR(_, ...) \
  _(x, int, __VA_ARGS__) \
  _(y, int, __VA_ARGS__) \
  _(z, int, __VA_ARGS__) \
  _(radius, double, __VA_ARGS__)

typedef struct {
  EXPAND_STAR(STRUCT_MEMBER, )
  } starStruct;

void serialize_star(const starStruct *const star, unsigned char *buffer) {
  EXPAND_STAR(SERIALIZE_MEMBER, star, buffer)
  }

void deserialize_star(starStruct *const star, const unsigned char *buffer) {
  EXPAND_STAR(DESERIALIZE_MEMBER, star, buffer)
  }

void print_star(const starStruct *const star) {
  EXPAND_STAR(PRINT_MEMBER, star)
  }

This approach can be dangerous in that the entire macro set is always interpreted as if it was on a single source line, which could encounter compiler limits with complex component macros and/or long member lists.

This technique was reported by Lars Wirzenius[15] in a web page dated January 17, 2000, in which he gives credit to Kenneth Oksanen for "refining and developing" the technique prior to 1997. The other references describe it as a method from at least a decade before the turn of the century.


We discuss X-Macros more in a later section, Serialization and X-Macros.

  1. Understanding C++/C Preprocessor
  2. "Exploiting the Preprocessor for Fun and Profit".
  3. David Hart, Jon Reid. "9 Code Smells of Preprocessor Use". 2012.
  4. HP C Compiler Reference Manual
  5. C++ reference: Predefined preprocessor variables
  6. "Compile Time Assertions in C" by Jon Jagger 1999
  7. Pádraig Brady. "static assertion".
  8. "ternary operator with a constant (true) value?".
  9. Wikipedia: C++0x#Static assertions
  10. Wirzenius, Lars. "C Preprocessor Trick For Implementing Similar Data Types". Retrieved January 9, 2011.
  11. Meyers, Randy (1 May 2001). "The New C: X Macros". Dr. Dobb's Journal. Retrieved 5 April 2024.
  12. Beal, Stephan (22 August 2004). "Supermacros". Retrieved 27 October 2008.{{cite web}}: CS1 maint: date and year (link)
  13. Keith Schwarz. "Advanced Preprocessor Techniques". 2009. Includes "Practical Applications of the Preprocessor II: The X Macro Trick".
  14. Lundin?. "What are X macros and when to use them?" 2025.
  15. Wirzenius, Lars. C Preprocessor Trick For Implementing Similar Data Types Retrieved January 9, 2011.
Previous: Error handling C Programming Next: Common practices
吃什么帮助消化 什么是肠梗阻 鸡蛋花的花语是什么 单纯疱疹吃什么药 10月10日是什么星座
尿白蛋白高是什么原因 血糖看什么指标 手脚出汗多是什么原因 平步青云什么意思 什么是钝角
厌氧菌感染用什么药 肝郁吃什么食物好 脑膜炎有什么症状 无名指戴戒指是什么意思 hrv什么意思
肚子疼恶心想吐吃什么药 身上有红色的小红点是什么原因 甲减是什么症状 什么叫菩提 tpo是什么意思
两个月小猫吃什么食物hcv9jop4ns6r.cn 感冒有黄痰是什么原因hcv9jop3ns2r.cn 澳门车牌号是什么样子hcv8jop4ns9r.cn 阴道里面痒是什么原因hcv7jop7ns1r.cn 贫血吃什么食物kuyehao.com
乌龙是什么意思hcv8jop3ns5r.cn 想起我叫什么了吗hcv8jop7ns8r.cn 寻常疣是什么原因造成的xjhesheng.com 胸闷气短是什么原因造成的hcv9jop4ns9r.cn 内内是什么意思hcv9jop2ns1r.cn
朝野是什么意思huizhijixie.com 美的不可方物是什么意思hcv8jop9ns0r.cn 什么是应激反应hcv9jop7ns2r.cn 皮肤瘙痒用什么药hcv9jop2ns8r.cn 例假量少是什么原因inbungee.com
点完痣要注意什么hcv9jop2ns3r.cn 手痒脱皮是什么原因ff14chat.com quake是什么意思hcv8jop5ns9r.cn 为什么不能随便看手相hcv8jop5ns6r.cn 毒龙钻什么意思hcv9jop2ns8r.cn
百度