- 相关推荐
C语言#define的用法
引导语:预处理器是在真正的编译开始之前由编译器调用的独立程序。以下是百分网小编分享给大家的C语言#define的用法,欢迎阅读!
#define的用法
#define 是一个预处理指令,这个预处理执行可以定义宏。与所有预处理指令一样,预处理指令#define用#符号作为行的开头。预处理指令从#开始,到其后第一个换行符为止。也就是说,指令的长度限于一行代码。如果想把指令扩展到几个物理行,可使用反斜线后紧跟换行符的方法实现,该出的换行符代表按下回车键在源代码文件中新起一行所产生的字符,而不是符号 \n 代表的字符。在预处理开始钱,系统会删除反斜线和换行符的组合,从而达到把指令扩展到几个物理行的效果。可以使用标准C注释方法在#define行中进行注释。
//使用反斜线+回车
#define OW "hello\
world!" /*注意第二行要左对齐*/
每一个#define行由三部分组成:
第一部分,指令#deine自身。
第二部分,所选择的缩略语,这些缩略语称为宏(分为对象宏和函数宏)。宏的名字中不允许有空格,而且必须遵循C变量命名规则:只能使用字母、数字和下划线(_),第一个字符不能为数字。习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。
第三部分,(#define行的其余部分)称为替换列表或主体。
注意,结尾没有分号
下面来看一个例子:
#include
#define OW 2 * 2
#define OW 2 * 2
//#undef OW 需要先取消宏定义
#define OW 2*2
int main (void)
{
printf ("%d\n", OW);
return 0;
}
输出结果:
define.c:5:0: 警告: “OW”重定义 [默认启用]
define.c:4:0: 附注: 这是先前定义的位置
相同定义意味着主体具有相同顺序的语言符号。因此,下面两个定义相同:
#define OW 2 * 2
#define OW 2 * 2
两者都有三个相同的语言符号,而且额外的空格不是主体的一部分。下面的定义则被认为是不同的:
#define OW 2*2
上式只有一个(而非三个)语言符号,因此与前面两个定义不同。可以使用#undef指令重新定义宏。
宏所代表的数字可以在编译命令中指定(使用-D选项)
/*
宏演示
*/
#include
int main()
{
int num=0;
int arr[SIZE]={}; //使用gcc -D可以宏定义这个数字
for(num = 0;num <= SIZE - 1;num++)
{
arr[num]=num;
printf("%d ",arr[num]);
}
printf("\n");
return 0;
}
gcc -DSIZE=4 define.c
输出结果:
0 1 2 3
函数宏:
通过使用参数,可以创建外形和作用都与函数相似的类函数宏。宏的参数也用圆括号括起来。类函数宏的定义中,用圆括号括起来一个或多个参数,随后这些参数出现在替换部分。
#include
#define SQUARE(X) X*X
#define PR(X) printf ("The result is %d\n", X)
int main (void)
{
int x = 4;
int z;
printf ("x = %d\n", x);
z = SQUARE(x);
printf ("Evaluating SQUARE(x): ");
PR(z);
z = SQUARE(2);
printf ("Evaluating SQUARE(2): ");
PR(z);
printf ("Evaluating 100/SQUARE(2): ");
PR(100/SQUARE(2));
z = SQUARE(x+2);
printf ("Evaluating SQUARE(x+2): ");
PR(z);
printf ("x is %d\n", x);
z = SQUARE(++x);
printf ("Eavluating SQUARE(++x): ");
PR(SQUARE (++x));
printf ("After incrementing, x is %x\n", x);
return 0;
}
输出结果:
x = 4
Evaluating SQUARE(x): The result is 16
Evaluating SQUARE(2): The result is 4
Evaluating 100/SQUARE(2): The result is 100
Evaluating SQUARE(x+2): The result is 14
x is 4
Eavluating SQUARE(++x): The result is 36
After incrementing, x is 6
SQUARE(x+2) 输出结果是14,而不是想要的6*6 = 36。这是因为预处理器不进行计算,而只进行字符串替换。在出现x的地方,预处理都用字符串 x+2进行替换。x*x 变为 x+2*x+2 根据运算符优先级,则结果为 14
100/SQUARE(2)输出结果是 100,而不是想要的 25。因为,根据优先级规则,表达式是从左到右求值的。
100/2*2 = 100
要处理前面两个示例中的情况,需要如下定义:
#define SQUARE(x) ((x) * (x))
从中得到的经验是使用必须的足够多的圆括号来保证以正确的顺序进行运行和结合。
SQUARE(++x) 根据编译器的不同会出现两种不同的结果。解决这个问题的最简单的方法是避免在宏的参数中使用++x。一般来说,在宏中不要使用增量或减量运算符。
参看:C 语言再学习 -- 运算符与表达式
利用宏参数创建字符串:#运算符
在类函数宏的替换部分中,#符号用作一个预处理运算符,它可以把语言符号转化为字符串。
例如:如果x是一个宏参量,那么#x可以把参数名转化为相应的字符串。该过程称为字符串化。
#include
#define PSQR(x) printf ("The square of " #x" is %d\n", ((x)*(x)))
int main (void)
{
int y = 2;
PSQR (y);
PSQR (2 + 4);
return 0;
}
输出结果:
The square of y is 4
The square of 2 + 4 is 36
#include
#include
#define VEG(n) #n
int main()
{
char str[20];
strcpy(str,VEG(num));//num
printf("%s\n",str);//拷贝
return 0;
}
输出结果:
num
预处理器的粘合剂:##运算符
和#运算符一样,##运算符可以用于类函数宏的替换部分。另外,##还可用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。
#include
#define XNAME(n) x##n
#define PRINT_XN(n) printf ("x"#n" = %d\n", x##n)
int main (void)
{
int XNAME (1) = 14; //变为 int x1 = 14;
int XNAME (2) = 20; //变为 int x2 = 20;
PRINT_XN (1); //变为 printf ("x1 = %d\n", x1);
PRINT_XN (2); //变为 printf ("x2 = %d\n", x2);
return 0;
}
输出结果:
x1 = 14
x2 = 20
宏用于简单函数:
#include
#define MAX(x,y) ((x)>(y) ? (x) : (y)) /*比较大小*/
#define ABS(x) ((x) < 0 ? -(x) : (x)) /*绝对值*/
#define ISSIGN(x) ((x) == '+' || (x) == '-' ? 1 : 0) /*正负号*/
int main()
{
printf ("较大的为: %d\n", MAX(5,3));
printf ("绝对值为: %d\n", ABS (-2));
printf ("正负号为: %d\n", ISSIGN ('+'));
return 0;
}
输出结果:
较大的为: 5
绝对值为: 2
正负号为: 1
下面是需要注意的几点:
1、宏的名字中不能有空格,但是在替代字符串中可以使用空格。ANSI C 允许在参数列表中使用空格。
2、用圆括号括住每个参数,并括住宏的整体定义。
3、用大写字母表示宏函数名,便于与变量区分。
4、有些编译器限制宏只能定义一行。即使你的编译器没有这个限制,也应遵守这个限制。
5、宏的一个优点是它不检查其中的变量类型,这是因为宏处理字符型字符串,而不是实际值。
面试:用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SEC (60*60*24*365)UL
考察内容:
1、懂得预处理器将为你计算常量表达式的值,因此,可直接写出你是如何计算一年中有多少秒而不是计算出实际的值,这样更清晰而没有代价。
2、意识到这个表达式将使一个16 位机的整形数溢出,因此要用到长整形符号 L ,告诉编译器这个常数是长整形数。
3、如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。
面试:写一个“标准”宏MIN ,这个宏输入两个参数并返回较小的一个
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
考察内容:
1、三目表达式的使用
2、使用必须的足够多的圆括号来保证以正确的顺序进行运行和结合
3、进一步讨论,在宏中不要使用增量或减量运算符
参看:宏名必须用大写字母吗?
研究:C语言中用宏定义(define)表示数据类型和用typedef定义数据类型有什么区别?
宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。
请看下面的例子:
#define P1 int *
typedef (int *) P2
从形式上看这两者相似,但在实际使用中却不相同。
下面用P1、P2说明变量时就可以看出它们的区别:
P1 a, b; 在宏代换后变成: int *a, b; 表示 a 是指向整型的指针变量,而 b 是整型变量。
P2 a, b; 表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。
由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是作字符代换。在使用时要分外小心,以避出错。
总结,typedef和#define的不同之处:
1、与#define不同,typedef 给出的符号名称仅限于对类型,而不是对值。
2、typedef 的解释由编译器,而不是是处理器执行。
3、虽然它的范围有限,但在其受限范围内,typedef 比 #define 更灵活。
用于定义字符串,尤其是路径
A),#define ENG_PATH_1 E:\English\listen_to_this\listen_to_this_3
B),#define ENG_PATH_2 “ E:\English\listen_to_this\listen_to_this_3”
A 为 定义路径, B 为定义字符串
C), #define ENG_PATH_3 E:\English\listen_to_this\listen\
_to_this_3
还没发现问题?这里用了 4 个反斜杠,到底哪个是接续符?回去看看接续符反斜杠。反斜杠作为接续符时,
在本行其后面不能再有任何字符,空格都不行。所以,只有最后一那给 ENG_PATH_1 加上双引号不就成了:“ENG_PATH_1”。但是请注意:有的系统里规定路径的要用双反斜杠“ \\” ,比如:
#define ENG_PATH_4 E:\\English\\listen_to_this\\listen_to_this_3
【C语言#define的用法】相关文章:
C语言for语句用法详解11-19
c语言问号冒号的用法01-08
c语言大括号的用法11-28
c语言中time函数的用法03-20
C语言预定义宏用法03-30
C语言中strpbr()函数的用法03-19
C语言assert的用法有哪些04-02
c语言位运算符的用法指导12-04
C语言学习中的指针用法教程04-01