What are C macros useful for?

前端 未结 18 2019
-上瘾入骨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 19:59

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

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

    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.

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

    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.

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

    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.

    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.

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

    Unlike regular functions, you can do control flow (if, while, for,...) in macros. Here's an example:

    #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;
    } 
    
    0 讨论(0)
  • 2020-11-28 20:05

    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.

    Others will argue against using #if for conditional compilation .. they would rather you:

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

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

    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.

    copied from 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; but D provides a more scalable means to solve the same problems.

    Macros

    Preprocessor macros add powerful features and flexibility to C. But they have a downside:

    • 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++.)
    • 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:

    1. Defining literal constants:

      • The C Preprocessor Way

        #define VALUE 5
        
      • The D Way

        const int VALUE = 5;
        
    2. Creating a list of values or flags:

      • The C Preprocessor Way

        int flags:
        #define FLAG_X  0x1
        #define FLAG_Y  0x2
        #define FLAG_Z  0x4
        ...
        flags |= FLAG_X;
        
      • The D Way

        enum FLAGS { X = 0x1, Y = 0x2, Z = 0x4 };
        FLAGS flags;
        ...
        flags |= FLAGS.X;
        
    3. Setting function calling conventions:

      • The C Preprocessor Way

        #ifndef _CRTAPI1
        #define _CRTAPI1 __cdecl
        #endif
        #ifndef _CRTAPI2
        #define _CRTAPI2 __cdecl
        #endif
        
        int _CRTAPI2 func();
        
      • The D Way

        Calling conventions can be specified in blocks, so there's no need to change it for every function:

        extern (Windows)
        {
            int onefunc();
            int anotherfunc();
        }
        
    4. Simple generic programming:

      • The C Preprocessor Way

        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 Way

        D enables declarations of symbols that are aliases of other symbols:

        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.

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