Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Self-cultivation of embedded C language 12: there is a kind of macro called variable parameter macro

2025-01-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/02 Report--

12.1 what is a variable parameter macro

In the above tutorial, we learned the definition and use of variable parameter functions, the basic routine is to use va_list, va_start, va_end and other macros to parse the variable parameter list. After we find the storage address of these parameters, we can deal with these parameters: either do it yourself, handle it yourself, or continue to call other functions to deal with.

Void print_num (int count,...) {va_list args; va_start (args,count); for (int I = 0; I < count; iTunes +) {printf ("* args:% d\ n", * (int *) args); args + = 4;}} void _ attribute__ ((format (printf,2,3) LOG (int KJEAR * fmt,...) {va_list args Va_start (args,fmt); vprintf (fmt,args); va_end (args);}

GNU C thinks this is not good enough, so let's hammer it again: even the macro definition supports changing parameters.

In this section, we will learn about the definition and use of variable parameter macros. In fact, the C99 standard already supports this feature, but other compilers are not very powerful, the support for the C99 standard is not very good, only GNU C supports this function, so sometimes we regard this variable parameter macro as a syntax extension of GNU C. The LOG function above can be defined directly if we want to use a variable parameter macro implementation.

# define LOG (fmt,...) Printf (fmt, _ _ VA_ARGS__) # define DEBUG (...) Printf (_ _ VA_ARGS__) int main (void) {LOG ("Hello! Isimm% s\ n", "Wanglitao"); DEBUG ("Hello! Isimm% s\ n", "Wanglitao"); return 0;}

The implementation form of a variable parameter macro is actually similar to that of a variable parameter function: with. Represents a variable parameter list, which consists of uncertain parameters separated by commas. Variable parameter macros use a new VA_ARGS__ predefined identifier added to the C99 standard to represent the previous parameter list, instead of using macros such as va_list, va_start, and va_end to parse the parameter list as a variable function. When a preprocessor expands a macro, it replaces all VA_ARGS__ identifiers in the macro definition with a list of parameters.

Use the macro definition to implement a variable parameter printing function, you will find that its implementation is even more convenient than the variable parameter function! Many print macros in the kernel are often implemented using variable parameter macros, which are generally defined in the following format.

# define LOG (fmt,...) Printf (fmt, _ _ VA_ARGS__)

In this macro definition, there is a fixed parameter, usually a format string, and the subsequent parameters are used to print data in various formats to match the previous format string. There is a loophole in this definition, that is, when the parameter is empty, a syntax error occurs when the macro is expanded.

# define LOG (fmt,...) Printf (fmt,__VA_ARGS__) int main (void) {LOG ("hello\ n"); return 0;}

The above program will fail when compiling, resulting in a syntax error. This is because we pass only one parameter to the LOG macro, and the parameter is empty. When the macro is expanded, it looks like this.

Printf ("hello\ n",)

After the macro is expanded, there is a comma after the first string parameter, so a syntax error is generated. We need to continue to improve on this macro, using the macro connector # # to avoid this syntax error.

12.2 continue to improve our macros

Next, we use the macro connector # # to improve the macro above.

The main function of the macro connector # # is to concatenate two strings, and we can use # # to concatenate two characters in the macro definition. When the preprocessor expands the macro during the preprocessing phase, it merges the characters on both sides of # # and deletes the two characters.

# define A (x) a##xint main (void) {int A (1) = 2; / / int A1 = 2; int A () = 3; / / int axiom 3; printf ("% d% d\ n", a1jina); return 0;}

As in the above program, we define a macro.

# define A (x) a roommate x

The function of this macro is to concatenate the characters an and x. In the program, the expansion of A (1) is the expansion of A1 and the expansion of A () is a. We can print the values of variables A1 and a directly using the printf () function, because when the macro is expanded, it is equivalent to defining two integer variables A1 and a using the int keyword. The above program can be compiled and run as follows.

2 3

Knowing how to use the macro connector # #, we can then make some changes to the LOG macro.

# define LOG (fmt,...) Printf (fmt, # # _ VA_ARGS__) int main (void) {LOG ("hello\ n"); return 0;}

We add the macro connector # # before the identifier _ _ VA_ARGS__. The advantage of this is that when the variable parameter list is not empty, the function of # # is to connect the fmt and the variable parameter list. The parameters are separated by commas, and the macro can be used normally. When the parameter list is empty, # # also has a special use by removing the comma after the fixed parameter fmt so that the macro can be used properly.

12.3 another way of writing variable parameter macros

When we define a parameter macro, in addition to using the predefined identifier _ _ VA_ARGS__ to represent the parameter list, we can also use the following way of writing.

# define LOG (fmt,args...) Printf (fmt, args)

A variable parameter macro is defined using a predefined identifier VA_ARGS, which is written in the C99 standard. The above format is a new way of writing the GNU C extension. Instead of using VA_ARGS, we use args... directly To represent a variable parameter list, and then in the later macro definition, you can simply use args to represent the variable parameter list.

As above, to avoid syntax errors when changing the parameter list to empty, we also need to add a connector # #.

# define LOG (fmt,args...) Printf (fmt,##args) int main (void) {LOG ("hello\ n"); return 0;}

In this way, you will find that it looks more intuitive and convenient than using _ _ VA_ARGS__.

12.4 variable parameter macros in the kernel

Variable parameter macros are mainly used for log printing in the kernel. Some driver modules or subsystems sometimes define their own print macros, which can support print switches, print formats, priority control, and so on. For example, in the printk.h header file, we can see the definition of the pr_debug macro.

# if defined (CONFIG_DYNAMIC_DEBUG) # define pr_debug (fmt,...)\ dynamic_pr_debug (fmt, # # _ VA_ARGS__) # elif defined (DEBUG) # define pr_debug (fmt,...)\ printk (KERN_DEBUG pr_fmt (fmt), # # _ _ VA_ARGS__) # else#define pr_debug (fmt,...)\ no_printk (KERN_DEBUG pr_fmt (fmt) # # _ _ VA_ARGS__) # endif#define dynamic_pr_debug (fmt,...)\ do {\ DEFINE_DYNAMIC_DEBUG_METADATA (descriptor, fmt) \ if (unlikely (descriptor.flags\ & _ DPRINTK_FLAGS_PRINT))\ _ dynamic_pr_debug (& descriptor, pr_fmt (fmt),\ # # _ VA_ARGS__);\} while (0) static inline _ printf (1,2) int no_printk (const char * fmt,...) {return 0 } # define _ _ printf (a, b)\ _ attribute__ ((format (printf, a, b)

Seeing this macro definition, I have to admire the author of macro. A small macro, the comprehensive use of a variety of skills and knowledge points, the C language to the extreme!

This macro defines three versions. If we have the dynamic debugging option when compiling the kernel, then this macro is defined as dynamicprdebug. If the dynamic debugging option is not configured, we can also use the DEBUG macro to control whether the macro is turned on or off.

No_printk (), as an inline function, is defined in the printk.h header file, and through the format attribute declaration, instructs the compiler to do parameter format checking according to the printf standard.

The most interesting is the dynamicprdebug macro, which uses the do {...} while (0) structure for the macro definition. This seems to be a bit redundant, with it or not, our macros can work. Anyway, it is all executed once, so why use this loop structure which seems to be "icing on the cake"? The reason is very simple, this definition is to prevent macros from generating macro ambiguity after they are expanded in statements with branch structures such as conditions, selections, and so on.

For example, we define a macro that consists of two print statements.

# define DEBUG ()\ printf ("hello"); printf ("else\ n") int main (void) {if (1) printf ("hello if\ n"); else DEBUG (); return 0;}

The running result of the program is as follows.

Hello ifelse

In theory, the else branch cannot be executed. But as you can see from the running results, the program also executes part of the code of the else branch. This is because the macro we define consists of multiple statements, which, when expanded directly, looks like this.

Int main (void) {if (1) printf ("hello if\ n"); else printf ("hello"); printf ("else\ n"); return 0;}

The direct expansion of multiple statements in the macro call destroys the original if-else branch structure of the program, resulting in a change in the program logic, so you will see the abnormal printing of the else branch. Using the structure of do {...} while (0), we can wrap the compound statements in our macro definition. After the macro is expanded, it is a code block, which avoids this kind of logic error.

A small macro, hidden various knowledge points, comprehensive use of a variety of skills, careful analysis, you can learn a lot of knowledge. In the future work and study, we may come into contact with a variety of shape × × macro, as long as we have a solid foundation of C language, familiar with the common extension syntax of GNU C, and then encounter such similar macros, we can analyze them slowly. Don't be afraid, only when you have really analyzed it, can you really master it, can it be transformed into your own knowledge and ability, and can you appreciate its subtlety.

This tutorial is adapted from the C language embedded Linux Advanced programming Video tutorial No. 05, the electronic version of the book can join the QQ group: 475504428 download, more embedded video tutorials, you can follow:

Official account of Wechat: Otaku tribe (armlinuxfun)

51CTO College-Mr. Wang Litao: http://edu.51cto.com/sd/d344f

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report