C语言预定义学习笔记

一:#define 替换文本,

最简单的预定义指令

#define MAX 100

1:格式注意 #define+空格(至少一个空格,最多可以多少个空格没有尝试过)+MAX(宏名;注意一般情况下,宏名需要使用全大写,虽然没有强制要求,但是这是现在默认的习惯)+空格(至少一个空格,最多可以多少个空格没有尝试过)+替换文本(可以是任何常量,数字常量,字符,字符串,都是可以的

2:如上 宏名是客户定义标识符,在程序中如果定义一个变量,变量名和宏名重复,编译器就会报错

#include <stdio.h>
#include <stdlib.h>
#define  MAX  100

int main()
{   int MAX=100;
    printf("Hello world!\n");
    return 0;
}

 3:替换文本

可以是任意合法的常量,数字+字符+字符串,都是合法的

#define  MAX  100
#define  MAC  0x45
#define  MAV  "Clanguage"
#define  MAB  'A'

4:宏名不可重复定义,但是不同的的宏名可以代替相同的 替代文本

#define  MAX  100
#define  MAX  0x45
#define  MAV  100
#define  MAB  'A'

编译阶段就会报错,

#define  MAX  100
//#define  MAX  0x45
#define  MAV  100
#define  MAB  'A'

第1行和第3行 虽然替换文本相同,但是编译阶段就能通过,也不会报错,在代码中遇到MAX,和MAV都用 100 代替。也很好理解,如果第1行和第2行,宏名相同,编译器就不知道到底是用100代替还是0x45代替

5:宏名的嵌套

也就是已经定义的宏名,可以放在另外一个宏定义的“替换文本”位置

#define  MAX  100
#define  MAC  0x45
#define  MAV  100
#define  MAB  'A'
#define  MAN   MAB

最后两行,就是宏定义的嵌套,如果你愿意基本可以无限套娃。其实本质和上面讲到的不同宏名 替代相同的“替代文本”是一样的

6:格式上还要注意一点,预定义结尾是不需要加“;”的。不要习惯就加上了。另外我们需要注意,宏定义,我们不称为“宏定义语句”,在C中语句是一定要在结尾加上;的。

7:宏定义,可以出现在任意位置,并且和在程序一开始的宏定义一样,在全局范围内,都能使用。

#include <stdio.h>
#include <stdlib.h>
#define  MAX  100
#define  MAC  0x45
#define  MAV  100
#define  MAB  'A'
#define  MAN   MAB
int Fun1(void);

int main()
{   int MAM=101;
#define MAA 100                 //如果在主函数中进行一个宏定义,看看在其他函数中是否能使用
    
    Fun1();
    return 0;
}
 int Fun1(void)
 {
   printf("在Fun1中打印MAA的值%d\n",MAA);
 }

可以看出,在主函数中的宏定义,在函数Fun1中依然能够使用

8:替换文本“可以是任何常量表达式”

#define MAX (3*5+6/2+10%3)
#define MAC (MAX>5? 0:1)
#define MAG ((MAD&&MAF)||0)
#define MAH (sizeof(9))
#define  MAS  (MAX==102)

 以上可以看到,替换文本可以是任意常量表达式,可以是任意合法的运算符(单目+双目+逻辑+位运算符)这里需要注意的是

#define MAH (sizeof(9))也是合法的,宏预定义 MAH=4,

看最后一行代码:#define  MAS  (MAX==102)是合法的表达式,如果MAX=102.则MAS=1,不等于则MAS =0。但是切记#define  MAS  (MAV=102)则是非法表达式,因为MAX是常量标识符,不能被重新赋值的。

9:宏定义还有一种比较特殊的用法

在不包含形参的被调用函数

#include <stdio.h>
#include <stdlib.h>

#define MAK Fun2()

int Fun2(int);

int main()
{   int MAM=101;
                 //如果在主函数中进行一个宏定义,看看在其他函数中是否能使用
    MAK();

    return 0;
}

 int Fun2(void)
 {
   printf("表明Fun2被调用\n");
   return 1;
 }

,输出“fun2被调用”,需注意的是,如果被调用函数是有形参的,则不可用简单的宏替换,来实现函数调用。其实利用宏定义来调用无形参函数,本质上也是编译阶段的简单替换。

如果是带形参的函数则不能简单使用宏定义替换。

10:宏替换不能替换字符串

#define  MAV  "Clanguage"
int main()
{
char arry1[]="Clanguage"
printf("Clanguage\n");


}

我看有些资料上说,宏替换不能替代字符串,并给出以上的例子。说明字符数组并不等于MAV,打印也是“Clanguage”而不是MAV。其实这种额外的说明只是还没有明白,宏定义的本质。只有在代码段出现宏名,编译器用替代文本,来替换宏名。切记万万不是,出现替代文本时,用宏名去代替

 小结:我们已经学习了,最简单的宏定义。我刚刚开始接触到宏定义时,有些不理解

如:#define MAX 100

 然后代码段有如下判断语句:if(a<MAX){执行代码}; 直接使用if(a<100){执行代码}不也可以吗?这样不是更直接吗?代码更是一目了然。其实在代码量较小时,确是第二种写法更直观。

但是想象一下,如果代码量非常大,我们需要将if(a<100)修改成if(a<105),代码中有20出需要修改,这时预定义的优势是不是非常明显?

我们只需要将#define MAX 100修改成#define MAX 105即可。

另外还有一种情况:很多情况下预定义其实更直观。

有如下场景:我们做了一个期末考试评估系统一共四门科,Chinese(语文150)、match(数学120)、english(英语120),physical(物理100),括号里数字代表分值。

考生,语文>120,评价S。数学>100,评价S。英语>100,评价S,物理>90,评价S。

于是我们可以如下定义

#define S_CHINESE 120
#define S_MATCH 100
#define S_ENGLISH 100
#define S_PHYSICAL 90
int main()
{   int a[4]= {110,102,110,99}; //定义一个数组,里面依次是一个考生的语文/数学/英语/物理的考试成绩
    int i;
    for(i=0;i<4,i++)
       { switch(i)                 //第一种不使用宏替换
         case 0:if(a[i]>120) printf("a的语文成绩是S");break;
         case 1:if(a[i]>100) printf("a的数学成绩是S");break;
         case 2:if(a[i]>100) printf("a的英语成绩是S");break;
         case 2:if(a[i]>90) printf("a的物理成绩是S");break;

                                  //第二种使用宏替换
         case 0:if(a[i]> S_CHINESE) printf("a的语文成绩是S");break;
         case 1:if(a[i]>S_MATCH) printf("a的数学成绩是S");break;
         case 2:if(a[i]>S_ENGLISH) printf("a的英语成绩是S");break;
         case 2:if(a[i]>S_PHYSICAL) printf("a的物理成绩是S");break;
       }


}

第二种更直观,而且如果下一次考试,试卷难易发生变化,只需要修改宏定义即可

一:

#define MUL(a,b) a*b
int main()
{
  int x=2,y=3,z;
  z=MUL(x,y);
  printf("输出z的值%d\n",z);
}

带参数宏定义指令,我们先定义一个实现乘法功能的宏定义

 实际上编译后的源代码等价于

//#define MUL(a,b) a*b
int main()
{
  int x=2,y=3,z;
  z=2*3;
  printf("输出z的值%d\n",z);
}

聪明的你或许已经看出来,其实编译器已经帮我们算好了,z的值,并直接赋值给z。

下面是分解教学看看编译器是怎么操作的:以下时我自己的理解

1:先进行函数替换(暂时先这么理解)z=a*b,

2:再将x替代a,再将y替换b 得到 z= x*y

有些书籍或资料,将带参数宏替换与函数调用做对比,并说明二者的相同和不同

如带参数宏定义里的参数,不需要做类型声明,也不用给分配存储空间,以及函数占用的代码段空间,二宏定义也不占用代码段的空间

这确是带参数宏定义在占用空间上的优势,比如在嵌入式系统(51单片机,stm32单片机中存储是比较珍贵的)。

但是问题来了,安卓或windows,linux系统开发中其实存储空间已经很大了,为什么还是广泛使用带参数宏定义

#include <stdio.h>
#include <stdlib.h>
#define MUL(a,b) a*b
int main()
{
  int x=2,y=3,z,i,j=2;
  z=MUL(x,y);
  z=MUL(5,6);
  for(i=0;i<=5;i++)
  {  z=MUL(i,j);
      printf("输出z的值%d\n",z);
  }
  //printf("输出z的值%d\n",z);
  return 0;
}

                                        图一

  

#include <stdio.h>
#include <stdlib.h>
//#define MUL(a,b) a*b
int mul(intx,inty);
int main()
{
  int x=2,y=3,z,i,j=2;
  z=mul(x,y);
  z=mul(5,6);
  for(i=0;i<=5;i++)
  {  z=mul(i,j);
      printf("输出z的值%d\n",z);
  }
  //printf("输出z的值%d\n",z);
  return 0;
}
int mul(a,b)
{ 
  return a*b;
}

                                          图2:使用函数调用

图1,使用宏定义  当代码编译执行到z=MUL(x,y);这一步时,编译器直接将MUL(2,3)带入到宏定义#define MUL(a,b) a*b中,直接进行简单替换z=2*3。

图2,   使用函数调用,当代码运行z=mul(x,y);时开始调用函数int mul(a,b)  ,将实参 传递给形参,然后运算器,累加器,寄存器工作,计算出a*b,然后返回a*b的值。把乘积再赋给z。同时还在调用前,对主函数中需要保护的状态和参数,还要执行入栈(push)保护,当调用函数执行结束,返回主函数时还要执行出栈(POP)操作。

所以你看出来了吗?宏定义就是把大量的工作在编译阶段,就交给编译器干了。而函数调用就是把工作交给运行中的代码去干。

也就造成了,在大量使用带参数宏定义的代码中,编译时工作量大一点,(可以理解为带参数的宏定义就是对编译器写的调用函数,),对比函数调用代码运行速度更快。

   

三:#define MAX(a)  #a 与 #define MAX(a)  ##

1:#define MAX(a)  #a

#include <stdio.h>
#include <stdlib.h>
#define MAX(s) #s
int main()
{
    printf(MAX(Hello \n));
     printf(MAX(world!\n));
    return 0;
}

宏定义中的#s,是指传入的参数强行格式化为字符串

格式上和带参数宏定义格式一致,MAX和()之间必须没有空格。

#define MAX(a) #a

#define MAX(b) #b

#define MAX(c) #c

也都是可以且合法的,注意并不是只能写成#s,这一点需要注意

另外我们想到这样的宏定义,每次只能格式化一个参数(也就是字符串),试试能不能同时格式化两个字符串,能不能格式化字符串的同时,传入正常参数

#include <stdio.h>
#include <stdlib.h>
#define MAX(s,a) #s #a
int main()
{
    printf(MAX(Hello \n,xixi\n));
    printf(MAX(world!\n,hah\n));
    printf(MAX("1111\n","22222\n"));

    return 0;
}

输出结果

 

 结论,字符化可以同时传入多个参数

格式宏定义格式#define 宏名(参数1,参数2,....参数n) #参数1 #参数2 。。。。#参数n

这样理解#s,#a与一般宏定义后的“替换文本”和带参数宏定义的“合法表达式也不同”他是对宏定义的形参做出了说明,参数做了#s说明后,宏被调用时,实参就能用字符常量代替,且可以看到。如果传入的实参用“”包围。也是直接被当做字符串整体传递给宏定义的实参。

2:#define MAX(a)  字符串##a

字符串##a起到链接的作用,##(连接运算符):用于将两个参数连接成一个符号;需要特别注意的是,预定义阶段并不能直接拼接字符串。他并不能直接被printf()打印出来。

#define ARGUMENT(x, y) x##y
 
int argument(a, b) = 10;
 
// 上面这一行宏定义等价于以下代码
// int ab = 10;

注意:传入的实参,是不需要提前定义的,因为传入的实参,是需要成为一个变量的标识符ab;

#include <stdio.h>
#include <stdlib.h>

#define TIMEMANAGEMENT(X) function_##X
int function_1(void);
int main()
{   int i =0;
    i=TIMEMANAGEMENT(1)();
    printf("打印i的值是%d\n",i);
    return 0;
}

int function_1(void)
{
return 5;
}

利用预定义拼接函数名,然后直接调用。把传入的实参“1”与function拼接起来

注意格式 拼接的字符串和参数之间不能有空格,也不能有

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值