Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any restrictions to the C standard allowing functions to be implemented as macros?

Often, in addition to providing a function declaration, C standard headers may provide a "masking macro" to make things speedier. For example, if I include ctype.h, the header file will declare

int isdigit(int c);

But it may also mask the declaration with a macro. I believe this is a portable isdigit macro according to the C standard:

#define isdigit(c) ((c) >= '0' && (c) <= '9')

Of course, this macro is also dangerous because it introduces undefined behavior if you do this while the macro is defined:

int c = 'A';
printf("%d\n", isdigit(c++));

To avoid UB in this hypothetical case, I have to surround the function name with parens: (isdigit)(c++). So, my question is: are there any restrictions to what sort of masking macros a standard header can define? Are they guaranteed to not cause undefined behavior if an argument expression has side effects, or are they technically allowed to have weird behavior such as we see above? Where are the limits?

like image 504
Ricky Stewart Avatar asked Oct 10 '13 14:10

Ricky Stewart


People also ask

Can a macro be a function in C?

The Concept of C MacrosMacros can even accept arguments and such macros are known as function-like macros. It can be useful if tokens are concatenated into code to simplify some complex declarations. Macros provide text replacement functionality at pre-processing time.

Can macros be used for functions?

In general, a macro language function processes one or more arguments and produces a result. You can use all macro functions in both macro definitions and open code. Macro functions include character functions, evaluation functions, and quoting functions.

Is it better to use a macro or a function in C?

Macros are typically faster than functions as they don't involve actual function call overhead.

Why use of macro is recommended instead of function?

Speed versus size The main benefit of using macros is faster execution time. During preprocessing, a macro is expanded (replaced by its definition) inline each time it is used. A function definition occurs only once regardless of how many times it is called.


2 Answers

Per C11 7.1.4.1, "Use of Library Functions", particularly the last part quoted below:

Any function declared in a header may be additionally implemented as a function-like macro defined in the header, so if a library function is declared explicitly when its header is included, one of the techniques shown below can be used to ensure the declaration is not affected by such a macro....For the same syntactic reason, it is permitted to take the address of a library function even if it is also defined as a macro. The use of #undef to remove any macro definition will also ensure that an actual function is referred to. Any invocation of a library function that is implemented as a macro shall expand to code that evaluates each of its arguments exactly once, fully protected by parentheses where necessary, so it is generally safe to use arbitrary expressions as arguments.

Note that the "generally" at the end is important, there, and there are explicit exceptions. C11 7.21.7.5.2 says "the getc function is equivalent to fgetc, except that if it is implemented as a macro, it may evaluate stream more than once, so the argument should never be an expression with side effects", with similar language for putc in C11 7.21.7.7.2. In this particular case, stream is a FILE *, so under normal circumstances it would be kind of weird to have this as an expression with side-effects, but it could happen. The same is also true for their wide character counterparts, putwc() and getwc(). I am not aware of any other exceptions like this.

like image 96
Crowman Avatar answered Oct 02 '22 10:10

Crowman


From here: http://www.qnx.com/developers/docs/6.5.0/index.jsp?topic=%2Fcom.qnx.doc.dinkum_en_ecpp%2Flib_over.html

Arguments that have side effects evaluate the same way whether the expression executes the macro expansion or calls the function. Macros for the functions getc and putc are explicit exceptions to this rule.

I've found a few other references saying about the same thing (though nothing citing the C standard directly).

So it seems the limits are that only getc() and putc() may cause havoc in the way you foresee. Apart from those two, you should be safe, assuming your platform is at least one of sane or conforming.

like image 28
John Zwinck Avatar answered Oct 02 '22 10:10

John Zwinck