Program environment and preprocessing (below) - "C"

Hello everyone uu from CSDN, today Xiaoyalan’s content is the next knowledge point of program environment and preprocessing. Then, after writing this blog, the knowledge point of C language will end here, and the follow-up will Focus on brushing questions and reading, also about C language, can write some data structure and C++ content, okay, let's enter the world of program environment and preprocessing


Detailed preprocessing

        #and##

        Macro arguments with side effects

        Macros vs. Functions

        naming convention

#undef

command line definition

conditional compilation

file contains

Other preprocessing directives


Detailed preprocessing

 Program environment and preprocessing (on) - "C"

In fact, the knowledge points here in Xiao Yalan's last blog are not finished, so keep going! ! !

#and##

How to insert parameters into the string?  

First let's look at this code:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	char* p = "hello ""bit\n";
	printf("hello"" bit\n");
	printf("%s", p);
}

Is the output here hello bit ?

The answer is definite: yes.

We found that strings are characterized by automatic connection.  

1. Can we write such code? :

#define PRINT(FORMAT, VALUE)\
 printf("the value is "FORMAT"\n", VALUE);

//...
PRINT("%d", 10);

Here only when the string is used as a macro parameter can the string be placed in the string.

2. Another trick is: use # to turn a macro parameter into a corresponding string .

For example: want to print out some content, the format of the content is the value of () is %()\n , the form of printing of these content is uncertain, and the name of each variable is also different, in Under such circumstances, can only printf print one line after another? Obviously, this is not suitable for encapsulation into a function, because when encapsulating a function, the parameter types of the function are fixed and hard-coded. So, is there any other better way?

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int a = 10;
	printf("the value of a is %d\n", a);
	int b = 20;
	printf("the value of b is %d\n", b);
	float f = 3.14f;
	printf("the value of f is %f\n", f);
	return 0;
}

This method is quite cuo! ! !

We can use the form of #define to accomplish this task:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define print_format(num,format)printf("the value of "#num" is "format,num)
int main()
{
	int a = 10;
	//printf("the value of a is %d\n", a);
	print_format(a , "%d\n");
	int b = 20;
	//printf("the value of b is %d\n", b);
	print_format(b , "%d\n");
	float f = 3.14f;
	//printf("the value of f is %f\n", f);
	print_format(f , "%f\n");
	return 0;
}

 

 

int i = 10;
#define PRINT(FORMAT, VALUE)printf("the value of " #VALUE " is "FORMAT "\n", VALUE);
//...
int main()
{
	PRINT("%d", i + 3);//产生了什么效果?
}

The #VALUE in the code will be processed by the preprocessor as: "VALUE" .

 ## role

## can combine the symbols on both sides of it into one symbol, which allows macro definitions to create identifiers from separate text fragments.

 Such a connection must result in a valid identifier. Otherwise the result is undefined.

#define ADD_TO_SUM(num, value)\
      sum##num += value;
//...
int main()
{
	ADD_TO_SUM(5, 10);//作用是:给sum增加10.
}

Macro arguments with side effects

When a macro parameter appears more than once in the definition of the macro, if the parameter has side effects, then you may be dangerous when using this macro, resulting in unpredictable consequences. Side effects are permanent effects that occur when an expression is evaluated.

int a=10;
int b=a+1;//b得到的是11,a还是10
//不带副作用
int a=10;
int b=a++;//b得到了11,但是a也变了,变成了11
//带有副作用

The MAX macro can demonstrate problems caused by parameters with side effects.

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define MAX(X,Y)((X)>(Y)?(X):(Y))
int main()
{
	int a = 3;
	int b = 5;
	int c = MAX(a++, b++);
	printf("%d\n", c);//6
	printf("%d\n", a);//4
	printf("%d\n", b);//7
}

 Why is it such a result?

Here we need to know what the result of the preprocessor is:

c = ( (a++) > (b++) ? (a++) : (b++));

 After a++ is executed, the value of a++ is used first, and then ++, but a will be incremented.

Then, the value of a++ is 3, the value of b++ is 5, a is incremented to 4, b is incremented to 6, and then b++ is executed, the value of b++ becomes 6, and b is incremented to 7.

So the final value is: a=4 b=7 c=6


Macros vs. Functions

Macros are usually used to perform simple operations, such as finding the larger of two numbers.

#define MAX(a, b) ((a)>(b)?(a):(b))

So why not use a function for this task?

There are two reasons:

The code to call and return from a function may take more time than it takes to actually perform this small computational work. So macros are better than functions in terms of program size and speed.

More importantly, the parameters of the function must be declared as specific types. So functions can only be used on expressions of the appropriate type . On the contrary, this macro can be applied to integer, long integer, floating point and other types that can be used for > to compare. Macros are type independent.

The time spent on the function call:

  • Preparation before function call (parameter passing, maintenance of function stack frame space)
  • main operation
  • Function return, processing of return value, destruction of function stack frame 

 Disadvantages of macros:

Of course, compared with functions, macros also have disadvantages:

  • Every time a macro is used, a copy of the code defined by the macro is inserted into the program. Unless the macro is relatively short, it can increase the length of the program considerably.
  • Macros cannot be debugged.
  • Macros are less rigorous because they are type-independent.
  • Macros can introduce problems with operator precedence, making programs prone to bugs.

 Next, let's look at an example:

        Find the greater of two numbers

Method 1: Function method

#include<stdio.h>
int Max(int a, int b)
{
	return a > b ? a : b;
}
int main()
{
	int a = 0;
	int b = 0;
	//输入
	scanf("%d %d", &a, &b);
	int m1 = Max(a, b);
	printf("%d\n", m1);
	return 0;
}

 

Method 2: Macro method

#include<stdio.h>
#define MAX(X,Y)((X>Y)?(X):(Y))
int main()
{
	int a = 0;
	int b = 0;
	//输入
	scanf("%d %d", &a, &b);
	int m2 = MAX(a, b);
	printf("%d\n", m2);
	return 0;
}

 

 Macros can sometimes do things that functions cannot.

For example: macro parameters can have types, but functions cannot.

#include<stdio.h>
#define MALLOC(num,type)  (type*)malloc(num*sizeof(type))
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		//......
	}
	int* p2 = MALLOC(10, int);
	if (p2 == NULL)
	{
		//......
	}
	return 0;
}

A comparison of macros and functions

When the function is relatively simple, you can use macros to implement it. If the function is more complicated, it is recommended to use functions to implement it! ! !

Attributes #defineDefine macros function
code length Macro code is inserted into the program each time it is used. Except for very small macros, the length of the program can grow substantially The function code appears in only one place; every time the function is used, the same code in that place is called
execution speed faster There is additional overhead for function calls and returns, so it is relatively slow
operator precedence The evaluation of macro parameters is in the context of all surrounding expressions. Unless parentheses are added, the precedence of adjacent operators may produce unpredictable results, so it is recommended to use more parentheses when writing macros. Function parameters are evaluated only once when the function is called, and its resulting value is passed to the function. The result of evaluating an expression is more predictable.
Parameters with side effects Parameters may be substituted in multiple places within the macro body, so parameter evaluation with side effects may produce unpredictable results. Function parameters are only evaluated once when they are passed, and the result is easier to control.
Parameter Type The parameter of macro has nothing to do with the type, as long as the operation on the parameter is legal, it can be used for any parameter type. The parameters of a function are related to the type. If the types of the parameters are different, different functions are required, even if they perform the same task.
debugging Macros are inconvenient to debug Functions can be debugged statement by statement
recursion Macros cannot be recursive functions can be recursive


naming convention

In general, functions and macros have very similar syntax.

So language itself cannot help us distinguish between the two.

Then one of our usual habits is:

capitalize macro names

Do not use all uppercase function names


#undef

This command is used to remove a macro definition.

#undef NAME

//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。

command line definition

Many C compilers provide the ability to define symbols on the command line. Used to start the compilation process.

For example: this feature is useful when we want to compile different versions of a program based on the same source file. (Suppose an array of a certain length is declared in a program. If the machine memory is limited, we need a small array, but if another machine has a larger memory, we need an array that can be larger.)

#include <stdio.h>
int main()
{
	int array[ARRAY_SIZE];
	int i = 0;
	for (i = 0; i < ARRAY_SIZE; i++)
	{
		array[i] = i;
	}
	for (i = 0; i < ARRAY_SIZE; i++)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
	return 0;
}

Compile instructions:

//linux environment demo

gcc -D ARRAY_SIZE=10 programe.c


conditional compilation

When compiling a program, it is very convenient if we want to compile or discard a statement (a group of statements). Because we have conditional compilation directives.

For example:

For debugging code, it is a pity to delete it, but to keep it in the way, so we can selectively compile it.

#include <stdio.h>
#define __DEBUG__
int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
#ifdef __DEBUG__
		printf("%d\n", arr[i]);//为了观察数组是否赋值成功。 
#endif __DEBUG__
	}
	return 0;
}

 

 

 Common conditional compilation directives:

1.

#if constant expression

//...

#endif

// Constant expressions are evaluated by the preprocessor.

like:

#define __DEBUG__ 1 #if __DEBUG__

//...

#endif

2. Conditional compilation of multiple branches

#if constant expression

//...

#elif constant expression

//...

#else

//...

#endif

3. Determine whether it is defined

#if defined(symbol)

#ifdef symbol

#if !defined(symbol)

#ifndef symbol

4. Nested instructions

#if defined(OS_UNIX)

        #ifdef OPTION1

                unix_version_option1();

        #endif

        #ifdef OPTION2

                unix_version_option2();

        #endif

#elif defined(OS_MSDOS)

        #ifdef OPTION2

                msdos_version_option2();

        #endif

#endif


file contains

We already know that the #include directive can cause another file to be compiled. Just like where it actually appears in the #include directive.

The way this replacement works is simple: the preprocessor first removes the directive and replaces it with the contents of the include file.

Such a source file is included 10 times, it is actually compiled 10 times.

The way header files are included:

The local file contains:

#include "filename"

Search strategy: First search in the directory where the source file is located. If the header file is not found, the compiler will search for the header file in the standard location just like searching for the library function header file.

If not found, a compilation error will be prompted.

The library files contain:

#include <filename.h>

To find the header file, go directly to the standard path to find it. If it cannot find it, it will prompt a compilation error.

In this way, can it be said that the form of "" can also be used for library files?

The answer is yes: yes .

But the efficiency of searching in this way is lower. Of course, it is not easy to distinguish whether it is a library file or a local file.

The path to the standard header files for the Linux environment:

/usr/include

The path to the standard header file for the VS environment:

 

The nested file contains

 If such a scenario occurs:

comm.h and comm.c are common modules.

test1.h and test1.c use common modules.

test2.h and test2.c use common modules.

test.h and test.c use the test1 module and test2 module.

In this way, two copies of comm.h will appear in the final program. This creates duplication of file content.

how to solve this problem?

Answer: conditional compilation.

 

At the beginning of each header file write:

#ifndef __TEST_H__
#define __TEST_H__

//头文件的内容

#endif   //__TEST_H__

or:

#pragma once

You can avoid repeated introduction of header files.


Other preprocessing directives


 Alright, this is the end of Xiao Yalan's C language column, thank uu for watching.

 Will continue to work hard in the future! ! !

 

Guess you like

Origin blog.csdn.net/weixin_74957752/article/details/130090387