What are C macros useful for?

前端 未结 18 2020
-上瘾入骨i
-上瘾入骨i 2020-11-28 19:45

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 complet

相关标签:
18条回答
  • 2020-11-28 20:09

    I didn't see anyone mentioning this so, regarding function like macros, eg:

    #define MIN(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).

    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).

    0 讨论(0)
  • 2020-11-28 20:10

    Macros allow someone to modify the program behavior during compilation time. Consider this:

    • C constants allow fixing program behavior at development time
    • C variables allow modifying program behavior at execution time
    • C macros allow modifying program behavior at compilation time

    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)

    Simple examples: (from glibc limits.h, define the largest value of 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.

    (POSIX version request)

    #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.

    (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 ?

    (pthread.h, complex definitions at compile time)

    # 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.

    0 讨论(0)
  • 2020-11-28 20:11

    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:

    #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.

    0 讨论(0)
  • 2020-11-28 20:13

    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.

    The C library's functions to compare memory and strings are quite ugly to use.

    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(...)).

    Here are two neat macros some people (including I) use when they need to compare strings or memory using 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.

    0 讨论(0)
  • 2020-11-28 20:15

    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!

    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++.

    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...!

    0 讨论(0)
  • 2020-11-28 20:15

    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.

    0 讨论(0)
提交回复
热议问题