1.概要
#include <iostream>
//变量可以直接使用
#define fun() a =100;
//用变量计算可以
#define fun2(a) a*2;
//用变量替换可以
#define fun3(a) d[a] = a;
//##链接的作用,一般用于链接变量名
#define fun4(type,name) type name##_s = 4;
//#的作用是转换成字符串
#define fun5(a) #a;
2.代码
#include <iostream>
//变量可以直接使用
#define fun() a =100;
//用变量计算可以
#define fun2(a) a*2;
//用变量替换可以
#define fun3(a) d[a] = a;
//##链接的作用,一般用于链接变量名
#define fun4(type,name) type name##_s = 4;
//#的作用是转换成字符串
#define fun5(a) #a;
int main()
{
int a;
fun();
std::cout << a << "\n";
int b = 5;
int c = fun2(b);//b*2
std::cout << c << "\n";
int d[3];
for (size_t i = 0; i < 3; i++)
{
fun3(i);// d[i] = i
}
for (size_t i = 0; i < 3; i++)
{
std::cout << d[i] << "\n";
}
fun4(int, ta);//int ta_s = 5;
std::cout << ta_s << "\n";
int e = 5;
const char* f = fun5(e);
std::cout << f << "\n";
std::cout << "Hello World!\n";
}
3.运行结果
100
10
0
1
2
4
e
Hello World!
4.附加
4.1c 宏的返回值如何确定
在C语言中,宏(macros)本身并不返回值,因为宏是预处理器(preprocessor)在编译前进行文本替换的工具。当你在代码中调用一个宏时,预处理器会简单地将宏名替换为其定义的内容。
但是,我们可以说宏"展开"后的代码可以产生返回值,这取决于宏是如何定义的以及它是如何被使用的。
例如,一个简单的宏可能定义如下:
|---|---------------------------------------------|
| | #define MAX(a, b) ((a) > (b) ? (a) : (b))
|
这个宏用于比较两个值并返回较大的一个。但是,请注意,这并不是宏本身"返回"值,而是宏展开后的代码(即条件运算符表达式)在运行时返回值。
要确定宏"展开"后的代码如何返回值,你需要查看宏的定义并理解它是如何工作的。在上述例子中,MAX
宏使用了条件运算符(?:
),该运算符在C语言中用于根据条件返回两个值中的一个。
注意:由于宏只是文本替换,所以在使用宏时需要特别小心,以避免意外的副作用或错误。例如,在上面的MAX
宏中,如果参数是复杂的表达式或具有副作用的表达式(如自增或自减),则可能会导致问题。为了避免这些问题,通常建议使用括号将宏的参数括起来,如上例所示。
4.2 #的作用
在C和C++编程中,#
符号在预处理器指令中有特殊的作用,但它本身并不是宏的一部分。预处理器指令是以 #
开头的特殊指令,它们在编译器的实际编译过程之前由预处理器处理。
以下是一些常见的以 #
开头的预处理器指令:
-
#include
:用于包含其他文件的内容。|---|----------------------------------------|
| |#include <stdio.h> // 包含标准输入输出头文件
|
| |#include "myheader.h" // 包含当前项目中的头文件
| -
#define
:用于定义宏。|---|--------------------------------------------------------------------|
| |#define PI 3.14159 // 定义一个名为PI的宏,其值为3.14159
|
| |#define MAX(a, b) ((a) > (b) ? (a) : (b)) // 定义一个名为MAX的宏,用于比较两个数
| -
#undef
:用于取消已定义的宏。|---|-------------------------|
| |#undef PI // 取消PI宏的定义
| -
#if
、#elif
、#else
、#endif
:用于条件编译。|---|--------------|
| |#if DEBUG
|
| |// 调试代码
|
| |#else
|
| |// 非调试代码
|
| |#endif
| -
#ifdef
和#ifndef
:用于检查某个宏是否已定义。|---|------------------------------------|
| |#ifdef SOME_MACRO
|
| |// 如果SOME_MACRO已定义,则包含此代码
|
| |#endif
|
| | |
| |#ifndef SOME_OTHER_MACRO
|
| |// 如果SOME_OTHER_MACRO未定义,则包含此代码
|
| |#endif
| -
#line
:用于改变预处理器对行号和文件名的跟踪。|---|----------------------------------------------------------|
| |#line 100 "newfile.c" // 将接下来的代码视为在newfile.c文件的第100行开始
| -
#error
和#warning
:用于在编译时生成错误或警告消息。|---|-----------------------------------------------|
| |#if SOME_CONDITION_THAT_SHOULD_NOT_HAPPEN
|
| |#error "This condition should not happen!"
|
| |#endif
|
| | |
| |#warning "This is a warning message."
| -
#pragma
:这是一个特定于编译器的指令,用于提供编译指示,如优化设置、警告控制等。|---|-----------------------------------------|
| |#pragma once // 一些编译器支持此指令来防止头文件被多次包含
|
请注意,虽然 #
符号在预处理器指令中很重要,但它本身并不是C或C++语言的一部分。在C或C++的代码中,你不能直接使用 #
符号(除了作为预处理器指令的一部分)来执行任何操作或定义变量。
4.3 # 在宏表达式内的作用
在宏表达式内部,#
符号具有特殊的作用,当它与 ##
一起使用时,它们是 C/C++ 预处理器的运算符,分别被称为"字符串化"运算符和"标记连接"运算符。
-
字符串化运算符
#
:当
#
运算符用于宏参数时,它会把该参数转换为一个用双引号括起来的字符串。例如:|---|-----------------------------------------------------------------------------------------------|
| |#define TO_STRING(x) #x
|
| |int main() {
|
| |printf("%s\n", TO_STRING(hello)); // 输出 "hello"
|
| |printf("%s = %d\n", TO_STRING(PI), PI); // 假设 PI 是一个宏,输出 "PI = 3.14159"(假设 PI 定义为 3.14159)
|
| |return 0;
|
| |}
|在这个例子中,
TO_STRING(hello)
会被替换为"hello"
,而TO_STRING(PI)
会被替换为"PI"
。 -
标记连接运算符
##
:
##
运算符用于连接两个标记(token),通常用于连接宏参数和其他标记以形成新的标记。这在编写可重用和灵活的宏时特别有用。例如:|---|-----------------------------------------------------------|
| |#define CONCAT(x, y) x ## y
|
| |int xy = CONCAT(x, y); // 这将声明一个名为 xy 的变量
|
| | |
| |// 或者更复杂的用法,例如与类型一起使用
|
| |#define TYPED_VAR(type, name) type name
|
| |TYPED_VAR(int, myVar); // 这将声明一个名为 myVar 的 int 类型变量
|
| | |
| |// 使用 `##` 可以让类型也是动态的
|
| |#define TYPED_NAMED_VAR(type, name) type name ## _var
|
| |TYPED_NAMED_VAR(int, my); // 这将声明一个名为 my_var 的 int 类型变量
|需要注意的是,
##
运算符不能用于连接两个字符串字面量或其他类型的表达式;它只能用于连接标记。
在宏表达式中,#
和 ##
通常一起使用或单独使用,以提供宏的额外功能。然而,它们的使用需要谨慎,因为不正确的使用可能会导致编译错误或不可预测的行为。