#define宏定义在C系开发中可以说占有举足轻重的作用. The underlying framework does not have to say, in order to compile optimization and convenience, as well as cross-platform capabilities, macros are heavily used, it can be said that the bottom of development left define will be unable to move. When developing at a higher level, we put more emphasis on business logic and seem to have little use and reliance on macros. But the benefits of using macro definitions are self-explanatory, and the code readability is greatly increased while saving effort. If you want to be a developer who can write beautiful and elegant code, the macro definition is definitely an essential skill (although the macro itself may not be beautiful and elegant xd). But because macros are defined for many people, it's not like business logic to be exposed every day. Even if you can occasionally use a few macros, but also only to stay at the level of use, but not to explore what is happening behind. Some developers do have the motivation and willingness to explore, but in the point of a definition and found there are other definitions of the macro, plus the full screen is different from the usual code, both do not understand and do not change color, so the heart of trouble, anger and return. In this paper, we hope that the basic rules and techniques in the world of C-System macro definition can be expressed by a few examples in a gradual way, starting from 0, hoping that we will finally be able to understand and restore some relatively complex macros at least. Considering my own now OBJC use more, this site's readers should also mostly use OBJC, so some examples are selected from OBJC, but most of the content of this article will be C language common.
Getting StartedIf you have no idea what a macro is, you can start with a hot body. Many people in the introduction of the macro will say, the macro is very simple, is simply to find a replacement. Well, that's only half right. The macros in C are divided into two categories, object macros (object-like macro) and function macros (Function-like macro). It's relatively simple for object macros, but it's not as simple as finding replacements. Object macros are generally used to define constants, for example:
- This defines PI
- #define M_PI 3.14159265358979323846264338327950288
#define关键字表明即将开始定义一个宏, followed by the M_PI is the name of the macro, the number after the space is the content. A # define X a macro like this is relatively straightforward, and at compile time the compiler will replace X with a when the semantic analysis is considered a macro, and this process is called the expansion of the macro. For example, the above M_PI
- #define M_PI 3.14159265358979323846264338327950288
- Double r = 10.0;
- Double Circleperimeter = 2 * M_PI * r;
- = = Double Circleperimeter = 2 * 3.14159265358979323846264338327950288 * r;
- printf ("Pi is%0.7f", M_PI);
- Pi is 3.1415927
So let's start looking at another type of macro. A function macro, by definition, is a macro that behaves like a function and can accept parameters. Specifically, at the time of definition, if we follow a pair of parentheses after the macro name, the macro becomes a function macro. Start with the simplest example, such as the following function macro
- A Simple Function-like macro
- #define SELF (x) x
- NSString *name = @ "Macro Rookie";
- NSLog (@ "Hello%@", self (name));
- = = NSLog (@ "Hello%@", name);
- = Hello Macro Rookie
The thing that this macro does is that if you encounter self at compile time, and the parentheses are followed by the number of arguments in the parentheses that match the definition, then the parameters in parentheses are swapped into the defined contents, and then the original content is replaced. In this code, self accepts a name, and then replaces the entire self (name) with name. Well.. Seems to be very simple and useless, a lot of battle-hardened reading code you will certainly think that this macro is written out to sell Moe. A macro that accepts multiple parameters is certainly a cinch, for example:
- #define PLUS (x, y) × + y
- printf ("%d", PLUS (3,2));
- = = printf ("%d", 3 + 2);
- = 5
Function macros are a bit more complicated than object macros, but they look pretty simple too. Well, now that the warm-up is over, let's formally open the door to the macro.
the world of the macro, small with the universeBecause macro expansion is actually the pre-processing of the editor, it can control the source code itself and the compilation process at a higher level. And it is this feature that gives the macro a powerful function and flexibility. But there are two sides to everything, and at the cost of getting flexible, it takes a lot of time to take into account the various border situations. This may not be very understandable, but most macros (especially function macros) have their own stories behind them, and digging into these stories and designing ideas can be an interesting thing. In addition, I have always believed that learning in practice is the only way to really master knowledge, although you may be looking at this blog post you may not originally intended to write some of the macros, but we might as well begin to learn from the actual writing and mistakes in learning and digging, because only muscle memory and brain memory to work together, To reach the level of mastery. It can be said that the process of writing macros and using macros must be the process of learning and thinking deeply in making mistakes, and the next thing we need to do is reproduce this series of processes to improve progress. The first topic is, let's implement a Min macro bar: Implement a function macro, given two digital inputs, to replace it with a smaller number. For example, the value of min is 1. Huh, simple enough? Define a macro, write a name, two input, and then replace it with a comparison value. Compare values, any one of the entry-level C programming will be said Ah, so we can quickly write our first version:
- Version 1.0
- #define MIN (A) A < B? A:b
Try a bit.
- int a = MIN (n);
- = = Int a = 1 < 2? 1:2;
- printf ("%d", a);
- = 1
The output is correct, packaged and released! Take a walk once but in practical use, we soon encountered such a situation
- int a = 2 * MIN (3, 4);
- printf ("%d", a);
- = 4
It seems incredible, but we'll expand the macro to know what's going on.
- int a = 2 * MIN (3, 4);
- = = Int a = 2 * 3 < 4? 3:4;
- = = Int a = 6 < 4? 3:4;
- = = int a = 4;
Well, write the program This thing, the bug came out, the reason to know, afterwards everyone is Zhuge Liang. Since the precedence of the less than and the comparison symbols is lower, the multiplication is first calculated, the correction is very simple, and the parentheses are good.
- Version 2.0
- #define MIN (A < B) A:B)
This time 2 * MIN (3, 4) Such a formula is relaxed and happy to win. After this modification, we have greatly increased our confidence in our own macro ... Until one day an angry colleague ran to drop the keyboard and gave an example:
- int a = MIN (3, 4 < 5? 4:5);
- printf ("%d", a);
- = 4
Simple compared to three numbers and find the smallest one, it is strange that you do not provide three of the size of the macro, poor colleagues have to achieve their own 4 and 5 comparison. When you start to solve this problem, the first thing you think about is that since you are all asking for the minimum, it is possible to write min (3, Min (4, 5)). So you can readily change and find that the result becomes 3, exactly what you want. Then, beginning to wonder if you were looking at the wrong result, and changed back to the original, a 4 impressively appeared on the screen. You finally realize that things are not as simple as you think, so go back to the most primitive direct means and expand the macro.
- int a = min (3, 4 < 5 ? 4 : 5);
- // => int a = (3 < 4 < 5 ? 4 : 5 ? 3 : 4 < 5 ? 4 : 5); //want you to remember operator precedence
- => int a = (3 < (4 < 5 ? 4 : 5) ( ? 3 : 4) < 5 ? 4 : 5) //for your not too tangled, I added parentheses to this
- // => int a = (3 < 4 ? 3 : 4) < 5 ? 4 : 5)
- // = > int a = (3 < 5 ? 4 : 5)
- // => int a = 4
Find the problem, because the connection symbol and the expanded expression in the expansion of the same precedence of the operation symbol, resulting in a change in the order of calculation, in fact, and our 1.0 version of the problem is similar, or poorly considered. Then just a little more strict, 3.0 version!
- Version 3.0
- #define MIN (A) < (B)? (A): (B))
As for why the Min (3, Min (4, 5)) in the 2.0 version has no problem, can be used correctly, here as an exercise, you can try to expand yourself to see what happened. After two tragedies, you are now full of doubts about this simple macro. So you ran countless test cases and they all passed, and we seem to have solved the problem of parentheses completely, and you think that this macro will be completed. But if you really think so, then you have the pattern Tucson broken. Life is always cruel, the bug must have come. Not surprisingly, in a cloudy afternoon, we received another example of a problem.
- float a = 1.0f;
- Float B = MIN (a++, 1.5f);
- printf ("A=%f, b=%f", b);
- = a=3.000000, b=2.000000
Get the example of this problem your first reaction may be the same as me, this TM who so two goods are still in the comparison when the + +, this is a mess! But such a person will exist, such a thing will happen, you can not say that they have the wrong logic. A is 1,a++ that the value of a is used to calculate, then add 1. So in fact, this equation wants to calculate the minimum value of a and B, then a equals a plus 1: so the correct output A is 2,b to 1! Well, the eyes are all tears, let us these tortured programmers calmly unfold this formula, to see what happened this time:
- float a = 1.0f;
- Float B = MIN (a++, 1.5f);
- = = Float B = ((a++) < (1.5f)? (a++): (1.5f))
In fact, as long as the development of a step is clear, in comparison a++ and 1.5f, the first to take 1 and 1.5 comparison, and then a since the increase of 1. Then the condition comparison gets really and then triggers once a++, at this time A is already 2, so B gets 2, finally a again self-increment value is 3. The source of the error is that we expect the a++ to execute only once, but because the macro expansion causes the a++ to be executed more, it changes the expected logic. Solving this problem is not a very simple thing, and the way to use it is ingenious. We need to use a GNU C assignment extension that uses ({...}) The form. This form of statement can be similar to many scripting languages, and after sequential execution, the assignment of the last expression is returned. As a simple example, the following code executes after the value of a is 3, and B and C exist only in a code field that is bounded by braces
- int a = ({
- int b = 1;
- int c = 2;
- B + C;
- });
- = A is 3
With this extension, we can do a lot of things we couldn't do before. For example, to completely solve the problem of min macro definition, but also is the GNU C in the standard notation of min
- Gnuc MIN
- #define MIN (b) ({__typeof__ (a) __a = (a); __typeof__ (b) __b = (b); __a < __b? __a: __b;})
Here, three statements are defined, __a and __b are declared with the input type, and assigned with input, then a simple conditional comparison is made to get the smaller values in __a and __b, and the result is returned using an assignment extension. This implementation ensures that the original logic is not changed, the first assignment, and also avoids the problem of the parentheses priority, can be said to be a better solution. If the compilation environment supports this extension of GNU C, then there is no doubt that we should write our min macro in this way, if we do not support this environment extension, then we can only artificially specify that the parameters do not have operations or function calls to avoid errors. We have had enough discussion about min, but we still have a suspense place. If there is already a definition of __a or __b in the same scope (although it is generally not the name of the tragedy, but who knows), the macro may be problematic. The assignment after the declaration cannot be initialized because of a duplicate definition, resulting in unpredictable behavior of the macro. If you are interested, you might as well try your hand at what the results will be. Apple solved the problem completely in clang, we opened Xcode to build a new project, enter min in the code, and then cmd+ click to find the clang min. To facilitate the explanation, I directly copied the relevant parts as follows:
- CLANG MIN
- #define __NSX_PASTE__ (A, b) a# #B
- #define MIN (b) __nsmin_impl__ (a,b,__counter__)
- #define __NSMIN_IMPL__ (a,b,l) ({__typeof__ (a) __nsx_paste__ (__a,l) = (a); __typeof__ (b) __nsx_paste__ (__b,l) = (b); (__nsx_paste__ (__a,l) < __nsx_paste__ (__b,l))? __NSX_PASTE__ (__a,l): __nsx_paste__ (__b,l); })
It seems to be a little long and arduous. We first beautify this macro, the first is the last __nsmin_impl__ content is too long. We know that the code can be inserted into the line without affecting the meaning of the word, whether the macro can do it? The answer is yes, but we cannot use a single carriage return to complete, and must precede the carriage return with a backslash \. Rewrite it and add a good line to it:
- #define __NSX_PASTE__ (A, b) a# #B
- #define MIN (b) __nsmin_impl__ (a,b,__counter__)
- #define __NSMIN_IMPL__ (a,b,l) ({__typeof__ (a) __nsx_paste__ (__a,l) = (a); \
- __typeof__ (b) __nsx_paste__ (__b,l) = (b); \
- (__nsx_paste__ (__a,l) < __nsx_paste__ (__b,l))? __NSX_PASTE__ (__a,l): __nsx_paste__ (__b,l); \
- })
But you can see that min consists of three macro definitions. The two concatenated pound # #在宏中是一个特殊符号 that appear in the first __nsx_paste__, which represents the operation of connecting two parameters together. Note that a function macro must be a meaningful operation, so you cannot directly write AB to connect two parameters, and you need to a# #B in the example. There are all the other arithmetic symbols in the macro, and we'll introduce a few later. The next is the min of the two parameters we call, and it does this by invoking another three parameter of the macro __nsmin_impl__, where the first two parameters are our input, and the third __counter__ we don't seem to know, and we don't know where it came from. In fact, __counter__ is a predefined macro that will count from 0 during compilation, plus 1 each time it is called. Because of uniqueness, it is often used to construct independent variable names. With the foundation above, it is easy to see the final implementation of the macro. The overall idea and the previous implementation are the same as the previous Gnuc min, where the difference is adding a count suffix to the variable name __a and __b, which greatly avoids the possibility of a problem with the same variable name (of course, if you stubbornly call the variable __a9527 and have a problem, You can only say that you will not die without dying. It took a lot of effort, and we finally got a simple min macro completely figured out. Macro is such a kind of things, the simple surface hides a lot of mystery, it is small has the universe. As an exercise, you can try it yourself. Implement a square (a), give a digital input, and output its squared macro. Although generally this calculation is now used inline to do, but with a similar idea of min we can be very good to achieve it, try it:)
IOS 7: Talking about # define macro definitions