服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C语言深入讲解宏的定义与使用方法

C语言深入讲解宏的定义与使用方法

2022-11-11 14:43清风自在 流水潺潺 C/C++

在 C 语言中,可以采用命令 #define 来定义宏。该命令允许把一个名称指定成任何所需的文本,例如一个常量值或者一条语句。在定义了宏之后,无论宏名称出现在源代码的何处,预处理器都会把它用定义时指定的文本替换掉

一、C语言中的宏定义

  • #define是预处理器处理的单元实体之一
  • #define 定义的宏可以出现在程序的任意位置
  • #define 定义之后的代码都可以使用这个宏
  • #define 定义的宏常量可以直接使用
  • #define 定义的宏常量本质为字面量

下面的宏常量定义正确吗?

C语言深入讲解宏的定义与使用方法

编写代码来测试:

#define ERROR -1
#define PATH1 "D:\test\test.c"
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
test.c

int main()
{
  int err = ERROR;
  char* p1 = PATH1;
  char* p2 = PATH2;
  char* p3 = PATH3;
}

先使用gcc -E Test.c -o Test.i 进行预编译,预编译没有报错,结果如下:

# 1 "Test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "Test.c"

int main()
{
  int err = -1;
  char* p1 = "D:\test\test.c";
  char* p2 = D:\test\test.c;
  char* p3 = D:\testtest.c;
}

直接进行编译,发现 char* p2 = PATH2; char* p3 = PATH3; 报错

C语言深入讲解宏的定义与使用方法

这说明宏定义是正确的,但是编译是过不了的,只是

#define PATH2 D:\test\test.c

#define PATH3 D:\test\

不符合语法规范。

 

二、宏定义表达式

  • #define 表达式的使用类似函数调用
  • #define 表达式可以比函数更强大
  • #define 表达式比函数更容易出错

强大之处其中之一就是可以求数组的大小,这是不能编写函数办到的。

下面看一段宏表达式的代码:

#include <stdio.h>

#define _SUM_(a, b) (a) + (b)
#define _MIN_(a, b) ((a) < (b) ? (a) : (b))
#define _DIM_(a) sizeof(a)/sizeof(*a)

int main()
{
  int a = 1;
  int b = 2;
  int c[4] = {0};

  int s1 = _SUM_(a, b);
  int s2 = _SUM_(a, b) * _SUM_(a, b);
  int m = _MIN_(a++, b);
  int d = _DIM_(c);

  printf("s1 = %d\n", s1);
  printf("s2 = %d\n", s2);
  printf("m = %d\n", m);
  printf("d = %d\n", d);

  return 0;
}

下面为输出结果,但是 s2 我们预期的结果应该是 9,m 的值我们预期的结果应该是 1,这是怎么回事呢?

C语言深入讲解宏的定义与使用方法

下面进行预编译看看代码到底是怎么运行的,输入 gcc -E Test.c -o Test.i

int main()
{
  int a = 1;
  int b = 2;
  int c[4] = {0};

  int s1 = (a) + (b);
  int s2 = (a) + (b) * (a) + (b);
  int m = ((a++) < (b) ? (a++) : (b));
  int d = sizeof(c)/sizeof(*c);

  printf("s1 = %d\n", s1);
  printf("s2 = %d\n", s2);
  printf("m = %d\n", m);
  printf("d = %d\n", d);

  return 0;
}

通过上面宏定义的替换,我们很容易知道为什么结果跟我们想的不一样。

 

三、宏表达式与函数的对比

  • 宏表达式被预处理器处理,编译器不知道宏表达式的存在
  • 宏表达式用“实参”完全替代形参,不进行任何运算
  • 宏表达式没有任何的“调用”开销
  • 宏表达式中不能出现递归定义

所以,下面递归定义就是错误的:

C语言深入讲解宏的定义与使用方法

 

四、有趣的问题

宏定义的常量或表达式是否有作用域限制?(没有)

下面看一个宏作用域分析的代码:

#include <stdio.h>

void def()
{
  #define PI 3.1415926
  #define AREA(r) r * r * PI
}

double area(double r)
{
  return AREA(r);
}

int main()
{
  double r = area(5);

  printf("PI = %f\n", PI);
  printf("d = 5; a = %f\n", r);
  
  return 0;
}

下面为输出结果:

C语言深入讲解宏的定义与使用方法

作用域的概念是针对 C 语言中的变量和函数,不针对宏。宏表达式被预处理器处理,编译器不知道宏表达式的存在。

 

五、强大的内置宏

含义 示例
_FILE_ 被编译的文件名 file1.c
_LINE_ 当前行号 25
_DATE_ 编译时的日期 Jan 31 2021
_TIME_ 编译时的时间 17:01:01
_STDC_ 编译器是否遵循标准C规范 1

下面看一个宏使用的综合示例:

#include <stdio.h>
#include <malloc.h>

#define MALLOC(type, x) (type*)malloc(sizeof(type)*x)

#define FREE(p) (free(p), p=NULL)

#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s)

#define FOREACH(i, m) for(i=0; i<m; i++)
#define BEGIN {
#define END   }

int main()
{
  int x = 0;
  int* p = MALLOC(int, 5);
  
  LOG("Begin to run main code...");
  
  FOREACH(x, 5)
  BEGIN
      p[x] = x;
  END
  
  FOREACH(x, 5)
  BEGIN
      printf("%d\n", p[x]);
  END
  
  FREE(p);
  
  LOG("End");
  
  return 0;
}

下面为输出结果:

C语言深入讲解宏的定义与使用方法

可以看到宏定义是很强大的,可以打印出日期,文件名,行号,不使用宏定义很难实现。

 

六、小结

  • 预处理器直接对宏进行文本替换
  • 宏使用时的参数不会进行求值和运算
  • 预处理器不会对宏定义进行语法检查
  • 宏定义时出现的语法错误只能被编译器检测
  • 宏定义的效率高于函数调用
  • 宏的使用会带来一定的副作用

到此这篇关于C语言深入讲解宏的定义与使用方法的文章就介绍到这了,更多相关C语言 宏的定义内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_43129713/article/details/123458247

延伸 · 阅读

精彩推荐
  • C/C++C++处理图存储的方式分享

    C++处理图存储的方式分享

    这篇文章主要介绍了C++处理图存储的方式分享,文章围绕邻接矩阵、邻接表、链式前向的主题展开详细内容,具有一定的参考价值,需要的小伙伴可以参考...

    苏州程序大白6652022-11-02
  • C/C++C++小知识:不要去做编译器的工作

    C++小知识:不要去做编译器的工作

    今天小编就为大家分享一篇关于C++小知识:不要去做编译器的工作,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小...

    修语讲编程3902021-07-18
  • C/C++C++优先队列用法案例详解

    C++优先队列用法案例详解

    这篇文章主要介绍了C++优先队列用法案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...

    华山青竹12102021-12-14
  • C/C++C++中的菱形继承深入分析

    C++中的菱形继承深入分析

    这篇文章主要介绍了C++中的菱形继承深入分析的相关资料,需要的朋友可以参考下...

    361251669332021-05-21
  • C/C++C++随机点名生成器实例代码(老师们的福音!)

    C++随机点名生成器实例代码(老师们的福音!)

    这篇文章主要给大家介绍了关于C++随机点名生成器的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    BeyondLimits10412021-07-12
  • C/C++C语言单链表实现通讯录管理系统

    C语言单链表实现通讯录管理系统

    这篇文章主要为大家详细介绍了C语言单链表实现通讯录管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    两片空白7322021-11-11
  • C/C++C++11新特性“=default”,“=delete”的使用

    C++11新特性“=default”,“=delete”的使用

    =default、=delete 是C++11的新特性,分别为:显式缺省(告知编译器生成函数默认的缺省版本)和显式删除(告知编译器不生成函数默认的缺省版本),本文就来介绍...

    君子黎8042021-11-09
  • C/C++C语言数据输入与输出实例详解

    C语言数据输入与输出实例详解

    这篇文章主要介绍了C语言数据输入与输出实例详解的相关资料,需要的朋友可以参考下...

    boy_cto_tony9402021-05-18