I have written a little bit of C, and I can read it well enough to get a general idea of what it is doing, but every time I have encountered a macro it has thrown me completely. I end up having to remember what the macro is and substitute it in my head as I read. The ones that I have encountered that were intuitive and easy to understand were always like little mini functions, so I always wondered why they weren't just functions.
我已经写了一点C,我可以很好地阅读它来大致了解它在做什么,但是每次我遇到一个宏,它就会把我完全抛诸脑后。最后,我必须记住宏是什么,并在我读的时候把它记在脑子里。我遇到的那些直观而容易理解的函数总是像小函数,所以我总是想为什么它们不只是函数。
I can understand the need to define different build types for debug or cross platform builds in the preprocessor but the ability to define arbitrary substitutions seems to be useful only to make an already difficult language even more difficult to understand.
我可以理解需要在预处理器中为调试或跨平台构建定义不同的构建类型,但是定义任意替换的能力似乎只会使本就困难的语言更加难以理解。
Why was such a complex preprocessor introduced for C? And does anyone have an example of using it that will make me understand why it still seems to be used for purposes other than simple if #debug style conditional compilations?
为什么要为C引入如此复杂的预处理器呢?有没有人举过使用它的例子,让我明白为什么它似乎仍然被用于除了简单的#debug风格的条件编译之外的目的?
Edit:
编辑:
Having read a number of answers I still just don't get it. The most common answer is to inline code. If the inline keyword doesn't do it then either it has a good reason to not do it, or the implementation needs fixing. I don't understand why a whole different mechanism is needed that means "really inline this code" (aside form the code being written before inline was around). I also don't understand the idea that was mentioned that "if its too silly to be put in a function". Surely any piece of code that takes an input and produces an output is best put in a function. I think I may not be getting it because I am not used to the micro optimisations of writing C, but the preprocessor just feels like a complex solution to a few simple problems.
读了很多答案后,我还是不明白。最常见的答案是内联代码。如果内联关键字不这样做,那么它有一个很好的理由不去做,或者实现需要修改。我不明白为什么需要一种完全不同的机制来表示“真正内联这个代码”(除了在内联之前编写的代码之外)。我也不理解上面提到的“如果把它放在函数中太愚蠢”的想法。当然,任何需要输入并产生输出的代码都最好放在一个函数中。我想我可能没有理解它,因为我不习惯编写C的微优化,但是预处理器只是感觉像是解决一些简单问题的复杂解决方案。
18 个解决方案
#1
51
I end up having to remember what the macro is and substitute it in my head as I read.
最后我必须记住宏是什么然后在我读的时候把它代入我的脑海。
That seems to reflect poorly on the naming of the macros. I would assume you wouldn't have to emulate the preprocessor if it were a log_function_entry()
macro.
这似乎反映了宏的命名不佳。如果它是一个log_function_entry()宏,那么我假定您不需要模拟预处理器。
The ones that I have encountered that were intuitive and easy to understand were always like little mini functions, so I always wondered why they werent just functions.
我遇到的那些直观而容易理解的函数总是像小函数,所以我总是想知道为什么它们不只是函数。
Usually they should be, unless they need to operate on generic parameters.
通常它们应该是,除非它们需要对通用参数进行操作。
#define max(a,b) ((a)<(b)?(b):(a))
will work on any type with an <
operator.
将使用 <运算符处理任何类型。< p>
More that just functions, macros let you perform operations using the symbols in the source file. That means you can create a new variable name, or reference the source file and line number the macro is on.
更重要的是,宏让您使用源文件中的符号执行操作。这意味着您可以创建一个新的变量名,或者引用宏所在的源文件和行号。
In C99, macros also allow you to call variadic functions such as printf
在C99中,宏还允许调用可变函数,如printf
#define log_message(guard,format,...) \
if (guard) printf("%s:%d: " format "\n", __FILE__, __LINE__,__VA_ARGS_);
log_message( foo == 7, "x %d", x)
In which the format works like printf. If the guard is true, it outputs the message along with the file and line number that printed the message. If it was a function call, it would not know the file and line you called it from, and using a vaprintf
would be a bit more work.
格式如printf。如果该保护为真,则输出消息以及打印消息的文件和行号。如果是函数调用,它将不知道调用它的文件和行,而使用vaprintf将需要更多的工作。
#2
16
This excerpt pretty much sums up my view on the matter, by comparing several ways that C
macros are used, and how to implement them in D
.
这一节选通过比较C宏使用的几种方式以及如何在D中实现它们,很好地总结了我对这个问题的看法。
复制从DigitalMars.com
Back when
C
was invented, compiler technology was primitive. Installing a text macro preprocessor onto the front end was a straightforward and easy way to add many powerful features. The increasing size & complexity of programs have illustrated that these features come with many inherent problems.D
doesn't have a preprocessor; butD
provides a more scalable means to solve the same problems.当C被发明时,编译技术还是很原始的。在前端安装一个文本宏预处理器是添加许多强大功能的简单方法。程序的不断增加和复杂性说明了这些特性带来了许多固有的问题。D没有预处理器;但是D提供了一种更可伸缩的方法来解决相同的问题。
Macros
Preprocessor macros add powerful features and flexibility to C
. But they have a downside:
预处理器宏为c增加了强大的特性和灵活性,但它们也有缺点:
- Macros have no concept of scope; they are valid from the point of definition to the end of the source. They cut a swath across .h files, nested code, etc. When
#include
'ing tens of thousands of lines of macro definitions, it becomes problematical to avoid inadvertent macro expansions. - 宏没有范围概念;它们从定义点到源的末尾都是有效的。当#包含成千上万行宏定义时,避免意外的宏扩展就成了问题。
- Macros are unknown to the debugger. Trying to debug a program with symbolic data is undermined by the debugger only knowing about macro expansions, not the macros themselves.
- 调试器不知道宏。调试器只知道宏展开而不知道宏本身,这就破坏了使用符号数据调试程序的努力。
- Macros make it impossible to tokenize source code, as an earlier macro change can arbitrarily redo tokens.
- 宏使得不可能对源代码进行标记,因为早期的宏更改可以任意重做令牌。
- The purely textual basis of macros leads to arbitrary and inconsistent usage, making code using macros error prone. (Some attempt to resolve this was introduced with templates in
C++
.) - 纯文本的宏基础导致任意和不一致的使用,使得使用宏的代码容易出错。(c++中的模板引入了一些试图解决这个问题的尝试。)
- Macros are still used to make up for deficits in the language's expressive capability, such as for "wrappers" around header files.
- 宏仍然被用来弥补语言表达能力的缺陷,比如在头文件的“包装器”。
Here's an enumeration of the common uses for macros, and the corresponding feature in D:
下面是对宏的常见用法的枚举,以及在D中对应的特性:
-
Defining literal constants:
定义文字常量:
-
The
C
Preprocessor WayC预处理器的方式
#define VALUE 5
-
The
D
WayD的方式
const int VALUE = 5;
-
-
Creating a list of values or flags:
创建一个值或标志列表:
-
The
C
Preprocessor WayC预处理器的方式
int flags: #define FLAG_X 0x1 #define FLAG_Y 0x2 #define FLAG_Z 0x4 ... flags |= FLAG_X;
-
The
D
WayD的方式
enum FLAGS { X = 0x1, Y = 0x2, Z = 0x4 }; FLAGS flags; ... flags |= FLAGS.X;
-
-
Setting function calling conventions:
设置函数调用约定:
-
The
C
Preprocessor WayC预处理器的方式
#ifndef _CRTAPI1 #define _CRTAPI1 __cdecl #endif #ifndef _CRTAPI2 #define _CRTAPI2 __cdecl #endif int _CRTAPI2 func();
-
The
D
WayD的方式
Calling conventions can be specified in blocks, so there's no need to change it for every function:
调用约定可以在block中指定,因此不需要对每个函数都进行更改:
extern (Windows) { int onefunc(); int anotherfunc(); }
-
-
Simple generic programming:
简单的泛型编程:
-
The
C
Preprocessor WayC预处理器的方式
Selecting which function to use based on text substitution:
根据文本替换选择要使用的函数:
#ifdef UNICODE int getValueW(wchar_t *p); #define getValue getValueW #else int getValueA(char *p); #define getValue getValueA #endif
-
The
D
WayD的方式
D
enables declarations of symbols that are aliases of other symbols:D允许声明作为其他符号别名的符号:
version (UNICODE) { int getValueW(wchar[] p); alias getValueW getValue; } else { int getValueA(char[] p); alias getValueA getValue; }
-
There are more examples on the DigitalMars website.
数字火星网站上还有更多的例子。
#3
12
They are a programming language (a simpler one) on top of C, so they are useful for doing metaprogramming in compile time... in other words, you can write macro code that generates C code in less lines and time that it will take writing it directly in C.
它们是C语言之上的一种编程语言(一种更简单的语言),因此它们对于在编译时进行元编程非常有用……换句话说,您可以编写用更少的行和时间生成C代码的宏代码,而直接用C编写这些代码需要更少的时间。
They are also very useful to write "function like" expressions that are "polymorphic" or "overloaded"; e.g. a max macro defined as:
它们对于编写“多态”或“重载”的“函数类”表达式也非常有用;例如,定义为:
#define max(a,b) ((a)>(b)?(a):(b))
is useful for any numeric type; and in C you could not write:
适用于任何数字类型;在C语言中,你不能这样写:
int max(int a, int b) {return a>b?a:b;}
float max(float a, float b) {return a>b?a:b;}
double max(double a, double b) {return a>b?a:b;}
...
even if you wanted, because you cannot overload functions.
即使你想要,因为你不能重载函数。
And not to mention conditional compiling and file including (that are also part of the macro language)...
更不用说条件编译和文件包含(也是宏语言的一部分)……
#4
12
Macros allow someone to modify the program behavior during compilation time. Consider this:
宏允许某人在编译期间修改程序行为。考虑一下:
- C constants allow fixing program behavior at development time
- C常量允许在开发时修复程序行为
- C variables allow modifying program behavior at execution time
- C变量允许在执行时修改程序行为。
- C macros allow modifying program behavior at compilation time
- C宏允许在编译时修改程序行为
At compilation time means that unused code won't even go into the binary and that the build process can modify the values, as long as it's integrated with the macro preprocessor. Example: make ARCH=arm (assumes forwarding macro definition as cc -DARCH=arm)
在编译时,意味着未使用的代码甚至不会进入二进制文件,构建过程可以修改这些值,只要它与宏预处理器集成即可。示例:make ARCH=arm(假设转发宏定义为cc -DARCH=arm)
Simple examples: (from glibc limits.h, define the largest value of long)
简单的例子:(来自glibc的限制)。h,定义long的最大值)
#if __WORDSIZE == 64
#define LONG_MAX 9223372036854775807L
#else
#define LONG_MAX 2147483647L
#endif
Verifies (using the #define __WORDSIZE) at compile time if we're compiling for 32 or 64 bits. With a multilib toolchain, using parameters -m32 and -m64 may automatically change bit size.
如果要编译32位或64位,则在编译时验证(使用#define __WORDSIZE)。使用一个多lib工具链,使用参数-m32和-m64可以自动更改位大小。
(POSIX version request)
(POSIX版本请求)
#define _POSIX_C_SOURCE 200809L
Requests during compilation time POSIX 2008 support. The standard library may support many (incompatible) standards but with this definition, it will provide the correct function prototypes (example: getline(), no gets(), etc.). If the library doesn't support the standard it may give an #error during compile time, instead of crashing during execution, for example.
在编译期间的请求POSIX 2008支持。标准库可能支持许多(不兼容的)标准,但是根据这个定义,它将提供正确的函数原型(例如:getline()、no get()等)。如果库不支持该标准,它可能会在编译时出现#错误,而不是在执行时崩溃。
(hardcoded path)
(硬编码路径)
#ifndef LIBRARY_PATH
#define LIBRARY_PATH "/usr/lib"
#endif
Defines, during compilation time a hardcode directory. Could be changed with -DLIBRARY_PATH=/home/user/lib, for example. If that were a const char *, how would you configure it during compilation ?
在编译期间定义硬编码目录。可以使用-DLIBRARY_PATH=/home/user/lib进行更改,例如。如果这是一个const char *,您将如何在编译期间配置它?
(pthread.h, complex definitions at compile time)
(pthread。h,编译时的复杂定义)
# define PTHREAD_MUTEX_INITIALIZER \
{ { 0, 0, 0, 0, 0, 0, { 0, 0 } } }
Large pieces of text may that otherwise wouldn't be simplified may be declared (always at compile time). It's not possible to do this with functions or constants (at compile time).
如果不这样做,可能会声明不能简化的大块文本(总是在编译时)。用函数或常量(在编译时)不能这样做。
To avoid really complicating things and to avoid suggesting poor coding styles, I'm wont give an example of code that compiles in different, incompatible, operating systems. Use your cross build system for that, but it should be clear that the preprocessor allows that without help from the build system, without breaking compilation because of absent interfaces.
为了避免让事情变得复杂,也为了避免出现糟糕的编码风格,我不会给出在不同的、不兼容的操作系统中编译代码的例子。为此使用交叉构建系统,但应该清楚的是,预处理器允许在没有构建系统帮助的情况下,不因缺少接口而中断编译。
Finally, think about the importance of conditional compilation on embedded systems, where processor speed and memory are limited and systems are very heterogeneous.
最后,考虑一下在嵌入式系统上条件编译的重要性,在嵌入式系统中,处理器速度和内存是有限的,系统是非常异构的。
Now, if you ask, is it possible to replace all macro constant definitions and function calls with proper definitions ? The answer is yes, but it won't simply make the need for changing program behavior during compilation go away. The preprocessor would still be required.
现在,如果你问,是否有可能用正确的定义替换所有宏常量定义和函数调用?答案是肯定的,但它不会简单地使在编译期间改变程序行为的需要消失。仍然需要预处理器。
#5
11
Remember that macros (and the pre-processor) come from the earliest days of C. They used to be the ONLY way to do inline 'functions' (because, of course, inline is a very recent keyword), and they are still the only way to FORCE something to be inlined.
记住,宏(和预处理器)来自c语言的早期,它们曾经是执行内联“函数”的唯一方式(当然,内联是最近才出现的关键字),而且它们仍然是迫使某些东西内联的唯一方式。
Also, macros are the only way you can do such tricks as inserting the file and line into string constants at compile time.
此外,宏是惟一一种可以在编译时将文件插入字符串常量的方法。
These days, many of the things that macros used to be the only way to do are better handled through newer mechanisms. But they still have their place, from time to time.
现在,许多宏以前是惟一的实现方式的东西都可以通过更新的机制更好地处理。但他们仍然有自己的位置,不时。
#6
8
Apart from inlining for efficiency and conditional compilation, macros can be used to raise the abstraction level of low-level C code. C doesn't really insulate you from the nitty-gritty details of memory and resource management and exact layout of data, and supports very limited forms of information hiding and other mechanisms for managing large systems. With macros, you are no longer limited to using only the base constructs in the C language: you can define your own data structures and coding constructs (including classes and templates!) while still nominally writing C!
除了内联效率和条件编译之外,还可以使用宏来提高低级C代码的抽象级别。C并没有真正将您与内存、资源管理和数据精确布局的细节隔离开来,它支持非常有限的信息隐藏形式和用于管理大型系统的其他机制。使用宏,您不再局限于仅使用C语言中的基本构造:您可以定义自己的数据结构和编码构造(包括类和模板!)
Preprocessor macros actually offer a Turing-complete language executed at compile time. One of the impressive (and slightly scary) examples of this is over on the C++ side: the Boost Preprocessor library uses the C99/C++98 preprocessor to build (relatively) safe programming constructs which are then expanded to whatever underlying declarations and code you input, whether C or C++.
预处理器宏实际上提供了编译时执行的图灵完全语言。其中一个令人印象深刻(也有点吓人)的例子是c++方面:Boost预处理器库使用C99/ c++ 98预处理器构建(相对)安全的编程结构,然后扩展到您输入的任何底层声明和代码,无论是C还是c++。
In practice, I'd recommend regarding preprocessor programming as a last resort, when you don't have the latitude to use high level constructs in safer languages. But sometimes it's good to know what you can do if your back is against the wall and the weasels are closing in...!
在实践中,我建议将预处理器编程作为最后的手段,当您没有在更安全的语言中使用高级结构时。但有时候,如果你的背靠墙,而黄鼠狼正逼近你,知道你能做些什么就好了。
#7
6
From Computer Stupidities:
从计算机的荒唐事:
I've seen this code excerpt in a lot of freeware gaming programs for UNIX:
我在很多UNIX免费游戏程序中看到过这段代码摘录:
/*
* Bit values.
*/
#define BIT_0 1
#define BIT_1 2
#define BIT_2 4
#define BIT_3 8
#define BIT_4 16
#define BIT_5 32
#define BIT_6 64
#define BIT_7 128
#define BIT_8 256
#define BIT_9 512
#define BIT_10 1024
#define BIT_11 2048
#define BIT_12 4096
#define BIT_13 8192
#define BIT_14 16384
#define BIT_15 32768
#define BIT_16 65536
#define BIT_17 131072
#define BIT_18 262144
#define BIT_19 524288
#define BIT_20 1048576
#define BIT_21 2097152
#define BIT_22 4194304
#define BIT_23 8388608
#define BIT_24 16777216
#define BIT_25 33554432
#define BIT_26 67108864
#define BIT_27 134217728
#define BIT_28 268435456
#define BIT_29 536870912
#define BIT_30 1073741824
#define BIT_31 2147483648/ * *位的值。* / # define BIT_0 1 # define BIT_1 2 # define BIT_2 4 # define BIT_3 8 # define BIT_4 16 # define BIT_5 32 # define BIT_6 64 # define BIT_7 128 # define BIT_8 256 # define BIT_9 512 # define BIT_10 1024 # define BIT_11 2048 # define BIT_12 4096 # define BIT_13 8192 # define BIT_14 16384 # define BIT_15 32768 # define BIT_16 65536 # define BIT_17 131072 # define BIT_18 262144 # define BIT_19 524288 # define BIT_20 1048576 # define BIT_21 2097152 # define BIT_22 2097152 # define BIT_23 2097152 # define BIT_24 16777216定义BIT_27 134217728 #定义BIT_28 26843554432 #定义BIT_29 536870912 #定义BIT_30 1073741824 #定义BIT_31 2147483648 #
A much easier way of achieving this is:
实现这一点的一个简单得多的方法是:
#define BIT_0 0x00000001
#define BIT_1 0x00000002
#define BIT_2 0x00000004
#define BIT_3 0x00000008
#define BIT_4 0x00000010
...
#define BIT_28 0x10000000
#define BIT_29 0x20000000
#define BIT_30 0x40000000
#define BIT_31 0x80000000定义位码0 0x00000001 #定义位码1 0x00000002 #定义位码2 0x00000004 #定义位码3 0x00000008 #定义位码4 0x00000010…定义BIT_28 0x10000000 #定义BIT_29 0x20000000 #定义BIT_30 0x40000000 #定义BIT_31 0x80000000
An easier way still is to let the compiler do the calculations:
更简单的方法是让编译器做计算:
#define BIT_0 (1)
#define BIT_1 (1 << 1)
#define BIT_2 (1 << 2)
#define BIT_3 (1 << 3)
#define BIT_4 (1 << 4)
...
#define BIT_28 (1 << 28)
#define BIT_29 (1 << 29)
#define BIT_30 (1 << 30)
#define BIT_31 (1 << 31)#定义位0(1)#定义位1(1 < 1)#定义位2(1 < 2)#定义位3(1 < 3)#定义位4(1 < 4)…#定义BIT_28(1 < 28) #定义BIT_29(1 < 29) #定义BIT_30(1 < 30) #定义BIT_31 (1 < 31)
But why go to all the trouble of defining 32 constants? The C language also has parameterized macros. All you really need is:
但是,为什么要费心去定义32个常数呢?C语言还具有参数化宏。你真正需要的是:
#define BIT(x) (1 << (x))
#定义位(x) (1 < (x))
Anyway, I wonder if guy who wrote the original code used a calculator or just computed it all out on paper.
不管怎样,我想知道,写原始代码的人是不是用了计算器,或者只是在纸上计算出来。
That's just one possible use of Macros.
这只是宏的一种可能用法。
#8
5
One of the case where macros really shine is when doing code-generation with them.
宏真正出色的一个例子是使用它们进行代码生成。
I used to work on an old C++ system that was using a plugin system with his own way to pass parameters to the plugin (Using a custom map-like structure). Some simple macros were used to be able to deal with this quirk and allowed us to use real C++ classes and functions with normal parameters in the plugins without too much problems. All the glue code being generated by macros.
我曾经在一个旧的c++系统上工作,这个系统使用一个插件系统,用他自己的方式向插件传递参数(使用一个自定义的类似地图的结构)。一些简单的宏被用来处理这个问题,并允许我们使用真正的c++类和在插件中使用正常参数的函数,而不会有太多的问题。所有由宏生成的胶水代码。
#9
5
I will add to whats already been said.
我要补充已经说过的话。
Because macros work on text substitutions they allow you do very useful things which wouldn't be possible to do using functions.
因为宏作用于文本替换,它们允许你做一些非常有用的事情,这些事情用函数是不可能做到的。
Here a few cases where macros can be really useful:
下面是一些宏非常有用的例子:
/* Get the number of elements in array 'A'. */
#define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0]))
This is a very popular and frequently used macro. This is very handy when you for example need to iterate through an array.
这是一个非常流行和经常使用的宏。这在需要迭代数组时非常方便。
int main(void)
{
int a[] = {1, 2, 3, 4, 5};
int i;
for (i = 0; i < ARRAY_LENGTH(a); ++i) {
printf("a[%d] = %d\n", i, a[i]);
}
return 0;
}
Here it doesn't matter if another programmer adds five more elements to a
in the decleration. The for
-loop will always iterate through all elements.
在这里,如果另一个程序员在解密中添加了5个元素,这并不重要。for循环总是遍历所有元素。
The C library's functions to compare memory and strings are quite ugly to use.
C库用来比较内存和字符串的函数使用起来很难看。
You write:
你写的:
char *str = "Hello, world!";
if (strcmp(str, "Hello, world!") == 0) {
/* ... */
}
or
或
char *str = "Hello, world!";
if (!strcmp(str, "Hello, world!")) {
/* ... */
}
To check if str
points to "Hello, world"
. I personally think that both these solutions look quite ugly and confusing (especially !strcmp(...)
).
检查str是否指向“Hello, world”。我个人认为这两种解决方案看起来都很丑陋和令人困惑(特别是!strcmp(…))。
Here are two neat macros some people (including I) use when they need to compare strings or memory using strcmp
/memcmp
:
这里有两个整洁的宏(包括I),当他们需要使用strcmp/memcmp来比较字符串或内存时使用:
/* Compare strings */
#define STRCMP(A, o, B) (strcmp((A), (B)) o 0)
/* Compare memory */
#define MEMCMP(A, o, B) (memcmp((A), (B)) o 0)
Now you can now write the code like this:
现在您可以这样编写代码:
char *str = "Hello, world!";
if (STRCMP(str, ==, "Hello, world!")) {
/* ... */
}
Here is the intention alot clearer!
这里的意图非常清楚!
These are cases were macros are used for things functions cannot accomplish. Macros should not be used to replace functions but they have other good uses.
这些情况是宏用于函数不能完成的事情。宏不应该用来替换函数,但是它们还有其他好的用途。
#10
4
Given the comments in your question, you may not fully appreciate is that calling a function can entail a fair amount of overhead. The parameters and key registers may have to be copied to the stack on the way in, and the stack unwound on the way out. This was particularly true of the older Intel chips. Macros let the programmer keep the abstraction of a function (almost), but avoided the costly overhead of a function call. The inline keyword is advisory, but the compiler may not always get it right. The glory and peril of 'C' is that you can usually bend the compiler to your will.
考虑到您的问题中的评论,您可能没有完全理解调用函数可能需要大量的开销。参数和密钥寄存器可能必须在输入时复制到堆栈中,在输出时释放堆栈。这一点在旧的英特尔芯片上尤为明显。宏使程序员保留了函数的抽象(几乎),但是避免了函数调用的开销。内联关键字是咨询,但编译器不一定总是正确的。“C”的优点和缺点是你通常可以让编译器服从你的意愿。
In your bread and butter, day-to-day application programming this kind of micro-optimization (avoiding function calls) is generally worse then useless, but if you are writing a time-critical function called by the kernel of an operating system, then it can make a huge difference.
在您的日常应用程序编程中,这种微优化(避免函数调用)通常比无用的更糟糕,但是如果您正在编写一个由操作系统内核调用的时间关键函数,那么它将产生巨大的差异。
#11
3
Unlike regular functions, you can do control flow (if, while, for,...) in macros. Here's an example:
与常规函数不同,您可以在宏中执行控制流(if, while, for,…)。这里有一个例子:
#include <stdio.h>
#define Loop(i,x) for(i=0; i<x; i++)
int main(int argc, char *argv[])
{
int i;
int x = 5;
Loop(i, x)
{
printf("%d", i); // Output: 01234
}
return 0;
}
#12
2
By leveraging C preprocessor's text manipulation one can construct the C equivalent of a polymorphic data structure. Using this technique we can construct a reliable toolbox of primitive data structures that can be used in any C program, since they take advantage of C syntax and not the specifics of any particular implementation.
通过利用C预处理器的文本操作,我们可以构建与多态数据结构等价的C。使用这种技术,我们可以构建一个可靠的原始数据结构工具箱,可以在任何C程序中使用,因为它们利用了C语法,而不是任何特定实现的细节。
Detailed explanation on how to use macros for managing data structure is given here - http://multi-core-dump.blogspot.com/2010/11/interesting-use-of-c-macros-polymorphic.html
关于如何使用宏来管理数据结构的详细解释如下:http://multi-core-dump.blogspot.com/2010/11/interesting-use- c-macros-polymorphic.html
#13
2
It's good for inlining code and avoiding function call overhead. As well as using it if you want to change the behaviour later without editing lots of places. It's not useful for complex things, but for simple lines of code that you want to inline, it's not bad.
它有利于内联代码和避免函数调用开销。如果您想在以后更改行为而不编辑大量的位置,也可以使用它。它对复杂的事情没有用处,但是对于简单的代码行,你想要内联,它并不坏。
#14
2
Macros let you get rid of copy-pasted fragments, which you can't eliminate in any other way.
宏允许您删除复制粘贴的片段,您无法以任何其他方式消除这些片段。
For instance (the real code, syntax of VS 2010 compiler):
例如(真实代码,VS 2010编译器的语法):
for each (auto entry in entries)
{
sciter::value item;
item.set_item("DisplayName", entry.DisplayName);
item.set_item("IsFolder", entry.IsFolder);
item.set_item("IconPath", entry.IconPath);
item.set_item("FilePath", entry.FilePath);
item.set_item("LocalName", entry.LocalName);
items.append(item);
}
This is the place where you pass a field value under the same name into a script engine. Is this copy-pasted? Yes. DisplayName
is used as a string for a script and as a field name for the compiler. Is that bad? Yes. If you refactor you code and rename LocalName
to RelativeFolderName
(as I did) and forget to do the same with the string (as I did), the script will work in a way you don't expect (in fact, in my example it depends on did you forget to rename the field in a separate script file, but if the script is used for serialization, it would be a 100% bug).
这是将同名字段值传递到脚本引擎的地方。这是充满吗?是的。DisplayName用作脚本的字符串,也用作编译器的字段名。是坏的吗?是的。如果你重构你的代码和重命名LocalName RelativeFolderName(像我一样),忘了做同样的字符串(像我一样),该脚本将在你工作别指望(事实上,在我的例子,这取决于你忘了重命名字段在一个单独的脚本文件,但是如果脚本用于序列化,这将是一个错误的100%)。
If you use a macro for this, there will be no room for the bug:
如果你用一个宏来做这个,就没有漏洞了:
for each (auto entry in entries)
{
#define STR_VALUE(arg) #arg
#define SET_ITEM(field) item.set_item(STR_VALUE(field), entry.field)
sciter::value item;
SET_ITEM(DisplayName);
SET_ITEM(IsFolder);
SET_ITEM(IconPath);
SET_ITEM(FilePath);
SET_ITEM(LocalName);
#undef SET_ITEM
#undef STR_VALUE
items.append(item);
}
Unfortunately, this opens a door for other types of bugs. You can make a typo writing the macro and will never see a spoiled code, because the compiler doesn't show how it looks after all preprocessing. Someone else could use the same name (that's why I "release" macros ASAP with #undef
). So, use it wisely. If you see another way of getting rid of copy-pasted code (such as functions), use that way. If you see that getting rid of copy-pasted code with macros isn't worth the result, keep the copy-pasted code.
不幸的是,这为其他类型的bug打开了大门。您可以编写一个拼写错误的宏,并且永远不会看到被破坏的代码,因为编译器不会显示所有预处理之后的样子。其他人可以使用相同的名称(这就是为什么我用#undef来“释放”宏的原因)。所以,明智地使用它。如果您看到另一种摆脱复制粘贴的代码(如函数)的方法,请使用这种方法。如果您看到用宏删除复制粘贴的代码不值得这样做,那么就保留复制粘贴的代码。
#15
1
One of the obvious reasons is that by using a macro, the code will be expanded at compile time, and you get a pseudo function-call without the call overhead.
其中一个显而易见的原因是,通过使用宏,代码将在编译时得到扩展,并且您可以在没有调用开销的情况下得到一个伪函数调用。
Otherwise, you can also use it for symbolic constants, so that you don't have to edit the same value in several places to change one small thing.
否则,您也可以将它用于符号常量,这样您就不必在多个地方编辑相同的值来更改一件小事。
#16
0
Macros .. for when your &#(*$& compiler just refuses to inline something.
宏…当你的&#(*$&编译器拒绝内联的时候。
That should be a motivational poster, no?
那应该是一个励志海报,不是吗?
In all seriousness, google preprocessor abuse (you may see a similar SO question as the #1 result). If I'm writing a macro that goes beyond the functionality of assert(), I usually try to see if my compiler would actually inline a similar function.
在所有严肃的情况下,谷歌预处理程序滥用(您可能会看到与第一个结果类似的问题)。如果我正在编写一个超出assert()功能的宏,我通常会尝试看看我的编译器是否会实际内联一个类似的函数。
Others will argue against using #if for conditional compilation .. they would rather you:
其他人会反对使用#if进行条件编译。他们宁愿你:
if (RUNNING_ON_VALGRIND)
rather than
而不是
#if RUNNING_ON_VALGRIND
.. for debugging purposes, since you can see the if() but not #if in a debugger. Then we dive into #ifdef vs #if.
. .出于调试目的,可以在调试器中看到if()而不是#if。然后我们深入到#ifdef和#if。
If its under 10 lines of code, try to inline it. If it can't be inlined, try to optimize it. If its too silly to be a function, make a macro.
如果少于10行代码,尝试内联。如果不能内联,尝试优化它。如果做一个函数太愚蠢,就做一个宏。
#17
0
While I'm not a big fan of macros and don't tend to write much C anymore, based on my current tasking, something like this (which could obviously have some side-effects) is convenient:
虽然我不太喜欢宏,也不太喜欢写C,但基于我目前的任务,类似这样的东西(很明显可能有一些副作用)很方便:
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
Now I haven't written anything like that in years, but 'functions' like that were all over code that I maintained earlier in my career. I guess the expansion could be considered convenient.
我已经很多年没有写过这样的东西了,但是像这样的“函数”在我职业生涯早期维护的代码中到处都是。我想扩展可以被认为是方便的。
#18
0
I didn't see anyone mentioning this so, regarding function like macros, eg:
我没看到有人提到这个,关于宏之类的函数,例如:
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#定义最小(X, Y) (X) < (Y) ?(X):(Y))
Generally it's recommended to avoid using macros when not necessary, for many reasons, readability being the main concern. So:
通常建议在不必要的时候避免使用宏,因为很多原因,可读性是主要的问题。所以:
When should you use these over a function?
什么时候应该在函数中使用这些?
Almost never, since there's a more readable alternative which is inline
, see https://www.greenend.org.uk/rjk/tech/inline.html or http://www.cplusplus.com/articles/2LywvCM9/ (the second link is a C++ page, but the point is applicable to c compilers as far as I know).
几乎从来没有,因为有一个更可读的选择是内联的,请参见https://www.greenend.org.uk/rjk/tech/inline.html或http://www.cplusplus.com/articles/2LywvCM9/(第二个链接是一个c++页面,但我知道这一点适用于C编译器)。
Now, the slight difference is that macros are handled by the pre-processor and inline is handled by the compiler, but there's no practical difference nowadays.
现在,稍微不同的是宏是由预处理器处理的,内联是由编译器处理的,但是现在没有实际的区别。
when is it appropriate to use these?
什么时候使用这些?
For small functions (two or three liners max). The goal is to gain some advantage during the run time of a program, as function like macros (and inline functions) are code replacements done during the pre-proccessing (or compilation in case of inline) and are not real functions living in memory, so there's no function call overhead (more details in the linked pages).
对于小函数(最多两个或三个衬垫)。目标是获得一些优势在程序的运行时,宏和内联函数等函数代码替换是在完成pre-proccessing(或编译的内联)和生活不是真正的函数在内存中,所以没有函数调用开销(更多细节的链接页)。
#1
51
I end up having to remember what the macro is and substitute it in my head as I read.
最后我必须记住宏是什么然后在我读的时候把它代入我的脑海。
That seems to reflect poorly on the naming of the macros. I would assume you wouldn't have to emulate the preprocessor if it were a log_function_entry()
macro.
这似乎反映了宏的命名不佳。如果它是一个log_function_entry()宏,那么我假定您不需要模拟预处理器。
The ones that I have encountered that were intuitive and easy to understand were always like little mini functions, so I always wondered why they werent just functions.
我遇到的那些直观而容易理解的函数总是像小函数,所以我总是想知道为什么它们不只是函数。
Usually they should be, unless they need to operate on generic parameters.
通常它们应该是,除非它们需要对通用参数进行操作。
#define max(a,b) ((a)<(b)?(b):(a))
will work on any type with an <
operator.
将使用 <运算符处理任何类型。< p>
More that just functions, macros let you perform operations using the symbols in the source file. That means you can create a new variable name, or reference the source file and line number the macro is on.
更重要的是,宏让您使用源文件中的符号执行操作。这意味着您可以创建一个新的变量名,或者引用宏所在的源文件和行号。
In C99, macros also allow you to call variadic functions such as printf
在C99中,宏还允许调用可变函数,如printf
#define log_message(guard,format,...) \
if (guard) printf("%s:%d: " format "\n", __FILE__, __LINE__,__VA_ARGS_);
log_message( foo == 7, "x %d", x)
In which the format works like printf. If the guard is true, it outputs the message along with the file and line number that printed the message. If it was a function call, it would not know the file and line you called it from, and using a vaprintf
would be a bit more work.
格式如printf。如果该保护为真,则输出消息以及打印消息的文件和行号。如果是函数调用,它将不知道调用它的文件和行,而使用vaprintf将需要更多的工作。
#2
16
This excerpt pretty much sums up my view on the matter, by comparing several ways that C
macros are used, and how to implement them in D
.
这一节选通过比较C宏使用的几种方式以及如何在D中实现它们,很好地总结了我对这个问题的看法。
复制从DigitalMars.com
Back when
C
was invented, compiler technology was primitive. Installing a text macro preprocessor onto the front end was a straightforward and easy way to add many powerful features. The increasing size & complexity of programs have illustrated that these features come with many inherent problems.D
doesn't have a preprocessor; butD
provides a more scalable means to solve the same problems.当C被发明时,编译技术还是很原始的。在前端安装一个文本宏预处理器是添加许多强大功能的简单方法。程序的不断增加和复杂性说明了这些特性带来了许多固有的问题。D没有预处理器;但是D提供了一种更可伸缩的方法来解决相同的问题。
Macros
Preprocessor macros add powerful features and flexibility to C
. But they have a downside:
预处理器宏为c增加了强大的特性和灵活性,但它们也有缺点:
- Macros have no concept of scope; they are valid from the point of definition to the end of the source. They cut a swath across .h files, nested code, etc. When
#include
'ing tens of thousands of lines of macro definitions, it becomes problematical to avoid inadvertent macro expansions. - 宏没有范围概念;它们从定义点到源的末尾都是有效的。当#包含成千上万行宏定义时,避免意外的宏扩展就成了问题。
- Macros are unknown to the debugger. Trying to debug a program with symbolic data is undermined by the debugger only knowing about macro expansions, not the macros themselves.
- 调试器不知道宏。调试器只知道宏展开而不知道宏本身,这就破坏了使用符号数据调试程序的努力。
- Macros make it impossible to tokenize source code, as an earlier macro change can arbitrarily redo tokens.
- 宏使得不可能对源代码进行标记,因为早期的宏更改可以任意重做令牌。
- The purely textual basis of macros leads to arbitrary and inconsistent usage, making code using macros error prone. (Some attempt to resolve this was introduced with templates in
C++
.) - 纯文本的宏基础导致任意和不一致的使用,使得使用宏的代码容易出错。(c++中的模板引入了一些试图解决这个问题的尝试。)
- Macros are still used to make up for deficits in the language's expressive capability, such as for "wrappers" around header files.
- 宏仍然被用来弥补语言表达能力的缺陷,比如在头文件的“包装器”。
Here's an enumeration of the common uses for macros, and the corresponding feature in D:
下面是对宏的常见用法的枚举,以及在D中对应的特性:
-
Defining literal constants:
定义文字常量:
-
The
C
Preprocessor WayC预处理器的方式
#define VALUE 5
-
The
D
WayD的方式
const int VALUE = 5;
-
-
Creating a list of values or flags:
创建一个值或标志列表:
-
The
C
Preprocessor WayC预处理器的方式
int flags: #define FLAG_X 0x1 #define FLAG_Y 0x2 #define FLAG_Z 0x4 ... flags |= FLAG_X;
-
The
D
WayD的方式
enum FLAGS { X = 0x1, Y = 0x2, Z = 0x4 }; FLAGS flags; ... flags |= FLAGS.X;
-
-
Setting function calling conventions:
设置函数调用约定:
-
The
C
Preprocessor WayC预处理器的方式
#ifndef _CRTAPI1 #define _CRTAPI1 __cdecl #endif #ifndef _CRTAPI2 #define _CRTAPI2 __cdecl #endif int _CRTAPI2 func();
-
The
D
WayD的方式
Calling conventions can be specified in blocks, so there's no need to change it for every function:
调用约定可以在block中指定,因此不需要对每个函数都进行更改:
extern (Windows) { int onefunc(); int anotherfunc(); }
-
-
Simple generic programming:
简单的泛型编程:
-
The
C
Preprocessor WayC预处理器的方式
Selecting which function to use based on text substitution:
根据文本替换选择要使用的函数:
#ifdef UNICODE int getValueW(wchar_t *p); #define getValue getValueW #else int getValueA(char *p); #define getValue getValueA #endif
-
The
D
WayD的方式
D
enables declarations of symbols that are aliases of other symbols:D允许声明作为其他符号别名的符号:
version (UNICODE) { int getValueW(wchar[] p); alias getValueW getValue; } else { int getValueA(char[] p); alias getValueA getValue; }
-
There are more examples on the DigitalMars website.
数字火星网站上还有更多的例子。
#3
12
They are a programming language (a simpler one) on top of C, so they are useful for doing metaprogramming in compile time... in other words, you can write macro code that generates C code in less lines and time that it will take writing it directly in C.
它们是C语言之上的一种编程语言(一种更简单的语言),因此它们对于在编译时进行元编程非常有用……换句话说,您可以编写用更少的行和时间生成C代码的宏代码,而直接用C编写这些代码需要更少的时间。
They are also very useful to write "function like" expressions that are "polymorphic" or "overloaded"; e.g. a max macro defined as:
它们对于编写“多态”或“重载”的“函数类”表达式也非常有用;例如,定义为:
#define max(a,b) ((a)>(b)?(a):(b))
is useful for any numeric type; and in C you could not write:
适用于任何数字类型;在C语言中,你不能这样写:
int max(int a, int b) {return a>b?a:b;}
float max(float a, float b) {return a>b?a:b;}
double max(double a, double b) {return a>b?a:b;}
...
even if you wanted, because you cannot overload functions.
即使你想要,因为你不能重载函数。
And not to mention conditional compiling and file including (that are also part of the macro language)...
更不用说条件编译和文件包含(也是宏语言的一部分)……
#4
12
Macros allow someone to modify the program behavior during compilation time. Consider this:
宏允许某人在编译期间修改程序行为。考虑一下:
- C constants allow fixing program behavior at development time
- C常量允许在开发时修复程序行为
- C variables allow modifying program behavior at execution time
- C变量允许在执行时修改程序行为。
- C macros allow modifying program behavior at compilation time
- C宏允许在编译时修改程序行为
At compilation time means that unused code won't even go into the binary and that the build process can modify the values, as long as it's integrated with the macro preprocessor. Example: make ARCH=arm (assumes forwarding macro definition as cc -DARCH=arm)
在编译时,意味着未使用的代码甚至不会进入二进制文件,构建过程可以修改这些值,只要它与宏预处理器集成即可。示例:make ARCH=arm(假设转发宏定义为cc -DARCH=arm)
Simple examples: (from glibc limits.h, define the largest value of long)
简单的例子:(来自glibc的限制)。h,定义long的最大值)
#if __WORDSIZE == 64
#define LONG_MAX 9223372036854775807L
#else
#define LONG_MAX 2147483647L
#endif
Verifies (using the #define __WORDSIZE) at compile time if we're compiling for 32 or 64 bits. With a multilib toolchain, using parameters -m32 and -m64 may automatically change bit size.
如果要编译32位或64位,则在编译时验证(使用#define __WORDSIZE)。使用一个多lib工具链,使用参数-m32和-m64可以自动更改位大小。
(POSIX version request)
(POSIX版本请求)
#define _POSIX_C_SOURCE 200809L
Requests during compilation time POSIX 2008 support. The standard library may support many (incompatible) standards but with this definition, it will provide the correct function prototypes (example: getline(), no gets(), etc.). If the library doesn't support the standard it may give an #error during compile time, instead of crashing during execution, for example.
在编译期间的请求POSIX 2008支持。标准库可能支持许多(不兼容的)标准,但是根据这个定义,它将提供正确的函数原型(例如:getline()、no get()等)。如果库不支持该标准,它可能会在编译时出现#错误,而不是在执行时崩溃。
(hardcoded path)
(硬编码路径)
#ifndef LIBRARY_PATH
#define LIBRARY_PATH "/usr/lib"
#endif
Defines, during compilation time a hardcode directory. Could be changed with -DLIBRARY_PATH=/home/user/lib, for example. If that were a const char *, how would you configure it during compilation ?
在编译期间定义硬编码目录。可以使用-DLIBRARY_PATH=/home/user/lib进行更改,例如。如果这是一个const char *,您将如何在编译期间配置它?
(pthread.h, complex definitions at compile time)
(pthread。h,编译时的复杂定义)
# define PTHREAD_MUTEX_INITIALIZER \
{ { 0, 0, 0, 0, 0, 0, { 0, 0 } } }
Large pieces of text may that otherwise wouldn't be simplified may be declared (always at compile time). It's not possible to do this with functions or constants (at compile time).
如果不这样做,可能会声明不能简化的大块文本(总是在编译时)。用函数或常量(在编译时)不能这样做。
To avoid really complicating things and to avoid suggesting poor coding styles, I'm wont give an example of code that compiles in different, incompatible, operating systems. Use your cross build system for that, but it should be clear that the preprocessor allows that without help from the build system, without breaking compilation because of absent interfaces.
为了避免让事情变得复杂,也为了避免出现糟糕的编码风格,我不会给出在不同的、不兼容的操作系统中编译代码的例子。为此使用交叉构建系统,但应该清楚的是,预处理器允许在没有构建系统帮助的情况下,不因缺少接口而中断编译。
Finally, think about the importance of conditional compilation on embedded systems, where processor speed and memory are limited and systems are very heterogeneous.
最后,考虑一下在嵌入式系统上条件编译的重要性,在嵌入式系统中,处理器速度和内存是有限的,系统是非常异构的。
Now, if you ask, is it possible to replace all macro constant definitions and function calls with proper definitions ? The answer is yes, but it won't simply make the need for changing program behavior during compilation go away. The preprocessor would still be required.
现在,如果你问,是否有可能用正确的定义替换所有宏常量定义和函数调用?答案是肯定的,但它不会简单地使在编译期间改变程序行为的需要消失。仍然需要预处理器。
#5
11
Remember that macros (and the pre-processor) come from the earliest days of C. They used to be the ONLY way to do inline 'functions' (because, of course, inline is a very recent keyword), and they are still the only way to FORCE something to be inlined.
记住,宏(和预处理器)来自c语言的早期,它们曾经是执行内联“函数”的唯一方式(当然,内联是最近才出现的关键字),而且它们仍然是迫使某些东西内联的唯一方式。
Also, macros are the only way you can do such tricks as inserting the file and line into string constants at compile time.
此外,宏是惟一一种可以在编译时将文件插入字符串常量的方法。
These days, many of the things that macros used to be the only way to do are better handled through newer mechanisms. But they still have their place, from time to time.
现在,许多宏以前是惟一的实现方式的东西都可以通过更新的机制更好地处理。但他们仍然有自己的位置,不时。
#6
8
Apart from inlining for efficiency and conditional compilation, macros can be used to raise the abstraction level of low-level C code. C doesn't really insulate you from the nitty-gritty details of memory and resource management and exact layout of data, and supports very limited forms of information hiding and other mechanisms for managing large systems. With macros, you are no longer limited to using only the base constructs in the C language: you can define your own data structures and coding constructs (including classes and templates!) while still nominally writing C!
除了内联效率和条件编译之外,还可以使用宏来提高低级C代码的抽象级别。C并没有真正将您与内存、资源管理和数据精确布局的细节隔离开来,它支持非常有限的信息隐藏形式和用于管理大型系统的其他机制。使用宏,您不再局限于仅使用C语言中的基本构造:您可以定义自己的数据结构和编码构造(包括类和模板!)
Preprocessor macros actually offer a Turing-complete language executed at compile time. One of the impressive (and slightly scary) examples of this is over on the C++ side: the Boost Preprocessor library uses the C99/C++98 preprocessor to build (relatively) safe programming constructs which are then expanded to whatever underlying declarations and code you input, whether C or C++.
预处理器宏实际上提供了编译时执行的图灵完全语言。其中一个令人印象深刻(也有点吓人)的例子是c++方面:Boost预处理器库使用C99/ c++ 98预处理器构建(相对)安全的编程结构,然后扩展到您输入的任何底层声明和代码,无论是C还是c++。
In practice, I'd recommend regarding preprocessor programming as a last resort, when you don't have the latitude to use high level constructs in safer languages. But sometimes it's good to know what you can do if your back is against the wall and the weasels are closing in...!
在实践中,我建议将预处理器编程作为最后的手段,当您没有在更安全的语言中使用高级结构时。但有时候,如果你的背靠墙,而黄鼠狼正逼近你,知道你能做些什么就好了。
#7
6
From Computer Stupidities:
从计算机的荒唐事:
I've seen this code excerpt in a lot of freeware gaming programs for UNIX:
我在很多UNIX免费游戏程序中看到过这段代码摘录:
/*
* Bit values.
*/
#define BIT_0 1
#define BIT_1 2
#define BIT_2 4
#define BIT_3 8
#define BIT_4 16
#define BIT_5 32
#define BIT_6 64
#define BIT_7 128
#define BIT_8 256
#define BIT_9 512
#define BIT_10 1024
#define BIT_11 2048
#define BIT_12 4096
#define BIT_13 8192
#define BIT_14 16384
#define BIT_15 32768
#define BIT_16 65536
#define BIT_17 131072
#define BIT_18 262144
#define BIT_19 524288
#define BIT_20 1048576
#define BIT_21 2097152
#define BIT_22 4194304
#define BIT_23 8388608
#define BIT_24 16777216
#define BIT_25 33554432
#define BIT_26 67108864
#define BIT_27 134217728
#define BIT_28 268435456
#define BIT_29 536870912
#define BIT_30 1073741824
#define BIT_31 2147483648/ * *位的值。* / # define BIT_0 1 # define BIT_1 2 # define BIT_2 4 # define BIT_3 8 # define BIT_4 16 # define BIT_5 32 # define BIT_6 64 # define BIT_7 128 # define BIT_8 256 # define BIT_9 512 # define BIT_10 1024 # define BIT_11 2048 # define BIT_12 4096 # define BIT_13 8192 # define BIT_14 16384 # define BIT_15 32768 # define BIT_16 65536 # define BIT_17 131072 # define BIT_18 262144 # define BIT_19 524288 # define BIT_20 1048576 # define BIT_21 2097152 # define BIT_22 2097152 # define BIT_23 2097152 # define BIT_24 16777216定义BIT_27 134217728 #定义BIT_28 26843554432 #定义BIT_29 536870912 #定义BIT_30 1073741824 #定义BIT_31 2147483648 #
A much easier way of achieving this is:
实现这一点的一个简单得多的方法是:
#define BIT_0 0x00000001
#define BIT_1 0x00000002
#define BIT_2 0x00000004
#define BIT_3 0x00000008
#define BIT_4 0x00000010
...
#define BIT_28 0x10000000
#define BIT_29 0x20000000
#define BIT_30 0x40000000
#define BIT_31 0x80000000定义位码0 0x00000001 #定义位码1 0x00000002 #定义位码2 0x00000004 #定义位码3 0x00000008 #定义位码4 0x00000010…定义BIT_28 0x10000000 #定义BIT_29 0x20000000 #定义BIT_30 0x40000000 #定义BIT_31 0x80000000
An easier way still is to let the compiler do the calculations:
更简单的方法是让编译器做计算:
#define BIT_0 (1)
#define BIT_1 (1 << 1)
#define BIT_2 (1 << 2)
#define BIT_3 (1 << 3)
#define BIT_4 (1 << 4)
...
#define BIT_28 (1 << 28)
#define BIT_29 (1 << 29)
#define BIT_30 (1 << 30)
#define BIT_31 (1 << 31)#定义位0(1)#定义位1(1 < 1)#定义位2(1 < 2)#定义位3(1 < 3)#定义位4(1 < 4)…#定义BIT_28(1 < 28) #定义BIT_29(1 < 29) #定义BIT_30(1 < 30) #定义BIT_31 (1 < 31)
But why go to all the trouble of defining 32 constants? The C language also has parameterized macros. All you really need is:
但是,为什么要费心去定义32个常数呢?C语言还具有参数化宏。你真正需要的是:
#define BIT(x) (1 << (x))
#定义位(x) (1 < (x))
Anyway, I wonder if guy who wrote the original code used a calculator or just computed it all out on paper.
不管怎样,我想知道,写原始代码的人是不是用了计算器,或者只是在纸上计算出来。
That's just one possible use of Macros.
这只是宏的一种可能用法。
#8
5
One of the case where macros really shine is when doing code-generation with them.
宏真正出色的一个例子是使用它们进行代码生成。
I used to work on an old C++ system that was using a plugin system with his own way to pass parameters to the plugin (Using a custom map-like structure). Some simple macros were used to be able to deal with this quirk and allowed us to use real C++ classes and functions with normal parameters in the plugins without too much problems. All the glue code being generated by macros.
我曾经在一个旧的c++系统上工作,这个系统使用一个插件系统,用他自己的方式向插件传递参数(使用一个自定义的类似地图的结构)。一些简单的宏被用来处理这个问题,并允许我们使用真正的c++类和在插件中使用正常参数的函数,而不会有太多的问题。所有由宏生成的胶水代码。
#9
5
I will add to whats already been said.
我要补充已经说过的话。
Because macros work on text substitutions they allow you do very useful things which wouldn't be possible to do using functions.
因为宏作用于文本替换,它们允许你做一些非常有用的事情,这些事情用函数是不可能做到的。
Here a few cases where macros can be really useful:
下面是一些宏非常有用的例子:
/* Get the number of elements in array 'A'. */
#define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0]))
This is a very popular and frequently used macro. This is very handy when you for example need to iterate through an array.
这是一个非常流行和经常使用的宏。这在需要迭代数组时非常方便。
int main(void)
{
int a[] = {1, 2, 3, 4, 5};
int i;
for (i = 0; i < ARRAY_LENGTH(a); ++i) {
printf("a[%d] = %d\n", i, a[i]);
}
return 0;
}
Here it doesn't matter if another programmer adds five more elements to a
in the decleration. The for
-loop will always iterate through all elements.
在这里,如果另一个程序员在解密中添加了5个元素,这并不重要。for循环总是遍历所有元素。
The C library's functions to compare memory and strings are quite ugly to use.
C库用来比较内存和字符串的函数使用起来很难看。
You write:
你写的:
char *str = "Hello, world!";
if (strcmp(str, "Hello, world!") == 0) {
/* ... */
}
or
或
char *str = "Hello, world!";
if (!strcmp(str, "Hello, world!")) {
/* ... */
}
To check if str
points to "Hello, world"
. I personally think that both these solutions look quite ugly and confusing (especially !strcmp(...)
).
检查str是否指向“Hello, world”。我个人认为这两种解决方案看起来都很丑陋和令人困惑(特别是!strcmp(…))。
Here are two neat macros some people (including I) use when they need to compare strings or memory using strcmp
/memcmp
:
这里有两个整洁的宏(包括I),当他们需要使用strcmp/memcmp来比较字符串或内存时使用:
/* Compare strings */
#define STRCMP(A, o, B) (strcmp((A), (B)) o 0)
/* Compare memory */
#define MEMCMP(A, o, B) (memcmp((A), (B)) o 0)
Now you can now write the code like this:
现在您可以这样编写代码:
char *str = "Hello, world!";
if (STRCMP(str, ==, "Hello, world!")) {
/* ... */
}
Here is the intention alot clearer!
这里的意图非常清楚!
These are cases were macros are used for things functions cannot accomplish. Macros should not be used to replace functions but they have other good uses.
这些情况是宏用于函数不能完成的事情。宏不应该用来替换函数,但是它们还有其他好的用途。
#10
4
Given the comments in your question, you may not fully appreciate is that calling a function can entail a fair amount of overhead. The parameters and key registers may have to be copied to the stack on the way in, and the stack unwound on the way out. This was particularly true of the older Intel chips. Macros let the programmer keep the abstraction of a function (almost), but avoided the costly overhead of a function call. The inline keyword is advisory, but the compiler may not always get it right. The glory and peril of 'C' is that you can usually bend the compiler to your will.
考虑到您的问题中的评论,您可能没有完全理解调用函数可能需要大量的开销。参数和密钥寄存器可能必须在输入时复制到堆栈中,在输出时释放堆栈。这一点在旧的英特尔芯片上尤为明显。宏使程序员保留了函数的抽象(几乎),但是避免了函数调用的开销。内联关键字是咨询,但编译器不一定总是正确的。“C”的优点和缺点是你通常可以让编译器服从你的意愿。
In your bread and butter, day-to-day application programming this kind of micro-optimization (avoiding function calls) is generally worse then useless, but if you are writing a time-critical function called by the kernel of an operating system, then it can make a huge difference.
在您的日常应用程序编程中,这种微优化(避免函数调用)通常比无用的更糟糕,但是如果您正在编写一个由操作系统内核调用的时间关键函数,那么它将产生巨大的差异。
#11
3
Unlike regular functions, you can do control flow (if, while, for,...) in macros. Here's an example:
与常规函数不同,您可以在宏中执行控制流(if, while, for,…)。这里有一个例子:
#include <stdio.h>
#define Loop(i,x) for(i=0; i<x; i++)
int main(int argc, char *argv[])
{
int i;
int x = 5;
Loop(i, x)
{
printf("%d", i); // Output: 01234
}
return 0;
}
#12
2
By leveraging C preprocessor's text manipulation one can construct the C equivalent of a polymorphic data structure. Using this technique we can construct a reliable toolbox of primitive data structures that can be used in any C program, since they take advantage of C syntax and not the specifics of any particular implementation.
通过利用C预处理器的文本操作,我们可以构建与多态数据结构等价的C。使用这种技术,我们可以构建一个可靠的原始数据结构工具箱,可以在任何C程序中使用,因为它们利用了C语法,而不是任何特定实现的细节。
Detailed explanation on how to use macros for managing data structure is given here - http://multi-core-dump.blogspot.com/2010/11/interesting-use-of-c-macros-polymorphic.html
关于如何使用宏来管理数据结构的详细解释如下:http://multi-core-dump.blogspot.com/2010/11/interesting-use- c-macros-polymorphic.html
#13
2
It's good for inlining code and avoiding function call overhead. As well as using it if you want to change the behaviour later without editing lots of places. It's not useful for complex things, but for simple lines of code that you want to inline, it's not bad.
它有利于内联代码和避免函数调用开销。如果您想在以后更改行为而不编辑大量的位置,也可以使用它。它对复杂的事情没有用处,但是对于简单的代码行,你想要内联,它并不坏。
#14
2
Macros let you get rid of copy-pasted fragments, which you can't eliminate in any other way.
宏允许您删除复制粘贴的片段,您无法以任何其他方式消除这些片段。
For instance (the real code, syntax of VS 2010 compiler):
例如(真实代码,VS 2010编译器的语法):
for each (auto entry in entries)
{
sciter::value item;
item.set_item("DisplayName", entry.DisplayName);
item.set_item("IsFolder", entry.IsFolder);
item.set_item("IconPath", entry.IconPath);
item.set_item("FilePath", entry.FilePath);
item.set_item("LocalName", entry.LocalName);
items.append(item);
}
This is the place where you pass a field value under the same name into a script engine. Is this copy-pasted? Yes. DisplayName
is used as a string for a script and as a field name for the compiler. Is that bad? Yes. If you refactor you code and rename LocalName
to RelativeFolderName
(as I did) and forget to do the same with the string (as I did), the script will work in a way you don't expect (in fact, in my example it depends on did you forget to rename the field in a separate script file, but if the script is used for serialization, it would be a 100% bug).
这是将同名字段值传递到脚本引擎的地方。这是充满吗?是的。DisplayName用作脚本的字符串,也用作编译器的字段名。是坏的吗?是的。如果你重构你的代码和重命名LocalName RelativeFolderName(像我一样),忘了做同样的字符串(像我一样),该脚本将在你工作别指望(事实上,在我的例子,这取决于你忘了重命名字段在一个单独的脚本文件,但是如果脚本用于序列化,这将是一个错误的100%)。
If you use a macro for this, there will be no room for the bug:
如果你用一个宏来做这个,就没有漏洞了:
for each (auto entry in entries)
{
#define STR_VALUE(arg) #arg
#define SET_ITEM(field) item.set_item(STR_VALUE(field), entry.field)
sciter::value item;
SET_ITEM(DisplayName);
SET_ITEM(IsFolder);
SET_ITEM(IconPath);
SET_ITEM(FilePath);
SET_ITEM(LocalName);
#undef SET_ITEM
#undef STR_VALUE
items.append(item);
}
Unfortunately, this opens a door for other types of bugs. You can make a typo writing the macro and will never see a spoiled code, because the compiler doesn't show how it looks after all preprocessing. Someone else could use the same name (that's why I "release" macros ASAP with #undef
). So, use it wisely. If you see another way of getting rid of copy-pasted code (such as functions), use that way. If you see that getting rid of copy-pasted code with macros isn't worth the result, keep the copy-pasted code.
不幸的是,这为其他类型的bug打开了大门。您可以编写一个拼写错误的宏,并且永远不会看到被破坏的代码,因为编译器不会显示所有预处理之后的样子。其他人可以使用相同的名称(这就是为什么我用#undef来“释放”宏的原因)。所以,明智地使用它。如果您看到另一种摆脱复制粘贴的代码(如函数)的方法,请使用这种方法。如果您看到用宏删除复制粘贴的代码不值得这样做,那么就保留复制粘贴的代码。
#15
1
One of the obvious reasons is that by using a macro, the code will be expanded at compile time, and you get a pseudo function-call without the call overhead.
其中一个显而易见的原因是,通过使用宏,代码将在编译时得到扩展,并且您可以在没有调用开销的情况下得到一个伪函数调用。
Otherwise, you can also use it for symbolic constants, so that you don't have to edit the same value in several places to change one small thing.
否则,您也可以将它用于符号常量,这样您就不必在多个地方编辑相同的值来更改一件小事。
#16
0
Macros .. for when your &#(*$& compiler just refuses to inline something.
宏…当你的&#(*$&编译器拒绝内联的时候。
That should be a motivational poster, no?
那应该是一个励志海报,不是吗?
In all seriousness, google preprocessor abuse (you may see a similar SO question as the #1 result). If I'm writing a macro that goes beyond the functionality of assert(), I usually try to see if my compiler would actually inline a similar function.
在所有严肃的情况下,谷歌预处理程序滥用(您可能会看到与第一个结果类似的问题)。如果我正在编写一个超出assert()功能的宏,我通常会尝试看看我的编译器是否会实际内联一个类似的函数。
Others will argue against using #if for conditional compilation .. they would rather you:
其他人会反对使用#if进行条件编译。他们宁愿你:
if (RUNNING_ON_VALGRIND)
rather than
而不是
#if RUNNING_ON_VALGRIND
.. for debugging purposes, since you can see the if() but not #if in a debugger. Then we dive into #ifdef vs #if.
. .出于调试目的,可以在调试器中看到if()而不是#if。然后我们深入到#ifdef和#if。
If its under 10 lines of code, try to inline it. If it can't be inlined, try to optimize it. If its too silly to be a function, make a macro.
如果少于10行代码,尝试内联。如果不能内联,尝试优化它。如果做一个函数太愚蠢,就做一个宏。
#17
0
While I'm not a big fan of macros and don't tend to write much C anymore, based on my current tasking, something like this (which could obviously have some side-effects) is convenient:
虽然我不太喜欢宏,也不太喜欢写C,但基于我目前的任务,类似这样的东西(很明显可能有一些副作用)很方便:
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
Now I haven't written anything like that in years, but 'functions' like that were all over code that I maintained earlier in my career. I guess the expansion could be considered convenient.
我已经很多年没有写过这样的东西了,但是像这样的“函数”在我职业生涯早期维护的代码中到处都是。我想扩展可以被认为是方便的。
#18
0
I didn't see anyone mentioning this so, regarding function like macros, eg:
我没看到有人提到这个,关于宏之类的函数,例如:
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#定义最小(X, Y) (X) < (Y) ?(X):(Y))
Generally it's recommended to avoid using macros when not necessary, for many reasons, readability being the main concern. So:
通常建议在不必要的时候避免使用宏,因为很多原因,可读性是主要的问题。所以:
When should you use these over a function?
什么时候应该在函数中使用这些?
Almost never, since there's a more readable alternative which is inline
, see https://www.greenend.org.uk/rjk/tech/inline.html or http://www.cplusplus.com/articles/2LywvCM9/ (the second link is a C++ page, but the point is applicable to c compilers as far as I know).
几乎从来没有,因为有一个更可读的选择是内联的,请参见https://www.greenend.org.uk/rjk/tech/inline.html或http://www.cplusplus.com/articles/2LywvCM9/(第二个链接是一个c++页面,但我知道这一点适用于C编译器)。
Now, the slight difference is that macros are handled by the pre-processor and inline is handled by the compiler, but there's no practical difference nowadays.
现在,稍微不同的是宏是由预处理器处理的,内联是由编译器处理的,但是现在没有实际的区别。
when is it appropriate to use these?
什么时候使用这些?
For small functions (two or three liners max). The goal is to gain some advantage during the run time of a program, as function like macros (and inline functions) are code replacements done during the pre-proccessing (or compilation in case of inline) and are not real functions living in memory, so there's no function call overhead (more details in the linked pages).
对于小函数(最多两个或三个衬垫)。目标是获得一些优势在程序的运行时,宏和内联函数等函数代码替换是在完成pre-proccessing(或编译的内联)和生活不是真正的函数在内存中,所以没有函数调用开销(更多细节的链接页)。