Technology Sharing

【C language】—— Detailed explanation of preprocessing (Part 2)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Preface

In the previous issue【C language】—— Detailed explanation of preprocessing (Part 2)In the study of , we introduced the relevant knowledge of macro in preprocessing in detail, I believe everyone has gained a lot. Don't worry, let's continue to learn other knowledge about preprocessing in this issue.

7. # and ##

7.1 # Operators

  • # 运算符Converts a macro argument to a string literal. It is only allowed to appear in the replacement list of a macro that takes an argument.
  • # 运算符The operation performed can be understood as "stringification"

What does that mean?
Let's lay the groundwork first:

int mian()
{
	printf("hello"   "worldn");
	printf("helloworldn");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

What is the difference between the above two codes? Let's take a look:

insert image description here

You can see that the effect of two strings and one string is the same.C语言会把两个字符串天然连成一个字符串, adding a space in the middle doesn't help either.

Now there is such a scene:

int main()
{
	int a = 1;
	printf("The value of a is %dn", a);

	int b = 20;
	printf("The value of b is %dn", b);

	float f = 8.5f;
	printf("The value of f is %fn", f);

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

We found that the logic of the three codes is very相像Yes, but there are些许不同
  
So we thought, since they are so similar, can we封装成一个函数, for easy use?
But the function can't do this.
then what should we do?
We can try to solve it with macros.

#define Print(n, format) printf("The value of n is " format "n", n)

int main()
{
	int a = 1;
	Print(a, "%d");
	//printf("The value of a is %dn", a);

	int b = 20;
	Print(b, "%d");
	//printf("The value of b is %dn", b);

	float f = 8.5f;
	Print(f, "%f");
	//printf("The value of f is %fn", f);

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  
operation result:

insert image description here

we discover n n n It has not changed, so how should I modify it?
This is where our # operator comes in handy:# Converts a macro argument toString literals,Right now n n n become “ n ” “n” n

At this time, we can use the splicing method to make it

#define Print(n, format) printf("The value of " #n " is " format "n", n)
  • 1

  
Don’t understand? Read the explanation below and you will understand.
insert image description here

  

7.2 The ## operator

  ## You can put it in hisThe symbols on both sides are combined into one symbol, which allows macro definitions从分离的文本片段创建标识符## is calledMarking adhesiveSuch a connection must produce a合法The result is undefined otherwise.
  
As we said before, to write a function that finds the larger value of two numbers, we need to write different functions for different data types.

int int_max(int x, int y)
{
	return x > y ? x : y;
}
float float_max(float x, float y)
{
	return x > y ? x : y;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

  

insert image description here

This is inevitably too cumbersome, and there are many similarities between the two functions.
Is there a way to quickly create such a function?MouldSame, just apply a function and it will come out
  
We can write a

#define GENERIC_MAX(type) 
		type type##_max(type x, type y)
		{
			return x > y ? x : y;
		}
  • 1
  • 2
  • 3
  • 4
  • 5

  

#define GENERIC_MAX(type) 
		type type##_max(type x, type y)
		{
			return x > y ? x : y;
		}

GENERIC_MAX(int);    //相当于定义了一个函数int_max
GENERIC_MAX(float);  //相当于定义了一个函数float_max

int main()
{
	int r1 = int_max(3, 5);
	printf("%dn", r1);
	float r2 = float_max(2.3f, 7.6f);
	printf("%fn", r2);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

  
operation result:

insert image description here

  
We can also g c c gcc gcc Observe the pre-processed.i Files, have a more intuitive understanding

insert image description here

Of course, the function generated in this way is not convenient for debugging

That here ## What is its function?

add ##, the compiler will think they are a symbol
Let's take a look at## Effect:

insert image description here

  

8. Naming Conventions

Generally speaking, the syntax of functions and macros is very similar, so语言本身没法帮我们区分二者
  
One of our usual habits is:

  • Name the macroAll caps
  • Function nameDon't capitalize

Of course, these naming rules are not absolute.
for example o f f s e t offset offset This macro is written in all lowercase
  
Note o f f s e t offset offset It is used to calculate the offset of the structure members relative to the starting position of the structure
  

Nine,# u n d e f undef undef

  # u n d e f undef undef Instructions forRemove a macro definition

insert image description here
The above code uses in line 169# u n d e f undef undef Removed the MAX macro. It works fine when the 168th line is called before the removal, but an error occurs when the 170th line is called after the removal.

  

10. Command line definition

Many C compilers (not including VS) provide a capability to allow symbols to be defined on the command line.
  
  For example: This feature is useful when we want to compile different versions of a program based on the same source file. (Suppose a program declares an array of a certain length. If the machine has limited memory, we need a very small array, but if another machine has more memory, we need a larger array.)

insert image description here

insert image description here

The command line definition isPreprocessing stageIn the preprocessing stage, the above code s z sz sz The value of has been determined

  

11. Conditional compilation

When compiling a program, it is very important to compile or discard a statement (or a group of statements).方便Because we can useConditional compilation directives
  
The conditional compilation directive is this code我想让你编译就编译,不想让你编译你就不要编译了We can set a condition for it. If the condition is true, this code will be compiled. If the condition is false, this code will not be compiled.
  
For example:
Some debugging code is a pity to delete, but it is troublesome to keep it, so we can compile it selectively
  

insert image description here

  
Commonly used conditional compilation instructions:

11.1. Conditional compilation of a single branch

#if 常量表达式
    //···
#endif
  • 1
  • 2
  • 3

insert image description here

  

11.2. Conditional compilation of multiple branches

#if  常量表达式
	//···
#elif  常量表达式
	//···
#else
	//···
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Whichever statement is true, execute that statement

#define M 1
int main()
{
#if M == 0
	printf("hellon");
#elif M == 1
	printf("worldn");
#elif M == 2
	printf("csdnn");
#endif
	printf("886n");

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  

11.3. Determine whether it is defined

#if defined(symbol)
#ifdef symbol

//上面两个的反面
if !defined(symbol)
#ifndef symbol
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

insert image description here

  

11.4. Nested directives

#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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

  

12. Inclusion of header files

12.1 How header files are included

(1) How local files are included

# include "filename"
  • 1

Search strategy: FirstThe project directory where the source files are locatedIf the header file is not found, the compiler searches for it in the same way as it searches for a library function header file.Standard location searchhead File
If you can't find it,Compile Error
  
L i n u x Linux Linux The standard header file path for the environment (where the header files are placed):

 /usr/include
  • 1

Standard header file path for VS environment:

C:Program Files (x86)Microsoft Visual Studio 12.0VCinclude
//这是VS2013的默认路径
  • 1
  • 2

  

(2) Library files include

#include <filename.h>
  • 1

Find the header file directlyStandard PathGo down and search, if you can't find it, it will promptCompile Error
  
Can we say that we can also use it for library files?“ ” The form includes
the answer isaffimBut this is how to findRelatively low efficiencyOf course, this is alsoNot easy to distinguishIs it a library file or a local file?
  

12.2 Nested File Inclusion

After studying the previous (compile and link), we know that the header file is included in the preprocessing stage.直接将该文件的代码拷贝到包含头文件的地方
  
If a header file is included 10 times, it is actually compiled 10 times. If it is included repeatedly, the pressure on compilation will be greater.

t e s t . c test.c test.c

#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
int main()
{
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

t e s t . h test.h test.h

void test();
struct Stu
{
int id;
char name[20];
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

But in a project, a file is inevitably included multiple times, so how to solve this problem?
Answer:Conditional compilation

#ifndef __TEST_H__
#define __TEST_H__
		//头文件的内容
#endif
  • 1
  • 2
  • 3
  • 4

  
How do you understand it?

  • Should we compile the header file when we include it for the first time?Make a judgment first
  • The symbol __TEST_H__ does notNot definedwantCompile
  • NextDefine the __TEST_H__ symbol
  • After including the header file again, I found __TEST_H__Definedno longerFor the header file included laterCompile

However, the above writing method is more troublesome, there is another way to write it:

#pragma once
  • 1

The effect is the same as above.
This avoids repeated import of header files
  

13. Other preprocessing instructions

#error
#pragma
#line
···
#pragma pack()//在结构体部分介绍
  • 1
  • 2
  • 3
  • 4
  • 5

Interested friends can read "In-depth Analysis of C Language"
  
  
  
  
  


  Well, this is the end of this article about preprocessing. I hope this blog can help you. At the same time, if there are any mistakes, please correct me. Let us make progress together on the road of learning C language!