|
|
|
|
移动端

1.7.2 #define

《程序员面试笔试真题与解析》本书针对当前各大 IT企业面试笔试中特性与侧重点,精心挑选了 3年以来近百家典型 IT企业的面试笔试真题,这些企业涉及业务包括系统软件、搜索引擎、电子商务、手机 APP、安全关键软件等,面试笔试真题非常具有代表性与参考性。本节为大家介绍#define。

作者:猿媛之家来源:机械工业出版社|2017-12-06 14:35

有奖调研 | 1TB硬盘等你拿 AI+区块链的发展趋势及应用调研


1.7.2 #define

【真题 75】有如下代码:

  1. #define add(a,b) a+b  
  2. int main()  
  3. {  
  4. printf("%d\n", 5 * add(3, 4));  
  5. return 0;  

程序的输出结果是( )。

A.23  B.35  C.16  D.19

答案:D。

宏是一种语法替换,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串),使用带参数的宏只是进行简单的字符替换。通常,使用带参数的宏有以下几个特点:

1)程序运行可能会稍微快些。一个函数调用在执行时通常会有些额外开销——存储上下文信息、复制参数的值等,而一个宏的调用则没有这些运行开销。

2)通用。与函数的参数不同,宏的参数没有类型。因此,只要预处理后的程序依然合法,宏就可以接受任何类型的参数。例如,可以使用 MAX宏从两个数中选出较大的一个,数的类型可以是: int、 long int、float、double等。

3)编译后的代码通常会变大。每一处宏调用都会导致插入宏的替换列表,由此导致程序源代码增加(因此,编译后的代码数量变大)。宏被使用得越频繁,这种效果就越明显。当宏调用嵌套时,这个问题会相互叠加从而使程序变得更加复杂。

本题中,当调用函数 add(3,4)时,会把其替换成 3+4,因此,5*add(3,4)=5*3+4=19。正因为如此,一般在宏定义的时候昀好通过增加括号的方式来避免产生不期望的结果。如果本题中把宏定义改为: #define add(a,b) (a+b),那么上述程序的运行结果为: 5*(3+4)=35。

引申:含参数的宏与函数有什么区别?含参数的宏有时完成的是函数实现的功能,但是并非所有的函数都可以被含参数的宏所替代,具体而言,含参数的宏与函数的优缺点如下:

1)函数调用时,首先求出实参表达式的值,然后带入形参。而使用带参的宏只是进行简单的字符替换。

2)函数调用是在程序运行时处理的,它需要分配临时的内存单元;而宏展开则是在编译时进行的,在展开时并不分配内存单元,也不进行值的传递处理,也没有“返回值”的概念。

3)对函数中的实参和形参都要定义类型,二者的类型要求一致,如果不一致,应进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时带入指定的字符即可。宏定义时,字符串可以是任何类型的数据。

4)调用函数只可得到一个返回值,而用宏可以设法得到几个结果。

5)使用宏次数多时,宏展开后源程序会变得很长,因为每展开一次都使程序内容增长,而函数调用不使源程序变长。

6)宏替换不占用运行时间,而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。

7)参数每次用于宏定义时,它们都将重新求值,由于多次求值,具有副作用的参数可能会产生不可预料的结果。而参数在函数被调用前只求值一次,在函数中多次使用参数并不会导致多种求值过程,参数的副作用并不会造成任何特殊的问题。

一般来说,用宏来代表简短的表达式比较合适。

【真题 76】有如下代码:

  1. #define INT_PTR int *  
  2. INT_PTR p1,p2;  
  3. int a,b; 

那么,下面代码中,在编译时可能产生 error或 warning的是()。

A.p1 =&a; B.p2 = &b; C.A和 B均可 D.A和 B均不可答案:B。由于宏定义进行的是文本的替换,本题的原意是希望定义两个指针变量 p1与 p2,但是由于采用的

是宏定义的方式,使得在替换后,在进行类型匹配的时候,原语句 INT_PTR p1,p2被替换为了 int *p1,p2,显然,只有 p1为 int*类型,而 p2则是 int类型。所以,当执行 p2 = &b语句时,会报 warning(警告)。所以,选项 B正确。

【真题 77】求数 x的平方,宏定义可以写为: #define SQR(x) (x)*(x)。()。答案:正确。

【真题 78】写一个“标准”宏,这个宏输入两个参数并返回较小的一个。答案:#define Min(X, Y) ((X)>(Y)?(Y):(X))。

【真题 79】对于一个频繁使用的短小函数,在 C语言中应使用什么实现?在 C++语言中应使用什么实现?答案:在 C语言中使用宏定义,在 C++语言中使用 inline。

【真题 80】 typedef和 define有什么区别?答:typedef与 define都是替一个对象取一个别名,以此来增强程序的可读性,但是它们在使用和作用上也存在着以下几个方面的不同。

(1)原理不同

#define是 C语言中定义的语法,它是预处理指令,在预处理时进行简单而机械地字符串替换工作,不作正确性检查,不管含义是否正确,照样带入,只有在编译已被展开的源程序时,才会发现可能的错误并报错。

例如#define PI 3.1415926 当程序中执行 area=PI*r*r语句时, PI会被替换为 3.1415926,于是该语句被替换为 area=3.1415926*r*r,如果把#define语句中的数字 9写成了 g,预处理也照样带入,而不去检查其是否合理、合法。

typedef是关键字,它在编译时处理,所以, typedef有类型检查的功能。它在自己的作用域内为一个已经存在的类型定义别名,但是不能在一个函数定义里面使用标识符 typedef。例如: typedef int INTEGER,这以后就可以用 INTEGER来代替 int作整型变量的类型说明了,例如:

  1. INTEGER a, b; 

用 typedef定义数组、指针、结构等类型将带来很大的方便,不仅使程序书写简单,而且使意义更为明确,因而增强了可读性。例如:

  1. typedef int a[10]; 

表示 a是整型数组类型,数组长度为 10。然后就可用 a说明变量,例如: a s1,s2;完全等效于 int s1[10],s2[10]。同理,typedef void (*p)(void)表示 p是一种指向 void型的指针类型。

(2)功能不同 typedef用来定义类型的别名,这些类型不只包含内部类型(例如 int、char等),还包括自定义类型

(例如 struct),可以起到使类型易于记忆的功能。

例如:typedef int (*PF) (const char *, const char *);

定义一个指向函数的指针的数据类型 PF,其中,函数返回值为 int,参数为 const char *。typedef还

有另外一个重要的用途,那就是定义机器无关的类型,例如,可以定义一个叫 REAL 的浮点类型,在目标机器上它可以获得昀高的精度: typedef long double REAL,在不支持 long double 的机器上,该 typedef看起来会是下面这样: typedef double REAL,在连 double 都不支持的机器上,该 typedef看起

面试笔试真题解析篇

来会是这样: typedef float REAL。 #define不只是可以为类型取别名,还可以定义常量、变量和编译开关等。

(3)作用域不同 #define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而 typedef有自己的作用域。

程序示例如下:

  1. void fun()  
  2. {  
  3. #define A int  
  4. }  
  5. void gun()  
  6. {  
  7. //在这里也可以使用A,因为宏替换没有作用域,但如果上面用的是typedef,那这里就不能用A,  
  8. 不过一般不在函数内使用typedef  

(4)对指针的操作不同

二者修饰指针类型时,作用不同。

  1. #define INTPTR1 int*  
  2. typedef int* INTPTR2;  
  3. INTPTR1 p1,p2;  
  4. INTPTR2 p3,p4; 

语句 INTPTR1 p1,p2;和语句 INTPTR2 p3,p4;的效果是截然不同的。语句 INTPTR1 p1,p2;进行字符串替换后变成 int* p1,p2;,要表达的意义是声明一个指针变量 p1和一个整型变量 p2,而对于语句 INTPTR2 p3,p4;,由于 INTPTR2是具有含义的,告诉我们是一个指向整型数据的指针,那么 p3和 p4都为指针变量,这句相当于 int* p1,*p2;,从这里可以看出,进行宏替换是不含任何意义的替换,仅仅为字符串替换,而用 typedef为一种数据类型起的别名是带有一定含义的。

以如下代码为例:

  1. #define INTPTR1 int*  
  2. typedef int* INTPTR2;  
  3. int a=1;  
  4. int b=2;  
  5. int c=3;  
  6. const INTPTR1 p1=&a;  
  7. const INTPTR2 P2=&b;  
  8. INTPTR2 const p3=&c; 

上述代码中, const INTPTR1 p1表示 p1是一个常量指针,即不可以通过 p1去修改 p1指向的内容,但是 p1可以指向其他内容;而对于 const INTPTR2 p2,由于 INTPTR2表示是一个指针类型,因此,使用 const去限定,表示封锁了这个指针类型,因此, p2是一个指针常量,不可使 p2再指向其他的内容,但可以通过 p2修改其当前指向的内容,语句 INTPTR2 const p3同样声明的是一个指针常量。

【真题 81】有如下代码:

  1. #define ADD(x,y) x+y  
  2. int m=3;  
  3. m+=m*ADD(m,m); 

则 m的值为()。

A.15 B.12 C.18 D.58答案:A。宏定义过程执行的是文本替换的工作,所以,对语句 m+=m*ADD(m,m)进行文本替换后,变为

m+=m*m+m(注意没有括号),即 m=m+(m*m+m),由于 m的初始值为 3,所以, m=3+(3*3+3)=15。所以,选项 A正确。

【真题 82】有如下代码:

  1. #define M(x,y,z) x*y+z  
  2. main()  
  3. {  
  4. int a=1b=2c=3;  
  5. printf("%d/n",M(a+b,b+c,c+a));  

以上程序的输出结果是()。

A.19 B.17 C.15 D.12答案:D。在 C/C++语言中,宏定义的规则是直接进行文本的替换。本题中,由于采用宏定义的方式定义了

M(x,y,z),而 M(x,y,z)展开后的表达式为 x*y+z,所以,M(a+b,b+c,c+a)经过宏替换之后的表达式是 a+b*b+c+c+a(需要注意的是,本题的宏定义中没有括号,如果宏定义中有括号,此时需要将括号带入),因为变量 a的值为 1,变量 b的值为 2,变量 c的值为 3,所以,将各变量带入到表达式以后,表达式 a+b*b+c+c+a=1+2*2+3+3+1=12。所以,选项 D正确。

【真题 83】下列对于宏的描述中,不正确的是()。

A.宏会带来性能的缺失 B.宏不进行类型检查

C.宏可以做到函数无法做到的功能 D.编译时宏的处理早于函数答案:A。宏的作用是在编译的时候将对应函数用宏命令替换,在编译阶段不进行类型安全性检查,而且,这

一过程对程序性能无影响。所以,选项 A错误。

喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


51CTO读书频道活动讨论群:365934973

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

SQL Server 2005数据库管理与应用高手修炼指南

全书分为基础篇、高级篇和应用篇3个部分,共18章,有重点、分层次地讲解SQL Server 2005的基础知识、高级使用技巧和项目应用方法。第1~10...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊