The parenthesis operator in C. What is the effect in the following code

I played with a macro to enable / disable tracing when I exited with the following code when the macro is disabled:

int main() { ("Hello world"); } 

This code is valid, and I got the desired effect (nothing happens when the macro is disabled), but I could not understand what exactly was happening. Does the compiler see parentheses as a declaration of an "nameless" method?

To make the code clearer:

  #ifdef TRACE #define trace printf("%s %d -> ",__FILE__, __LINE__);printf else #define trace #endif int main() { trace("Hello world"); } 

Thanks in advance.

+4
source share
5 answers

If the function name is missing, as in the first example, then this is not a “parenthesis operator”. It is simply a syntax element of an expression that changes the relationship between operators and operands. In this case, he just does nothing. What you have is just an expression

 "Hello world"; 

which evaluates a value of type char * , and this value is ignored. You can surround this expression with an extra pair ()

 ("Hello world"); 

which will not change anything.

Similarly, you can write

 (5 + 3); 

in the middle of your code and get an expression that evaluates to a value of 8 , which is immediately discarded.

Typically, compilers do not generate code for expression statements that have no side effects. In fact, in C, the result of each expression operator is discarded, so the only expressions that "make sense" are expressions with side effects. Compilers are usually pretty good at detecting catchy statements and dropping them (sometimes with a warning).

A warning can be annoying, so writing unsuccessful expressions to an expression like

 "Hello world"; 

may not be a good idea. Typically, compilers recognize a void cast as a request not to generate this warning

 (void) "Hello world"; 

So, you might consider redefining your macro accordingly.

Of course, using the above trace technique, you should remember that if you put something that has a side effect as an argument to your macro

 trace("%d\n", i++); 

then in the "disabled" form it will look as follows

 ("%d\n", i++); 

(two subexpressions encoded by the comma operator in one expression). The side effect of increment i saved in this case; it is not disabled. All this is equivalent to simple

 i++; 

Also, if you use a function call as an argument

 trace(get_trace_name()); 

the disconnected form will look like

 (get_trace_name()); 

and the compiler may not be smart enough to realize that the call to get_trace_name() should be dropped. Therefore, be careful when using your macro. Avoid arguments with side effects, avoid arguments with function calls, unless, of course, you intend to save side effects when disabling the actual trace.

+18
source

Whether this will work or not depends on what you pass as arguments to the macro (see the side effect problem mentioned by AndreyT). In this case, it is benign. However, it is probably safer, as this will lead to the fact that the text will not be inserted during macro processing and TRACE will not be defined:

 #ifdef TRACE #define trace printf("%s %d -> ",__FILE__, __LINE__);printf #else #define trace( ... ) #endif 

Assuming your compiler supports variable macros. If it is, there may be a better definition:

 #ifdef TRACE #define trace( fmt, ...) printf("%s %d -> " fmt, __FILE__, __LINE__, __VA_ARGS__ ) ; #else #define trace( ... ) #endif 

Note that the absence of a comma between "%s %d -> " and fmt is intentional and mandatory. Also note that the fmt argument must be a literal string constant in order for contiguous literary string concatenation to occur - a variable of any type would throw an error, but it's bad practice to use a variable for the format specifier anyway.

+3
source
 ("Hello world"); 

- An expression that returns a constant pointer to a string. This value is not consumed.

The brackets do not have a special role, and you can omit them:

 "Hello world"; 
+2
source
 #ifdef TRACE #define trace printf("%s %d -> ",__FILE__, __LINE__);printf #else #define trace #endif int main { trace("Hello world"); } 

The way macros work in C is that the compiler (essentially) * does a literal replacement for the identifier.

So, in your case there are two options depending on the value of #IFDEF

trace("Hello world");

can be

  • printf("%s %d -> ",__FILE__, __LINE__);printf("Hello world");

or

  1. ("Hello world");

The first option is a sequence of valid C code consisting of two printf statements. The second option is a sequence of valid C code that consists of a string (char *) inside unnecessary curly braces.

0
source

If your compiler supports C99 (or you are using gcc that had this function before), you can use variable macros:

 #ifdef TRACE #define trace(...) printf("%s %d -> ",__FILE__, __LINE__);printf(__VA_ARGS__) #else #define trace(...) #endif 

This avoids the problems that you might get with the side effects in the arguments. If you have a strict C89 compiler, you need to avoid side effects ...

0
source

Source: https://habr.com/ru/post/1306808/


All Articles