Detailed usage of C language macros

1 Introduction


In the C language macro there is a piece of code fragment names. Whenever used to this macro, the macro contents will be replaced this code. There are two main macro, their main difference is in the use of the above, one is similar to the data object when called using the Object-like, similar to another when using the function calls known as Function-like. It is defined in the C language using #define macros
you can be any valid identifier defined as a macro, set the C language keywords can be. But in the C language as a macro name can not be defined. In C ++, the following keywords not be used as a macro name and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq.

2 two types of macros


2.1 Object-like宏

Object-like macros, the replacement of code segments may be relatively simple. The most common way to do this represents a constant number. E.g:

#define BUFFER_SIZE 1024

When using the macro can be used instead of numbers.

foo = (char *) malloc (BUFFER_SIZE);

The macro preprocessor will replace the corresponding numbers, as shown below.

foo = (char *) malloc (1024);

By convention, the macro generally writing in capital letters.

Multi-line macros

#Define macro ends at the end of the line, if necessary, you can add a backslash to the end of the defined macros into multiple lines.

#define NUMBERS 1, \
                2, \
                3
int x[] = { NUMBERS };
//→ int x[] = { 1, 2, 3 };


Repeatedly macro substitution


If the code segment is still macro macro definition, then, we will continue preprocessor macro replacement operation.

#define TABLESIZE BUFSIZE
#define BUFSIZE 1024
TABLESIZE
//→ BUFSIZE
//→ 1024

TABLESIZE will eventually be replaced by 1024

2.2 Function-like宏

Macros can also be defined into the following form, when using the macro, call the function similar to the definition of such macros, after the name of the macro (no spaces between brackets) followed pile brackets.

#define lang_init()  c_init()
lang_init()
//→ c_init()

Acer calls this kind of time, that must be followed by a parenthesis, if not with brackets, then displays the syntax error.

3 macro parameters
Function-like Macros can accept parameters, similar to the real function the same. Parameter must be a valid C language identifier, use a comma separated

#define min(X, Y)  ((X) < (Y) ? (X) : (Y))
  x = min(a, b);          //→  x = ((a) < (b) ? (a) : (b));
  y = min(1, 2);          //→  y = ((1) < (2) ? (1) : (2));
  z = min(a + 28, *p);    //→  z = ((a + 28) < (*p) ? (a + 28) : (*p));

In the above example, x = min (a, b), when the macro is called, the parameters into a, b replacing the parameter X, Y position within the macro, it becomes x = ((a) <( ? b) (a): (b))

4 the string of
the string of means that may be added before the parameter # macro, so that the parameter becomes a string.
E.g:

#include <stdio.h>
#define str(expr) printf("%s\r\n", #expr)

int main()
{
    str(abc);
    str(12345);
    return 0;
}

这里运行代码会打印:

abc
12345

str宏的入参,都变成了字符串打印了出来。

5 is connected to a symbol
in a macro can be used to connect two two # symbols into one symbol.

#include <stdio.h>
#define A1 printf("print A1\r\n")
#define A2 printf("print A2\r\n")
#define A(NAME) A##NAME
int main()
{
    A(1);
    return 0;
}

这里会打印

print A1

In this example, the macro is called A (1), NAME 1. A ## NAME the symbolic link, and is about 1 A connection has become a symbol A1, and then execute the contents of the macro A1. The final print out the print A1

6, variable parameters
defining the macro can accept a variable number of parameters, similar to the definition of a function. The following is an example

#include <stdio.h>
#define myprintf(...) fprintf (stderr, __VA_ARGS__)
int main()
{
    myprintf("1234\r\n");
    return 0;
}

这里会输出

1234

This form of macros, ... will expanded parameter representative to the back of __VA_ARGS__. In this example, it will expand to fprintf (stderr, "1234 \ r \ n").
If your argument is more complex, above myprintf it can also be defined as a form of self with the meaning defined to represent the name args parameter:

#define myprintf(args...) fprintf (stderr, args)


7 predefined macros


Standard predefined macros
standard predefined macros are double-underlined at the beginning and end, such as __FILE__ and __LINE__, line number and the name of the lines of code files.

#include <stdio.h>

int main()
{
    printf("FILE:%s,LINE:%d\r\n",__FILE__, __LINE__);
    printf("DATA:%s\r\n",__DATE__);
    printf("TIME:%s\r\n",__TIME__);
    printf("STDC:%d\r\n",__STDC__);
    printf("STDC_VERSION:%d\r\n",__STDC_VERSION__);
    printf("STDC_HOSTED:%d\r\n",__STDC_HOSTED__);
#ifdef __cplusplus
    printf("cplusplus:%d\r\n", __cplusplus);    
#else
    printf("complied by c\r\n");    
#endif
    
    return 0;
}

输出如下

FILE:macro.c,LINE:5
DATA:Jan 13 2019
TIME:21:41:14
STDC:1
STDC_VERSION:201112
STDC_HOSTED:1
complied by c


本文件名为macro.c,并且该行代码为第5行。
__DATA__表示当前的日期
__TIME__表示当前的时间
__STDC__在正常的操作中,此宏为1,表示编译器符合ISO C标准
__STDC_VERSION__表示ISO C的版本
__STDC_HOSTED__如果值为1的话,表示目标环境有完成的标准C库
__cplusplus如果该宏被定义了,表示是被C++编译器编译的

Common predefined macros
of the macro section is an extension of the GNU C compiler implementation.

#include <stdio.h>

int main()
{
    printf("__COUNTER_%d\r\n", __COUNTER__);
    printf("__COUNTER_%d\r\n", __COUNTER__);    
    printf("__GNUC__:%d\r\n",__GNUC__);
    printf("__GNUC_MINOR__:%d\r\n",__GNUC_MINOR__);
    printf("__GNUC_PATCHLEVEL__:%d\r\n",__GNUC_PATCHLEVEL__);
    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        printf("little endian\r\n");
    #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
        printf("big endian\r\n");
    #elif __BYTE_ORDER__ == __ORDER_PDP_ENDIAN__
        printf('pdp endian\r\n')
    #endif
    #if __LP64__ == 1
        printf("64bit env\r\n");
    #else
        printf("other bit env\r\n");
    #endif
    return 0;
}

输出

__COUNTER_0
__COUNTER_1
__GNUC__:7
__GNUC_MINOR__:3
__GNUC_PATCHLEVEL__:0
little endian
64bit env


__COUNTER_:是生成一个唯一的数字。
__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__确定了你的GCC版本号。例如我的环境就是7.3.0
__BYTE_ORDER__表示当前环境的字节序
__LP64__ 表示当前环境是不是64位,如果该值为1,则环境为64位环境
更多GNU C编译器的预定义宏可以 点此连接查看

System-specific predefined macros
system-specific predefined macros, on different operating systems and CPU above, presented results may vary. For example, my environment is Linux X86_64 platform. The following code execution

#include <stdio.h>

int main()
{
    printf("__unix_:%d\r\n", __unix__);
    printf("__x86_64__:%d\r\n", __x86_64__);
    return 0;
}

输出结果是:

__unix_:1
__x86_64__:1

如果是其他操作系统的CPU平台的话,执行的结果会有所不同。

C ++ naming the operator
in the first quarter has said that C ++ and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq these names can not be used as the name of the macro. In C ++ it is because these keywords predefined system will become operator.

命名操作符    符号
and    &&
and_eq    &=
bitand    &
bitor    |
compl    ~
not    !
not_eq    !=
or    ||
or_eq    |=
xor    ^
xor_eq    ^=
所以在C++ 中,你可以使用命名操作符来代替这些符号。例如:

#include <iostream>
using namespace std;
int main()
{
    int a = 10;
    int b = 20;
    int c = a bitor b; // a | b
    int d = a bitand b; //a & b
    cout << "c = " << c << endl;
    cout << "d = " << d << endl;

    if ( true and (a > b))
        cout << "true" << endl;
    else
        cout << "false" << endl;
        
    return 0;
}


输出:

c = 30
d = 0
false


8, cancel repeat macro definition and macro definitions
to cancel the macro definition
using #undef can cancel already-defined macros

#define BUFSIZE 1020
#undef BUFSIZE

If we use BUFSIZE after #undef will report an error, do not define BUFSIZE

Repeat macro definition
if between two macro definition, only whitespace and comments in different words, two or macros with a macro definition.
E.g:

#define FOUR (2 + 2)
#define FOUR         (2    +    2)
#define FOUR (2 /* two */ + 2)

These three macros are actually the same, not a duplicate definitions.
And the following macros are different, the compiler will give a warning repeated macro definition. And only last a macro to take effect

#define FOUR (2 + 2)
#define FOUR ( 2+2 )
#define FOUR (2 * 2)
#define FOUR(score,and,seven,years,ago) (2 + 2)


9, several common usage scenarios
alternative magic number
this may be a very common usage in the C language, is to use a macro to replace a digital magic, increase code readability.

#include <stdio.h>
#include <stdlib.h>

#define BUFSIZE 1024
int main()
{
    char *buf = (char *)malloc(BUFSIZE);
    free(buf);
    return 0;
}



LOG log and do {} while (0)
 

#include <stdio.h>
#include <stdlib.h>
#define BUFSIZE 1024
#define LOG(str) \
do \
{\
    fprintf(stderr, "[%s:%d %s %s]:%s\r\n",  __FILE__, __LINE__, __DATE__, __TIME__, str); \
}while(0)
int main()
{
    char *buf = (char *)malloc(BUFSIZE);
    LOG("malloc for buf");
    free(buf);
    return 0;
}

Output content:

[macro.c:12 Jan 13 2019 22:38:33]:malloc for buf

This defines LOG macro, you can print the log, the output current code file and line number, as well as content and user-defined time. Self-extension grows richer content.
Here a do {} while (0) to contain the contents of the macro. This seemingly do () while (0) does not make sense. But this is a good habit to write macros within multiple lines of code segments.

Using the do {} while (0) included, can be used as an independent Block, defined variables and some complex operations
this usage is to prevent errors in the course of use of macros.
E.g

#define foo() \
    fun1(); \
    fun2()
if (a > 10)
    foo()

In this case, if there is no back with braces, which defines the macro foo we are two statements, which are outside the if condition fun2 judgment. This is not in line with our expectations up.

If you use braces to avoid the above error, the following error will appear:

#include <stdio.h>
#include <stdlib.h>
#define add(x, y) {x += 1; y += 2;}

int main()
{
    int x = 10;
    int y = 20;
    if (x > y)
        add(x, y);
    else
        ;

    return 0;
}

Here, after a semicolon add (x, y). Else if not match will cause compile errors. Therefore, in order to prevent these errors, you can do {} while (0) comprises a function.

Linux kernel offsetof

In the Linux kernel code, we use a lot of offsetof the macro, which role is to calculate the offset value of a variable structure is.

#include <stdio.h>
#include <stdlib.h>
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)
typedef struct myStructTag
{
    int a;
    double b;
    float c;
    char szStr[20];
    long int l;
}myStruct;
int main()
{
    printf("%d\r\n", offsetof(myStruct, a));
    printf("%d\r\n", offsetof(myStruct, b));
    printf("%d\r\n", offsetof(myStruct, c));
    printf("%d\r\n", offsetof(myStruct, szStr));
    printf("%d\r\n", offsetof(myStruct, l));
}

Output:

0
8
16
20
40

The first reference into macro TYPE is the type of structure, the second term is a variable name MEMBER structural body. The strong macro 0 type converted TYPE * pointer, and acquires the address pointer to the structure of the individual members. Because the structure of the address pointer is 0, the member address is taken to get the address of the base address 0 of the offset value.
With the macro, we will be able to get the address of the structure pointer to any address by a member of the structure of the body.

Linux kernel macro container_of

The macro function is to be acquired by the address pointer to the structure of any member of the structure. This macro need the help of one of the offsetof.
Here is the macro code:

#include <stdio.h>
#include <stdlib.h>
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
    const typeof(((type *)0)->member) * __mptr = (ptr); \
    (type *)((char *)__mptr - offsetof(type, member)); })

typedef struct myStructTag
{
    int a;
    double b;
    float c;
    char szStr[20];
    long int l;
}myStruct;
int main()
{
    myStruct *p = (myStruct *)malloc(sizeof(myStruct));
    printf("base ptr=%p\r\n", p);
    printf("base ptr by l=%p\r\n", container_of(&p->l, myStruct, l));
}

输出内容:

base ptr=0x55cc10d66260
base ptr by l=0x55cc10d66260

As can be seen, by container_of calculated base address and the address of the direct printing p are the same. Many basic abstract data structures Linux kernel, such as double linked list, etc., are widely used to container_of this macro. With this macro, we can write out irrelevant data abstraction, data structures, for example, we can not write a doubly linked list data field.

struct list_head {
    struct list_head *next, *prev;
};

Realization of the time, we only need to operate relationship to the list, without any interference of the data field. In use, we only need to be defined as a list node to node specific data structure.

struct person 
    int age; 
    char name[20];
    struct list_head list; 
};

Insert and delete nodes only need to operate the list, while by container_of this macro, we can go to get to the first address through the entire data structure pointer list. This put abstract data structure, and specific data is completely peeled off.

 

Examples of nodes registered VPP

\ VLIB_REGISTER_NODE macro definitions
first look at the definition of the macro in a period of VPP node registration:

#define VLIB_REGISTER_NODE(x,...)                                       \
    __VA_ARGS__ vlib_node_registration_t x;                             \
static void __vlib_add_node_registration_##x (void)                     \
    __attribute__((__constructor__)) ;                                  \
static void __vlib_add_node_registration_##x (void)                     \
{                                                                       \
    vlib_main_t * vm = vlib_get_main();                                 \
    x.next_registration = vm->node_main.node_registrations;             \
    vm->node_main.node_registrations = &x;                              \
}                                                                       \
__VA_ARGS__ vlib_node_registration_t x

In the snippet, VLIB_REGISTER_NODE a macro argument x, and variable parameters.
__VA_ARGS__ vlib_node_registration_t x vlib_node_registration_t declares a structure variable x, where the role is only a statement.
static void __vlib_add_node_registration _ ## x (void ) \ __attribute __ ((__ constructor__)) code is a function declaration, to generate the character ## using the function name in accordance with parameters of the connection. __constructor__ GNU compiler is an extension, which as a function of the constructor specifies the function call when the module is initialized.
The next step is __vlib_add_node_registration _ ## x functions defined. Specific content we can ignore the first out.
And the last line, and define it again x. This requires a combination of macro calls the place a look. In short, the macro declares a variable x, and then define a
VLIB_REGISTER_NODE use of macros
reading the macro definition, we look at how the macro is called.

VLIB_REGISTER_NODE (ip4_icmp_echo_request_node,static) = {
  .function = ip4_icmp_echo_request,
  .name = "ip4-icmp-echo-request",

  .vector_size = sizeof (u32),

  .format_trace = format_icmp_input_trace,

  .n_next_nodes = 1,
  .next_nodes = {
    [0] = "ip4-load-balance",
  },
};

First introduced to the macro parameters x ip4_icmp_echo_request_node, when expanding macros, x will be replaced by the incoming parameters.
The second parameter is static, so the definition of a variable x, will be static modification.
After the last definition, equal sign and braces. Here is the last line in the macro code for __VA_ARGS__ vlib_node_registration_t x structure assignment. Here you can understand why __VA_ARGS__ vlib_node_registration_t x twice in the macro definitions inside. The first was a statement only, behind-defined functions need only address the value to be registered. In the last section of the code of macros, it is the real definition of the structure.
Finally, he launched into a code like the following:

static vlib_node_registration_t ip4_icmp_echo_request_node;                             
static void __vlib_add_node_registration_ip4_icmp_echo_request_node (void)                     
    __attribute__((__constructor__)) ;                                  
static void __vlib_add_node_registration_ip4_icmp_echo_request_node (void)                     
{                                                                       
    vlib_main_t * vm = vlib_get_main();                                 
    ip4_icmp_echo_request_node.next_registration = vm->node_main.node_registrations;             
    vm->node_main.node_registrations = &ip4_icmp_echo_request_node;                              
}                                                                       
static vlib_node_registration_t ip4_icmp_echo_request_node = {
  .function = ip4_icmp_echo_request,
  .name = "ip4-icmp-echo-request",

  .vector_size = sizeof (u32),

  .format_trace = format_icmp_input_trace,

  .n_next_nodes = 1,
  .next_nodes = {
    [0] = "ip4-load-balance",
  },
};


VPP is defined error code

In practice, C language programming, there will be a lot of error codes and error messages corresponding definitions. VPP used in the following manner to define a code error code and the error string.

#include <stdio.h>
#define foreach_ethernet_arp_error                    \
  _ (replies_sent, "ARP replies sent")                    \
  _ (l2_type_not_ethernet, "L2 type not ethernet")            \
  _ (l3_type_not_ip4, "L3 type not IP4")                \
  _ (l3_src_address_not_local, "IP4 source address not local to subnet") \
  _ (l3_dst_address_not_local, "IP4 destination address not local to subnet") \
  _ (l3_src_address_is_local, "IP4 source address matches local interface") \
  _ (l3_src_address_learned, "ARP request IP4 source address learned")  \
  _ (replies_received, "ARP replies received")                \
  _ (opcode_not_request, "ARP opcode not request")                      \
  _ (proxy_arp_replies_sent, "Proxy ARP replies sent")            \
  _ (l2_address_mismatch, "ARP hw addr does not match L2 frame src addr") \
  _ (gratuitous_arp, "ARP probe or announcement dropped") \
  _ (interface_no_table, "Interface is not mapped to an IP table") \
  _ (interface_not_ip_enabled, "Interface is not IP enabled") \

static char *ethernet_arp_error_strings[] = {
#define _(sym,string) string,
  foreach_ethernet_arp_error
#undef _
};
typedef enum
{
#define _(sym,string) ETHERNET_ARP_ERROR_##sym,
  foreach_ethernet_arp_error
#undef _
    ETHERNET_ARP_N_ERROR,
} ethernet_arp_input_error_t;

int main()
{
    printf("%s\r\n", ethernet_arp_error_strings[ETHERNET_ARP_ERROR_interface_no_table]);
}


Output:

Interface is not mapped to an IP table

foreach_ethernet_arp_error defined in corresponding relation to the modules of all types of errors and error codes. ethernet_arp_error_strings defines a set of error strings.
ethernet_arp_input_error_t defines a set of error codes.
We can find the error code to ethernet_arp_error_strings corresponding error string as an index.
So that we can easily extend and modify the type of error and the error code. Only you need to modify the definition of foreach_ethernet_arp_error.
Here the error code and the error strings are used to automatically generate the macros.
-------------------------------------------------- -------------------------------------------------- ------------------------------------

A, # parameters for converting a string

Example 1:

#define P(A) printf("%s:%d\n",#A,A);

int main(int argc, char **argv)

{

        int a = 1, b = 2;

        P(a);

        P(b);

        P(a+b);

        system("pause");

}

Example 2:

#define SQUARE(x) printf("The square of "#x" is %d.\n", ((x)*(x)));

SQUARE(8)

The output is: The square of 8 is 64

Second, the operator ## can be used to replace part of the macro function. The operators of the two language symbols into a single symbol languages, there is provided a means connected to the actual arguments for macro expansion

Example 1:

#define XNAME(n) x ## n

If you use this macro: XNAME (8)

It will be expanded in such a way: x8

## is a pressure-sensitive adhesive, before and after the two parts are bonded together, i.e. the "character" of the meaning. But "##" not at liberty to bond any character, must be a valid C language identifier. In a single macro definition, at most a "#" or "##" pretreatment operator may occur. If the "#" or "##" pretreatment relevant operator does not specify the order calculated, problems can occur. To avoid this problem, the use of only one operator (i.e., a "#" or "##", or do not) in a single macro definition. Unless it is necessary, or try not to use the "#" and "##."

Third, __ macro VA_ARGS__ is a variable parameter, few people know this macro, the macro variable parameter is the new norm in the new C99, now it seems that only supports gcc (VC6.0 compiler does not support).
The last idea is to achieve a macro parameter defined in the parameter list for the ellipsis (ie three points).

 

Four, ## __ VA_ARGS__ macro role is preceded by ##, when the number of the variable parameter is 0, the front ## where the excess play "," effect removed, otherwise compile error

This is generally used in a little more information on debugging

E.g:

#define my_print1 (...) printf (__ VA_ARGS__) my_print1 ( "i =% d, j =% d \ n", i, j) is printed correctly

#define my_print2(fmt,...)  printf(fmt,__VA_ARGS__)  

my_print1 ( "i =% d, j =% d \ n", i, j) is printed correctly

my_print1 ( "iiiiiii \ n") fails to compile printed because extended out only one parameter, to at least two or more parameters

If #define my_print2 (fmt, ...) printf (fmt, ## __ VA_ARGS__)  

Then

my_print1 several parameters which can either print correctly

#define MODULE_NAME   "MY_LIB"

#define error_print(fmt, ...)  printf("[ERROR]["MODULE_NAME"](%s|%d)" fmt, __func__, __LINE__, ##__VA_ARGS__)
 

 

Guess you like

Origin blog.csdn.net/armlinuxww/article/details/93965786
Recommended