C语言-第16课

第16课 - 宏定义与使用分析

 

  1. 宏定义常量

#define定义宏常量可以出现在大地吗的任何地方。

#define从本行开始,之后的代码都是用这个宏常量。

例如:

#define ERROR   -1

#define PI       3.1415926

#define  PATH_1    “D:DelphiCTopic3.ppt”

#define  PATH_2     D:DelphiCTopic3.ppt

#define  PATH_3     D:DelphiC

 

  1. 宏定义表达式

#define表达式给有函数调用的假象,却不是函数。

#define表达式比函数更强大。

#define表达式比函数更容易出错。

#define SUM(a , b) (a)+(b)

#define MIN(a , b) ((a)<(b) ? (a) : (b))

#define DIM(a) (sizeof(a)/sizeof(*a))

例如:

#include<stdio.h>

#define MIN(a , b) ((a)<(b) ? (a) : (b))

int main()

{

int i = 1;

int j = 5;

printf("%d ",MIN(i++,j));

printf("%d ",i);

}

运行结果:

2

3

分析:预编译过程中,变成printf("%d ",(i++)<(j) ? (i++) : j);一共++两次

这里我们看到的是宏的缺点,但是他却有函数没有的功能,例如#define DIM(a) (sizeof(a)/sizeof(*a))就是函数做不到的。我们看下面的例子。

#include <stdio.h>

#include <malloc.h>

 

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

#define FOREVER() while(1)

#define BEGIN {

#define END   }

#define FOREACH(i, m) for(i=0; i<m; i++)

 

int main()

{

    int array[] = {1, 2, 3, 4, 5};

    int x = 0;

    int*p = MALLOC(int, 5);

    FOREACH(x, 5)

    BEGIN

        p[x] = array[x];

    END  

    FOREACH(x, 5)

    BEGIN

        printf("%d ", p[x]);

    END   

    FOREVER();  

    free(p);  

    printf("Last printf... ");  

    return 0;

}

运行结果:1 2 3 4 5

 

  1. 宏表达式与函数的对比

(1) 宏表达式在预编译期被处理,编译器不知道宏表达式的存在。

(2) 宏表达式用“实参”完全代替形象,不进行任何的运算。

(3) 宏表达式没有任何的“调用”开销。

(4) 宏表达式中不能出现递归的定义。

#define FAC(n) ((n>0) ? (FAC(n-1)+1) : 0)这就是错误的。

 

  1. 宏定义的作用域限制

#include <stdio.h>

int f1(int a, int b)

{

    #define _MIN_(a,b) ((a)<(b) ? a : b)    

    return _MIN_(a, b);

}

int f2(int a, int b, int c)

{

    return _MIN_(_MIN_(a,b), c);

}

int main()

{

    printf("%d ", f1(2, 1));

    printf("%d ", f2(5, 3, 2));  

    return 0;

}

运行结果:1

          2

分析:宏定义可以出现在代码的任何地方,而且在任何其他地方都可以调用宏。这里介绍一个符号#undef。当我们使用的#define的内容,我们在后面加上这个符号,就会限制它的使用了。例如:

int f1(int a, int b)

{

    #define _MIN_(a,b) ((a)<(b) ? a : b)    

return _MIN_(a, b);

#undef

}

这样作用域的概念就出现了。函数里面没法定义另一个函数的,但是却可以定义另一个宏。

 

  1. 强大的内置宏

含义

示例

__FILE__

被编译的文件名

file.c

__LINE__

当前的行号

25

__DATE__

编译时的日期

Jan 31 2018

__TIME__

编译时的时间

17:01:01

__STDC__

编译器是否遵循标准C规范

1

定义日志,若用函数

#include<stdio.h>

#include<time.h>

void f()

{

time_t t;

struct tm* ti;

time(&t);

ti = localtime(&t);

printf(“%s”,asctime(ti));

printf("Enter f()... ");

printf("Exit f()... ");

}

void Log(char* s)

{

printf("%s:%d %s ", __FILE__, __LINE__, s);

}

int main()

{

Log("Enter main()...");

f();

Log("Exit main()...");

return 0;

}

会出现行号的错误,改为宏

#include<stdio.h>

#include<time.h>

 

#define LOG(s) do{  

time_t t;            

struct tm* ti;         

time(&t);            

ti = localtime(&t);     

printf("%s [%s:%d] %s ", asctime(ti),__FILE__, __LINE__, s);  

}while(0)

 

void f()

{

LOG("Enter f()...");

LOG("Exit f()...");

}

int main()

{

LOG("Enter main()...");

f();

LOG("Exit main()...");

return 0;

}

行号显示正确。注意该程序实在linux下运行的。

 

思考:

#define f (x) ((x)-1)注意在f后面有一个空格那么f是否带了一个参数呢?代表f(x)或者代表((x)-1),还是f代表(x) ((x)-1)呢?为什么?

这是一个错误的用法,#define f(x) ((x)-1)这个是没问题的,但你意外发现f 后面可以加上一个空格,因为宏定义#define的用法是 #define 标识符 字符串,就是用简单的标识符取代复杂的字符串,这个字符串可以是表达式,系统认为标识符是一个函数的借口,返回表达式的值,而返回类型则与主函数中定义的x有关,也可以是一句话(要用双引号括起来,否则系统就会认为是表达式),这样就要以字符串形式输出 关键字+空格+标识符+空格+字符串,这样了在实现取代作用,如果出现多个空格,就会认为是属于字符串中的空格,但如果没有双引号,就会认为是表达式,明显这种表达式是不能运算得,于是当使用f时,就会出现一句编译错误,提示无法评估f函数返回值的类型

 

原文地址:https://www.cnblogs.com/free-1122/p/9720710.html