C语言之宏详解(超级详细!)

简介: C语言之宏详解(超级详细!)

一、用宏前须知-#define相关知识

       大致结构:

#define    name    stuff
         自定义名    内容

   栗子:

#define OP 256
#define YU "youarewelcome"
#define S(r) (r*r)
#define REG register          //为 register这个关键字,创建一个简短的名字
#define PRINT printf("file:%s line:%d\n", __FILE__, __LINE__)//__FILE__,__LINE__这是个啥?别急后面有补充!
#include<stdio.h>
int main()
{
  printf("%d \n", OP);
  printf("%s \n",YU);
  printf("%d \n", S(6));
  PRINT;
  return 0;
}

    对预定义符号的补充:

__FILE__      //进行编译的源文件 
__LINE__     //文件当前的行号 
__DATE__    //文件被编译的日期 
__TIME__    //文件被编译的时间 
__STDC__    //如果编译器遵循ANSI C,其值为1,否则未定义

二、用#define定义宏

       什么是宏?

       宏可以看作为一些命令的集合。它是一种预处理器指令,在预编译阶段将宏名替换为后面的替换体。


       而#define可以用来定义宏:


       #define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。


 宏的声明方式:

  #define name( parament-list ) stuff
//其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中#

 栗子:

       实现一个开平方的宏        

#define SQ(x) x*x
printf("%d \n", SQ(7));

他的实际执行结构为7*7->即为49

       还是用这个我们刚刚定义的宏

       我们执行下面这段代码:      

int a=6;
printf("%d \n", SQ(6+1));

他的实际执行结构为6+1*6+1->即为13


       这说明了什么?这印证了上面所说的宏是把参数替换到文本中,也就是宏直接吧6+1替换掉了x直接带入了后面的表达式当中了!


       对此,根据我们原来的用意。这时我们可以用括号来解决这个问题:


#define SQ(x) (x)*(x)

 这给了我们一个提醒:我们在用宏时最好多用括号,以此来避免不必要的错误!

再看个栗子:

#define DOUBLE(x) (x) + (x) 
int a = 5; 
printf("%d\n" ,10 * DOUBLE(a));

 在这个例子中我们对这个宏使用了括号,乍一看这个例子输出的值像是100

       但是实际呢?上图!

     

来看看实际它怎么执行的,它执行替换操作实际上为:

printf ("%d\n",10 * (5) + (5));

     因此我们说定义宏要多用括号!

#define DOUBLE( x)   ( ( x ) + ( x ) )

       改成这样就能实现想要的效果啦!

  #define的替换规则:

      在程序中扩展#define 定义符号和宏时,需要涉及几个步骤:

1. 在调用宏时,首先对参数进行检查,看看是否包含任何由 #define 定义的符号。如果是,它们首先被替换。

2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。

3. 最后,再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。如果是,就重复上 述处理过程。

      注意:

1. 宏参数和 #define 定义中可以出现其他 #define 定义的符号。但是对于宏,不能出现递归。

2. 当预处理器搜索 #define 定义的符号的时候,字符串常量的内容并不被搜索。

三、常用的宏定义

1、宏定义常量

        定义诸如:字符串、整形数、浮点型数等等的常量.

       栗子:

#define AO 114514
#define NTR "我可太喜欢了"
#define KK 3.14
#include<stdio.h>
int main()
{
  printf("%d %s %f", AO, NTR, KK);
  return 0;
}

     


2、定义一个宏语句

       栗子:

#include<stdio.h>
#define PFINT printf("hello world!\n");
int main()
{
  PFINT///预处理时会被替换为 printf("hello world!\n");
  return 0;
}

3、宏定义函数

       宏可以接受参数,类似于真正的函数一样。具体的接受参数方法与函数差不多,只不过不用指定函数类型,这也是宏定义函数的一个优势!比如:当我定义了一个函数有时要传整形,有时要传浮点型,函数可能要根据类型来多定义几个,而宏只需要一个就行了!

       栗子1:

#define MAX(a, b) ((a)>(b)?(a):(b))
#include<stdio.h>
int main()
{
  printf("%d", MAX(114, 514));
  return 0;
}

     

       栗子2:

#define MALLOC(num, type)\ 
(type *)malloc(num * sizeof(type)) //这和\是干啥的?详见地四大点\的作用
... 
//使用 
MALLOC(10, int);//类型作为参数 
//预处理器替换之后: 
(int *)malloc(10 * sizeof(int)); 

这个例子中,函数是做不到跟宏相同的效果的,所以说为啥有了函数我们还要定义宏呢?这个就是一个很明显的例子。


       宏与函数的对比:


4、#和## 分字符串化将两个符号连接成一个符号

#字符串化

       这个很好理解看看例子就会了!

       栗子:

#define STR(exp) printf("%s\n",#exp);
#include<stdio.h>
int main()
{
  STR(for fun)
  return 0;
}

     


##将两个符号连接成一个符号

       ##可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。

       栗子:

#include<stdio.h>
#define ADD_TO(num, value) num##value
int main()
{
  int a = ADD_TO(114, 514);
  printf("%d \n", a);
  return 0;
}

     


四、与宏相关的作用符号

1、换行符 '\'

       在每行末尾(除了最后一行)加上"\",代表换行的意思。这个目的是为了不让代码冗余,如果代码都挤在一段,代码就不美观,可读性不好。

      栗子:

#include<stdio.h>
#define PFINT printf("hello world!\n");\
        printf("goodbye world!\n");
#define NUM 1,\
      2,\
      3
int main()
{
  PFINT
  int x[] = { NUM };//->int x[] = { 1,2,3 };
  printf("%d %d %d \n", x[0], x[1], x[2]);
  return 0;
}

     


2、取消宏定义#undef

       这条指令用于移除一个宏定义。        

#define SORT 1000
#undef SORT

  在#undef之后SORT就相当于没有定义,失效了。再使用则会报错!


五、命名约定

       细心的朋友可能发现了!我在定义宏时用的都是大写的字母来作为它自定义的名字!


       这是因为这有一个默认的命名约定:


       一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。


       那我们平时的一个习惯是:


       把宏名全部大写


       函数名不要全部大写          

感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o!

相关文章
|
6月前
|
C语言
【C语言】库宏offsetof(结构体成员偏移量计算宏)
【C语言】库宏offsetof(结构体成员偏移量计算宏)
50 0
|
6月前
|
程序员 C语言 UED
详解C语言assert宏
详解C语言assert宏
72 0
|
6月前
|
存储 编译器 Linux
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
|
3月前
|
编译器 程序员 C语言
C语言 宏
C语言 宏
39 5
|
4月前
|
Linux C语言
C语言宏IS_REACHABLE
C语言宏IS_REACHABLE
38 1
|
6月前
|
编译器 C语言
C语言宏定义(#define定义常量​、#define定义宏​、 带有副作用的宏参数、 宏替换的规则、 宏函数的对比)
C语言宏定义(#define定义常量​、#define定义宏​、 带有副作用的宏参数、 宏替换的规则、 宏函数的对比)
|
6月前
|
安全 编译器 程序员
C语言(16)----预处理中的宏以及预处理指令
C语言(16)----预处理中的宏以及预处理指令
54 2
|
5月前
|
安全 编译器 C语言
【C语言进阶篇】offsetof宏的介绍 及其实现
【C语言进阶篇】offsetof宏的介绍 及其实现
|
5月前
|
编译器 程序员 C语言
【C语言进阶篇】assert宏 使用详解
【C语言进阶篇】assert宏 使用详解
|
6月前
|
C语言
C语言进阶21收尾(编程练习)(atoi,strncpy,strncat,offsetof模拟实现+找单身狗+宏交换二进制奇偶位)(下)
C语言进阶21收尾(编程练习)(atoi,strncpy,strncat,offsetof模拟实现+找单身狗+宏交换二进制奇偶位)
40 0