define 面试知识点都在这里了!
Posted Linux猿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了define 面试知识点都在这里了!相关的知识,希望对你有一定的参考价值。
作者:Linux猿
简介:CSDN博客专家,C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊!
关注专栏:C/C++面试通关集锦 (优质好文持续更新中……)
define 是预处理器的一个指令,定义在 C 语言中。在预处理过程中,将宏替换为对应宏值,可以理解为字符串的替换。
为什么说它是预处理器的一个指令呢?
我们来看一个简单的例子:
#include <stdio.h>
#define x 5
int main() {
int y = x + 3;
printf("y = %d\\n", y);
}
在上面的代码中,定义了一个宏 x ,其值为 5,我们只将代码预处理一下,执行如下命令:
gcc -E -o main.i main.c
预处理:完成头文件的插入、宏定义的展开以及条件编译的处理等。
执行命令后,在当前目录下生成了文件 main.i,部分内容如下所示:
......
......
# 4 "main.c"
int main() {
int y = 5 + 3;
printf("y = %d\\n", y);
}
......
......
可以看到,宏 x 在预处理后已被替换为 5了。
当然,宏定义也可以是更复杂的形式,比如:表达式、函数等,来看一个例子:
#include <stdio.h>
#define MAX(x, y) ((x) > (y)) ? (x) : (y)
int main() {
printf("max = %d\\n", MAX(2+3, 4+5));
}
输出结果为:
linuxy@linuxy:~/defineDir$ ./main
max = 9
linuxy@linuxy:~/defineDir$
来看一下预处理后的结果(main.i文件部分内容):
......
......
# 4 "main.c"
int main() {
printf("max = %d\\n", ((2+3) > (4+5)) ? (2+3) : (4+5));
}
......
......
可以看到直接将定义的宏进行了替换。
但是,这个替换可能会导致如下问题:
问题 1. define 并不做类型检查
define 只做值替换,而不像普通变量那样做类型的检查。
来看一个例子:
#include <stdio.h>
#define PLUSONE(x) ++x
int main() {
int valInt = 10;
double valDou = 20;
char *p = "LinuxY";
printf("valInt = %d\\n", PLUSONE(valInt));
printf("valDou = %lf\\n", PLUSONE(valDou));
printf("p = %s\\n", PLUSONE(p));
}
输出结果为:
linuxy@linuxy:~/defineDir$ gcc main.c -o main
linuxy@linuxy:~/defineDir$ ./main
valInt = 11
valDou = 21.000000
p = inuxY
linuxy@linuxy:~/defineDir$
可以看见,PLUSONE(x) 可以接受任何类型的参数 x,这就加大了程序出错的风险。
问题 2. define 的宏直接替换导致的问题
宏的替换可以说是字符串的直接替换,所以会导致下面这个问题。
#include <stdio.h>
#define MULTIPLY(x, y) x * y
int main() {
printf("result = %d\\n", MULTIPLY(2, 3 + 4));
}
输出结果为:
linuxy@linuxy:~/defineDir$ gcc main.c -o main
linuxy@linuxy:~/defineDir$ ./main
result = 10
linuxy@linuxy:~/defineDir$
是不是有点惊讶!按照正常的理解输出结果应该为14,但是这里结果却是10,其实程序是这样计算的:
宏替换后,2 * 3 + 4 = 10,即:直接将 x 替换为 2,y 替换为 3 + 4。而不是将 y 替换为 7。
为了证明,我们来看下预处理后的程序,执行命令:
linuxy@linuxy:~/defineDir$ gcc -E -o main.i main.c
预处理后的 main.i 文件有如下内容:
......
......
# 4 "main.c"
int main() {
printf("result = %d\\n", 2 * 3 + 4);
}
......
......
这下明白了吧!
那么,如何避免上面这种情况呢 ?
需要添加括号,规定好优先级,修改后如下所示:
#include <stdio.h>
#define MULTIPLY(x, y) (x) * (y)
int main() {
printf("result = %d\\n", MULTIPLY(2, 3 + 4));
}
输出结果为:
linuxy@linuxy:~/defineDir$ gcc main.c -o main
linuxy@linuxy:~/defineDir$ ./main
result = 14
linuxy@linuxy:~/defineDir$
接下来再说一下 define 的一个容易理解错误的点。
我们通常意义上的理解是宏定义了就不能更改,事实上真是这样吗?
我们来看一个简单例子:
#include <stdio.h>
#define x 5
int main() {
printf("x = %d\\n", x);
#undef x
#define x 10
printf("x = %d\\n", x);
}
输出结果为:
linuxy@linuxy:~/defineDir$ gcc main.c -o main
linuxy@linuxy:~/defineDir$ ./main
x = 5
x = 10
linuxy@linuxy:~/defineDir$
看到了吗?x 值变化了!我们来看一下预处理后的结果,执行命令:
linuxy@linuxy:~/defineDir$ gcc -E -o main.i main.c
文件 main.i 中可以看到源代码部分内容为:
......
......
# 4 "main.c"
int main() {
printf("x = %d\\n", 5);
printf("x = %d\\n", 10);
}
......
......
x 分别被替换为 5 和 10,是不是很意外!
接着上面这个问题,再说一下条件编译。
经常在条件编译中看到 #define 的身影,通常在一个头文件中会有如下定义:
#ifndef ADD_H
#define ADD_H
//头文件内的语句
#endif // ADD_H
上面语句的意思是:如果还没有定义 ADD_H,就执行 #ifndef ADD_H 后的内容。
这样可以防止一个头文件被多次包含,重复引用。
当然,还有其它的条件编译指令,如下所示:
(1)#if
检测表达式值是否为真。如果为真,则编译后面的代码直到出现 #else、#elif 或 #endif 为止,否则不编译。类似于 if 语句。
(2)#else
当#if #ifdef #ifndef 指令不满足的时候,就执行 #else 后面的代码。
(3)#elif
类似于 else if 语句,后面接一个表达式进行判断。
(4)#ifdef
判断宏是否定义,如果已定义,则执行后面的语句。
(5)#ifndef
判断宏是否未定义,如果未定义,则执行后面的语句。
(6)#endif
用于终止 #if #ifdef #ifndef 指令。
(7)#undef
用于取消宏的定义,上面已举例说明。
上面的指令大多和 if else 等语句有类似的功能,但是,上面的指令除 #undef 外,其它指令必须有结束标志 #endif。
当然,条件编译还有其它的用途,比如:跨平台、调试的情况。
最后,说一下 # 开头的预处理指令。
在文件中以 # 开头的预处理指令,都是在预处理环节处理的,上面已经看了宏的预处理,接下来再看一个简单例子:
#include <stdio.h>
int main() {
printf("Hello World!");
}
预处理一下上面的代码,执行命令:
linuxy@linuxy:~/defineDir$ gcc -E -o main.i main.c
查看 main.i 文件,部分内容如下所示:
extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
# 840 "/usr/include/stdio.h" 3 4
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 858 "/usr/include/stdio.h" 3 4
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
# 873 "/usr/include/stdio.h" 3 4
可以看到头文件 #include<stdio.h> 被展开了。
总结
好了,define 的知识点就讲解完了,希望对大家有帮助!
关注专栏:C/C++面试通关集锦 (优质好文持续更新中……)
以上是关于define 面试知识点都在这里了!的主要内容,如果未能解决你的问题,请参考以下文章