1.引言
不久前在学习有关于#define预处理指令,宏定义的使用,发现了一些平时不注意或是平时想不通的细节。下面就来说下我的体会吧。
2.编译相关实现
关于宏常量,一开始学习C的语言的人应该都有一定的了解,它的作用就是简单的代码替换。在编译器执行编译前会先执行以下几个操作:
- 若是设置了ANSI C的编译器,会将一部分的扩展字符替换(想了解的可以自行查找有关三字符序列和双字符的资料)
- 替换所有注释为空格
- 替换宏常量为其代替值
所以运用宏可以实现一些比较方便的功能,对于一些不需要长期修改的数据,或是简单的函数功能可以使用#define预处理的指令。
3.简单的#define预处理指令
a.没有参数的宏
此类的宏定义,主要用于文本替换或是数据替换,在C中此类的宏最为常见,也最为常用。在ACM、蓝桥杯、CCSP等算法竞赛中,经常会把数组的最大下标通过宏定义的方法给出,以方便调试
如下的代码实例:
#include <stdio.h>
#define LEN 500
#define WORDS "I like C"
int main(void)
{
char str1[LEN] = WORDS;
puts(str1);
return 0;
}
这段示例定义了字符数组str1,并把字符串WORDS赋值给这个数组,这是C中数组和常量配合使用的最常见例子。
b.带参数的宏
你可以定义具有形式参数(简称“形参”)的宏。当预处理器展开这类宏时,它先使用调用宏时指定的实际参数(简称“实参”)取代替换文本中对应的形参。带有形参的宏通常也称为类函数宏(function-like macro)。以下是带参数宏的基本定义方法:
#define 宏名称( [形参列表] ) 替换文本
#define 宏名称( [形参列表 ,] ... ) 替换文本
另外,stdio.h中,编译器常常使用宏函数来定义getchar()和putchar(),目的是使用预处理指令,提供编译和运算的速度。如下两个函数在头文件(stdio.h)中的定义:
#define getchar() getc(stdin)
#define putchar(x) putc(x, stdout)
这里需要注意的是,在定义一个宏时,必须确保宏名称与左括号之间没有空白符。如果在名称后面有任何空白,那么命令就会把宏作为没有参数的宏,且从左括号开始采用替换文本。如下代码片段(有缺陷)
#include <stdio.h>
#define SQUARE(x) x*x
#define PR(x) printf("The result is %d.\n", x)
int main(void)
{
int x;
x = SQUARE(5);
PR(x); //输出:The result is 25.
x = SQUARE(5 + 3);
PR(x); //输出:The result is 23.
return 0;
}
从上诉代码的运行结果来看,函数SQUARE(x)的作用是求x2,但如果按照函数的思想,第二个运算应该求8*8的结果,而宏函数却输出了23。其原因在于宏这是简单的进行了文本的替换,没有实际的过程和返回值,如上的代码展开为:x = 5 + 3 * 5 + 3;所有按照乘法和加发的优先级来计算,此时x=5+15+3=23。
因此上实例的宏定义可以改成:
#define SQUARE(x) ((x)*(x))
这样展开就变成了x=((5+3)*(5+3));可以成功的计算出所需要的结果。
4.C语言函数库内置的宏
在C语言的很多头文件中,都含有丰富的宏定义,使用这些宏可以大大的简化程序,加快速度。我整理了一些常用的头文件中的部分宏定义,希望和我一样的小白同学可以多了解下~
- stdio.h
#define EOF (-1) //这个宏听说在处理文件的时候很常用的哦~我们可以试着使用下
#define getchar() getc(stdin)
#define putchar(x) putc(x, stdout)
- math.h
这个头文件大多是关于数学的函数,宏常量这些,有些挺好用的,基本都是干货。(PS:可能很多想我这样的大一同学,并不知道这个库中关于PI,LN这些的宏定义,那我就来普及下~~)
#define M_E 2.71828182845904523536 //指数e
#define M_LOG2E 1.44269504088896340736 //log2E
#define M_LOG10E 0.434294481903251827651 //log10E
#define M_LN2 0.693147180559945309417 //ln2
#define M_LN10 2.30258509299404568402 //ln10
#define M_PI 3.14159265358979323846 //PI
#define M_PI_2 1.57079632679489661923 //PI/2
#define M_PI_4 0.785398163397448309616 //4/PI
#define M_1_PI 0.318309886183790671538 //2/PI
#define M_SQRT2 1.41421356237309504880 //根号2
- limtis.h
这个头文件中多是有关各种类型的大最小值,在一些不确定的极值题中可以使用这些宏方便快捷。
//由于说明中官方文档中已有了注释,那我就不重复劳动添加注释啦
#define SHRT_MIN (-32768) /* minimum (signed) short value */
#define SHRT_MAX 32767 /* maximum (signed) short value */
#define USHRT_MAX 0xffff /* maximum unsigned short value */
#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */
#define INT_MAX 2147483647 /* maximum (signed) int value */
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */
#define LONG_MAX 2147483647L /* maximum (signed) long value */
#define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */
#define LLONG_MAX 9223372036854775807i64 /* maximum signed long long int value */
#define LLONG_MIN (-9223372036854775807i64 - 1) /* minimum signed long long int value */
#define ULLONG_MAX 0xffffffffffffffffui64 /* maximum unsigned long long int value */
#define _I8_MIN (-127i8 - 1) /* minimum signed 8 bit value */
#define _I8_MAX 127i8 /* maximum signed 8 bit value */
#define _UI8_MAX 0xffui8 /* maximum unsigned 8 bit value */
#define _I16_MIN (-32767i16 - 1) /* minimum signed 16 bit value */
#define _I16_MAX 32767i16 /* maximum signed 16 bit value */
#define _UI16_MAX 0xffffui16 /* maximum unsigned 16 bit value */
#define _I32_MIN (-2147483647i32 - 1) /* minimum signed 32 bit value */
#define _I32_MAX 2147483647i32 /* maximum signed 32 bit value */
#define _UI32_MAX 0xffffffffui32 /* maximum unsigned 32 bit value */
/* minimum signed 64 bit value */
#define _I64_MIN (-9223372036854775807i64 - 1)
/* maximum signed 64 bit value */
#define _I64_MAX 9223372036854775807i64
/* maximum unsigned 64 bit value */
#define _UI64_MAX 0xffffffffffffffffui64
总结谢语
这是我大学第一次总结前面所学知识写的博客,里面包括了我自己认为需注意的点和一些宏的总览。若是在语句等方面有不足的请多多谅解,也恳请大家在评论中留言、讨论,谢谢!