C语言_10:宏定义

目录

无参数宏定义

宏定义的几点说明

带参宏定义

对于带参的宏定义有以下问题需要说明

文件包含

条件编译

小结


无参数宏定义

1.其定义的一般形式为:#define 标识符  字符串

其中 的”#”表示这是一条预处理命令,凡是以#开头的均为预处理命令,Define为宏定义命令,“标识符” 为所定义的宏名,“字符串”可以是常数、表达式、格式串等。

例如: #define PI 3.1415

它的作用是指定标识符PI来代替数 3.1415

在编写源程序的时候,所有的 3.1415都可由PI 代替,而对源程序作编译时,将先由预处理程序进行宏代替,即用 3.1415表达式去置换所有的宏名PI,然后再进行编译。

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

#define PI 3.14159

int main()
{
    int r;
    double s;
    printf("Please input the radius:");
    scanf("%d",&r);
    s = PI * r * r;
    printf("Area of the toundness is:%g\n",s);
    return 0;
}


宏定义的几点说明

1.宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不做任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。

2.宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换

3.宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用 #undef 命令

4.宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。

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

#define PI 3.14159

int main()
{
    void fun();
    int r;
    double s;
    printf("Please input the radius:");
    scanf("%d",&r);
    s = PI * r * r;
    printf("Area of the toundness is:%g\n",s);
    fun();
    return 0;
}

void fun()
{
    printf("the PI is :%g\n",PI);
    printf("PI");
}

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

#define PI 3.14159

int main()
{
    void fun();
    int r;
    double s;
    printf("Please input the radius:");
    scanf("%d",&r);
    s = PI * r * r;
    printf("Area of the toundness is:%g\n",s);
    fun();
    return 0;
}
#undef PI

void fun()
{
    printf("the PI is :%g\n",PI);
    printf("PI");
}

5.宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换

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

#define PI 3.14159
#define S PI*r*r

int main()
{
    int r;
    double s;
    printf("Please input the radius:");
    scanf("%d",&r);
    s = S;
    printf("Area of the toundness is:%g\n",s);
    return 0;
}

 6.习惯上宏名用大写字母表示,以便于与变量区别,但也允许用小写字母

7.可用宏定义表示数据类型,使书写方便

例如:  #define INTEGER int

在#include  <windows.h>

8.宏定义表示数据类型和用typedef定义数据说明符的区别

宏定义只是简单的字符串替换,是在预处理完成的,而 typedef 是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名,被命名的标识符具有类型定义说明的功能

如:(正规语句)Typedef   WNDCLASSA WNDCLASS ;

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

#define P1N1 char*
typedef char* P1N2;

int main()
{
    P1N1 a,b;
    P1N2 x,y;
    printf("By #define: %d %d\n",sizeof(a),sizeof(b));
    printf("By typedef: %d %d\n",sizeof(x),sizeof(y));
    return 0;
}

简单的替换        char  *a,b;

typedef              char*  x,char* y;

#define 是简单的文本替换,不会处理逗号分隔的变量列表

typedef 是真正的类型定义,会正确应用于所有变量

8. 对 “输出格式” 作宏定义,可以减少书写麻烦。 

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

#define P printf
#define D "%d\n"
#define F "%f\n"

int main()
{
    int a = 3,c = 4;
    float b = 4.5,d = 5.5;
    P(D F,a,b);
    P(D F,c,d);
    return 0;
}

 

带参宏定义

C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。

对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。

带参宏定义的一般形式为: #define   宏名(形参表)  字符串

带参宏调用的一般形式为:  宏名(实参表);

例如:

#define  M(y)  y*y+3*y    /* 宏定义 */

k = M(5);                         /* 宏调用 */

在宏调用的时,用宏参5去代替形参y,经预处理宏展开后的语句为:

   k = 5* 5 + 3 *5 ;

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

#define MAX(a,b) (a>b)?a:b

int main()
{
    int a,b,c;
    printf("Please input two numbers:");
    scanf("%d %d",&a,&b);
    c = MAX(a,b);
    printf("max = %d\n",c);
    return 0;
}

对于带参的宏定义有以下问题需要说明

1.带参宏定义中,宏名和形参表之间不能有空格出现。

例如把: #define MAX(a,b)   (a>b)?a:b

写为:     #define MAX   (a,b)    (a,b)?a:b  错误

2.在带参宏中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。

这是与函数中的情况不同的,在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。

3.在宏定义中的形参是标识符,而宏调用中的实参可以是表达式

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

#define SAY(y) (y)^(y)//异或,相异为1,相同为0

int main()
{
    char say[] = {73,74,75,76,77};
    int i = 0;
    while(say[i])
    {
        say[i] = SAY(say[i]);
        i++;
    }
    printf("%s\n",say);
    return 0;
}

4.在宏定义中,字符串内的形参通常要用括号括起来以避免出错

例:宏定义中 (y)*(y)表达式的y 都用括号括起来

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

#define SQ(y) (y)*(y)

int main()
{
    int a,sq;
    printf("Please input a number:");
    scanf("%d",&a);
    sq = SQ(a+1);//(a+1)*(a+1)
    printf("sq = %d\n",sq);
    return 0;
}

如果去掉括号,把程序改为以下形式: 

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

#define SQ(y) y*y

int main()
{
    int a,sq;
    printf("Please input a number:");
    scanf("%d",&a);
    sq = SQ(a+1);
    printf("sq = %d\n",sq);
    return 0;
}

 整个宏都加括号

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

#define SQ(y) ((y)*(y))

int main()
{
    int a,sq;
    printf("Please input a number:");
    scanf("%d",&a);
    sq = 160/SQ(a+1);
    printf("sq = %d\n",sq);
    return 0;
}

 不加总体括号

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

#define SQ(y) (y)*(y)

int main()
{
    int a,sq;
    printf("Please input a number:");
    scanf("%d",&a);
    sq = 160/SQ(a+1);//160/(a+1)*(a+1)
    printf("sq = %d\n",sq);
    return 0;
}

5.带参的宏和带参函数很相似,但又本质上的不同,除上面已谈到的各点外,把同一表达式用函数处理与用宏处理,两者的结果有可能是不同的

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

#define SQ(y) ((y)*(y))

int main()
{
    int i = 1;
    while(i <= 5)
    {
        printf("%d\n",SQ(i++));
    }
    return 0;
}

一次循环会进行两次i++

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

int main()
{
    int SQ(int);
    int i = 1;
    while(i <= 5)
    {
        printf("%d\n",SQ(i++));
    }
    return 0;
}

int SQ(int y)
{
    return ((y)*(y));
}

6.宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。

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

#define STR(s1,s2,s3,sum) strcat(strcat(strcat(sum,s1),s2),s3)

int main()
{
    char str1[] = "i",str2[] = "love",str3[] = "moumou",str[40] = "....";
    STR(str1,str2,str3,str);
    printf("str1 = %s\nstr2 = %s\nstr3 = %s\nstr = %s\n",str1,str2,str3,str);
    return 0;
}

注:str中空间要足够大!

文件包含

文件包含中的文件名可以用双引号括起来,也可以用尖括号括起来。

例如以下写法都是允许的:

#include <stdio.h>

#include “stdio.h”

但是这两种形式是有区别的:使用尖括号表示在包含文件目录区中查找(包含目录是由用户在设置环境时设置的),而不是在源文件目录区查找。

使用双引号则表示首先在当前的源文件目录中查找,若未找到才到包含目录中区查找。用户编程时可根据自己文件所在的目录来选择某一种命令形式。

条件编译

预处理程序提供了条件编译的功能。可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。

条件编译有三种潜规则:

第一种形式:

它的功能是,如果标识符已被#define命令定义过则对程序段1进行编译;否则对程序段2进行编译。

#ifdef      标识符

     程序段1

#else

      程序段2

#endif

如果没有程序段2(它为空),本格式符中的#else可以没有,既可以写为:

#ifdef  标识符

     程序段

#endif

第二种形式: 

#ifndef   标识符

     程序段1

#else 

      程序段2

#endif

第三种形式: 

If  常量表达式

      程序段

#else

      程序段

#endif

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

#define PI 3.14159
#define ROUND 1

int main()
{
    int r;
    double s;
    printf("Please input a number:");
    scanf("%d",&r);

#if ROUND
    s = r * r * PI;
    printf("Area of round is :%6.5f\n",s);
#else
    s = r * r;
    printf("Area of square is :%6.5f\n",s);
#endif // ROUND
    return 0;
}

小结

1.预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。程序员在程序中用预处理命令来调用这些功能。

2.宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量,变量或表达式。在宏调用中将用该字符串代换宏名。

3.宏定义可以带有参数,宏调用时是以实参代换形参,而不是“值传递”。

4.为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两边也应加括号

5.文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。

6.条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。

7.使用预处理功能便于程序的修改,阅读、移植和调试,也便于实现模块化程序设计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值