#define Directive

This page was translated by a robot.

Using the #definedirective, a macro is assigned to a name, which replaces the name in the entire code that follows. Macros can have parameters and can therefore be called in the code in a similar way to functions . However, the use of such macros is insidious.









PI = 3.140000
I love ManderC
Hypothenuse = 5.00
#include <stdio.h>
#include <math.h>

#define PI 3.14
#define TRUE_LOVE "ManderC"
#define PYTHAGORAS(a,b) sqrt(a*a+b*b)

int main(){
  printf("PI = %f\n", PI);
  printf("I love %s\n", TRUE_LOVE);
  printf("Hypothenuse = %1.2f\n", PYTHAGORAS(3,4));
  return 0;
}

Details

A macro is basically nothing more than a text snippet, which is copied to the places where the name of the macro occurs during compilation in a pre-processing step (pre-processing) by means of simple replacement. With the exception of certain preprocessor operators and any macro parameters, any characters (including whitespace) of the #definetext specified in the directive are simply copied one-to-one and then processed by the actual compiler.

Macros are very well suited for universal constants such as pi, e, square root of 2, the force of gravity g and the like. Such unchangeable constant values ​​are preferably to be defined as macros insofar as they do not have to be read from the memory during the program run like a variable, but are compiled directly in the program code.

Macros can simplify the writing of repetitive program code because they can be used almost anywhere, thus eliminating tedious typing.

Furthermore, macros are often used for conditional compilation , for example with flags like #define USE_ACCURATE_METHOD 1. If a macro's name is not defined, any usage will be 0evaluated in a preprocessor condition. If an undefined macro is used in the code to be compiled, the compiler reports an error.

Macros are only evaluated when they are needed, each time anew:


UNDEFINED_NUMBER

42
#define FOURTY_TWO UNDEFINED_NUMBER
FOURTY_TWO
#define UNDEFINED_NUMBER 42
FOURTY_TWO

Macros are usually written in CAPITAL LETTERS. Furthermore, it is recommended to write macros that are as small and manageable as possible. A macro must be defined on a single line. Multi-line macros can be reached using a backslash \ at the end of the line .

Macros are not without problems! A few examples are given below of where sources of error lie and how macros can be written as safely as possible based on this.

Parameterized macros

Similar to functions, macros can define parameters. ,To do this, a comma- separated list of parameter names in round brackets must ()follow directly after the macro name . There must be no whitespace between the macro name and the opening parenthesis! Parameterized macros can also have any number of parameters using the ellipsis ....

When a parameterized macro occurs, the preprocessor will copy the macro text to the position that occurred, as described above, but then also replace all parameter names with the corresponding arguments passed. The following example shows the output of the preprocessor on the left:


printf("%d\n", 6*7);
#define MULTIPLY(x, y) x*y
printf("%d\n", MULTIPLY(6, 7));

Note that a macro argument does not represent a value like a function, but can be any text. The preprocessor will thus blindly translate the parameters through the arguments and finally transfer the result to the compiler.

printf("%d\n", 3+3*7);
printf("%d\n", MULTIPLY(3+3, 7));

As just shown in this example, many problems can arise due to the blind substitution of macros and their parameters. Some important error sources are explained below.

Macro arguments with side effects

The use of macros with arguments is not popular with every programmer. It is often forgotten that macros are not read or called like variables or functions, but are written character by character directly into the program code by the preprocessor. The following program illustrates the problem:









520932930, 520932930
28925691, 822784415
#include <stdio.h>
#include <stdlib.h>

void print_twice(int v){ printf("%d, %d\n", v, v);}
#define PRINT_TWICE(v)   printf("%d, %d\n", v, v)

int main(){
  srand(0);
  print_twice(rand());
  PRINT_TWICE(rand());
  return 0;
}

While the first variant outputs the same value twice, the macro variant outputs two different values, even though the macro and the function define the same code. The difference is that with the first variant, the function rand()is called first and the result (a random number) is passed to the function as an argument. With the macro definition, on the other hand, the code rand()itself is passed, which the preprocessor finally converts into the following line:

printf("%d, %d\n", rand(), rand());

This means that it rand()is called twice. However , the randfunction has a side effect: With each call, a variable that is hidden from the programmer is changed. This also changes the output result.

Whenever macro parameters occur multiple times in the macro text, the corresponding side effects of an expression are also executed multiple times. Unfortunately, not using arguments with side effects is just as difficult as writing macros where the parameters are guaranteed not to appear more than once in the macro text. Fortunately, errors like this rarely occur. The author's recommendation is to replace complicated macros with a corresponding function (possibly with the inlinekeyword ) in the event of an error at the latest.

Other examples of expressions with side effects are assignments or the increment and decrement operators. Further explanations can be found in the sequence points .

Directives as macros

Macros cannot contain any other directives. Both the leading #and the name of the directive are not resolved by the preprocessor:

expected unqualified-id
before '#' token
#define DEFINE_PI #define PI 3.14
DEFINE_PI

The error this produces is confusing. It is created by the compiler, not the preprocessor. The above example translates to the following line:

#define PI 3.14

However, the preprocessor then terminates its service and transfers this code to the actual compiler, which #cannot do anything with it.

The extra semicolon ;

Since the preprocessor converts character by character, there is no need to end a macro with a semicolon ;if you want the expression to look like a function in actual code.




sqrt(3*3 +4*4);;
sqrt(3*3 +4*4);
#define PYTHAGORAS_1(a,b) sqrt(a*a+b*b);
#define PYTHAGORAS_2(a,b) sqrt(a*a+b*b)

PYTHAGORAS_1(3,4);
PYTHAGORAS_2(3,4);

In this example, this measure looks like an unimportant cosmetic correction. However, if this macro is not written as a stand-alone statement but as part of an expression, the additional semicolon is extremely problematic, since the semicolon is copied into the expression, which leads to a syntax error:

error
printf("%f", sqrt(3*3 +4*4););

Furthermore, there is another phenomenon in connection with macros, which is referred to as swallowing the semicolon . It has to do with writing a macro in front of a elsekeyword of a one-line ifcondition , which is programmed as a block of code . The following example illustrates this:












Error
#include <stdio.h>

#define CLIP_TO_CENTS(x) {x*=100.; x = (double)(int)x; x/=100.;}
#define CLIP_TO_5CENTS(x) {x*=20.; x = (double)(int)x; x/=20.;}

int main(){
  double mybankaccount = 5624.4736;
  
  int useswisscurrency = 1;
  if(useswisscurrency)
    CLIP_TO_5CENTS(mybankaccount);
  else
    CLIP_TO_CENTS(mybankaccount);

  printf("%f\n", mybankaccount);
  return 0;
}

The programmer has assumed that the macro can be terminated with a semicolon, similar to a statement. However, the ifsyntax of the part has already been completed by the closing curly brackets of the macro. The C syntax now expects either the elsekeyword or a new statement. By additionally specifying the semicolon, the compiler assumes the latter and regards the ifstatement as complete. It then finds one elsewithout a matching one ifand reports an error.

To fix the error, either the semicolon must be removed, or the macro must be programmed to swallow the trailing semicolon . Usually this is solved by the following construct:

#define CLIP_TO_5CENTS(x) do {x*=20.; x = (double)(int)x; x/=20.;} while(0)

The implementation of the macro is therefore still in a block, but due to the do– whileloop it is implemented as a control structure, which always expects a semicolon at the end. The termination condition 0guarantees that the loop runs exactly once. The compiler will eventually optimize this construction out.

bracketing of calculations

There are fixed rules for the preprocessor or compiler as to the order in which operands are converted. The programmer can control this order with brackets, among other things. With macros, it is recommended to always use parentheses around calculations. The following example shows what happens if a calculation is not bracketed correctly:






1 / PI_HALF: 0.159236
1 / PI_HALF: 0.636943
#include <stdio.h>
#define PI_HALF_1  3.14/2.0
#define PI_HALF_2 (3.14/2.0)

int main(){
  printf("1 / PI_HALF: %f\n",  1. / PI_HALF_1);
  printf("1 / PI_HALF: %f\n",  1. / PI_HALF_2);
  return 0;
}

The preprocessor translates the code into the following lines:

printf("1 / PI_HALF: %f\n", 1. /  3.14/2.0 );
printf("1 / PI_HALF: %f\n", 1. / (3.14/2.0));

In the first line, the 1 / PIcalculation is performed first and then the result is 2divided by. However, it would be correct to calculate first, as in the second line PI / 2, and then take the reciprocal of the result.

bracketing of casts

Explicit casts are often made in macros. In the vast majority of cases this will not cause any problems, however the following program shows a simple example of what could happen:









Int of 1.5 + 1.5 is 2.50

Int of 1.5 + 1.5 is 3.00
#include <stdio.h>
#define INTEGRAL_1(x) (int) x
#define INTEGRAL_2(x) (int)(x)

int main(){
  float a = 1.5;
  float d;
  d = INTEGRAL_1(a + 1.5f);
  printf("Int of 1.5 + 1.5 is %1.2f\n", d);
  d = INTEGRAL_2(a + 1.5f);
  printf("Int of 1.5 + 1.5 is %1.2f\n", d);
  return 0;
}

The two lines with the calculation are converted by the preprocessor as follows:

d = (int) a + 1.5f;
d = (int)(a + 1.5f);

This makes it clear that the first line only acasts, while the second line converts the entire calculation a + 1.5into an integer.

Forgotten cast parenthesis errors can get even worse when used in conjunction with pointer conversions, even causing the program to crash. it is recommended to add extra parentheses around the whole macro. The following example tries to cast a two-dimensional array as one-dimensional:








Number: -1073743256
Number: -1073743256
Number: 2
#include <stdio.h>
#define CASTING_1(x)  (int*) x
#define CASTING_2(x)  (int*)(x)
#define CASTING_3(x) ((int*)(x))

int main(){
  int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
  printf("Number: %i\n", CASTING_1(a)[1]);
  printf("Number: %i\n", CASTING_2(a)[1]);
  printf("Number: %i\n", CASTING_3(a)[1]);
  return 0;
}

The lines are converted by the preprocessor as follows:

printf("Number: %i\n",  (int*) a  [1]);
printf("Number: %i\n",  (int*)(a) [1]);
printf("Number: %i\n", ((int*)(a))[1]);

While in the first two lines the element with index [1]is addressed first and then erroneously dereferenced (which leads to a number in the nirvana of unused memory) according to the operator sequence specified by the programming language, it is only in the third line that the array is first correctly displayed as one-dimensional Array cast and then the element [1]addressed with index.

Bracketing of dereferences

The following example shows what happens if a dereferencing is not bracketed correctly:







Number: 2
Number: 4
#include <stdio.h>
#define VALUE_1 (*numbers)
#define VALUE_2  *numbers

int main(){
  int numbers[3][3] = {1,2,3,4,5,6,7,8,9};
  printf("Number: %d\n", VALUE_1[1]);
  printf("Number: %d\n", VALUE_2[1]);
  return 0;
}

The preprocessor translates the code into the following lines:

printf("Number: %d\n", (*numbers)[1]);
printf("Number: %d\n",  *numbers [1]);

While in the first row the first three-tuple {1,2,3}is dereferenced first and then the element 2with an index [1]is selected, in the second row the three-tuple {4,5,6}with an index [1]is first selected and then the first element is 4dereferenced.