预定义符号都是语言内置的。
下面是一些常用的:
#include
#include
int main()
{
printf("%s\n",__FILE__); //进行编译的源文件路径,数据类型为字符串
printf("%d\n", __LINE__); //文件当前的行号,数据类型为整型
printf("%s\n",__DATE__); //文件被编译的日期,数据类型为字符串
printf("%s\n",__TIME__); //文件被编译的时间,数据类型为字符串
printf("%s\n",__FUNCTION__); //代码所在的函数
}
语法规则:#define 标识符名 内容(标识符名通常为大写)
内容可以是值、关键字、甚至是一段代码:
#define MAX 1000
#define reg register //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
如果定义的内容过长,可以分成几行写,除了最后一行外,每行的后面都加一个’'(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" , \
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
#define 机制包括了一个规定,允许把参数替换到文本中(只是替换,不能传参),这种实现通常称为宏(macro)或定义宏(define macro)。
语法规则:#define 宏名(参数) 内容(宏名通常为大写)
#define SQUARE( x ) x * x
int a = SQUARE(5); //等价于int a = 5*5;
预定义宏只是文本的替换,不是像函数一样可以传参:
#define SQUARE( x ) x * x
int b = 5;
int a = SQUARE(b+1); //等价于int a = 5+1*5+1 = 11;(不是int a = 36;)
#define SQUARE( x ) (x) * (x)
int b = 5;
int a = SQUARE(b+1); //等价于int a = (5+1)*(5+1) =36;
#define DOUBLE(x) (x) + (x)
int a = 5;
int b = 10 * DOUBLE(a); //等价于int b = 10 * (a) + (a) = 55;
#define DOUBLE(x) ((x) + (x))
int a = 5;
int b = 10 * DOUBLE(a); //等价于int b = 10 * ((a) + (a)) = 100;
用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
宏通常被应用于执行简单的运算。比如在两个数中找出较大的一个。
简单运算中宏相对函数的优点:
简单运算中宏相对函数的缺点:
一般来讲函数和宏的使用语法很相似。所以语言本身没法帮我们区分二者。
因此平时习惯将宏名全大写,函数名不是全大写,单词首字母大写。
注意:
#和##的作用就是将参数插入到字符串中。
字符串是有自动连接特点:
//三个都输出hello world
printf("hello world\n");
printf("hello " "world\n");
printf("hel" "lo " "world\n");
那么可以实现如下代码:
#define PRINT(FORMAT, VALUE)\
printf("the value is "FORMAT"\n", VALUE)
int main()
{
PRINT("%d", 10); //printf("the value is ""%d"\n", 10)
//the value is 10
}
还可以用#实现:
#define PRINT(VALUE)\
printf("the value is "#VALUE"\n")
int main()
{
PRINT(10); //printf("the value is "10"\n);
//the value is 10
}
这里’#'的作用就是将10变成一个字符串。
也可以实现如下代码:
#define ADD_TO_SUM(num, value) \
sum##num += value
int main()
{
int sum5=0;
ADD_TO_SUM(5, 10); //sum5 += 10;
printf("%d", sum5);
}
这里##可以把位于它两边的符号合成一个存在的标识符。
该指令用于移除一个#define定义的标识符或宏。
如果现存的一个#define定义的标识符或宏不再需要或者需要被重新定义,那么可以用该指令移除或移除后重定义。
这里在移除宏后仍使用,因此报错。
程序中的一些代码删除可惜,保留又碍事,所以我们可以使用条件编译预处理指令选择性的编译。
条件编译预处理指令类似C程序中的条件分支语句if-else
语法规则:#if 常量表达式 ··· #endif
如果常量表达式为1,就编译#if-#endif间的代码段,否则相当于没有#if-#endif间这段代码。
#include
#include
#define __DEBUG__ 1
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#if __DEBUG__
printf("%d\n", arr[i]);//为了观察数组是否赋值成功。
#endif //__DEBUG__
}
return 0;
}
这里__DEBUG__为1,那么就编译#if-#endif间的代码段。
这个预编译指令与C程序中的if-else相似,也可以多分支和嵌套使用。
语法规则:#if defined(标识符) ··· #endif
如果标识符被定义过,就编译#if defined(标识符)-#endif间的代码段,否则相当于没有#if defined(标识符)-#endif间这段代码。
与此相对应的是#if !defined(标识符) ··· #endif,与#if defined(标识符) ··· #endif,其功能正好相反。
#include
#include
#define __DEBUG__
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#if defined(__DEBUG__)
printf("%d\n", arr[i]);//为了观察数组是否赋值成功。
#endif //__DEBUG__
}
return 0;
}
#include
#include
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#if !defined __DEBUG__
printf("%d\n", arr[i]);//为了观察数组是否赋值成功。
#endif //__DEBUG__
}
return 0;
}
这里__DEBUG__被定义过,那么就编译代码段。
这里也可以使用#ifdef实现相同的功能,当然也有与此相对应的ifndef,其功能与#ifdef是相反的。
语法规则:#ifdef 标识符 ··· #endif
#include
#include
#define __DEBUG__
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#ifdef __DEBUG__
printf("%d\n", arr[i]);//为了观察数组是否赋值成功。
#endif //__DEBUG__
}
return 0;
}
#include
#include
int main()
{
int i = 0;
int arr[10] = { 0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#ifndef __DEBUG__
printf("%d\n", arr[i]);//为了观察数组是否赋值成功。
#endif //__DEBUG__
}
return 0;
}
同样的,这个预编译指令与C程序中的if-else相似,也可以多分支和嵌套使用。
当然用本地文件包含也可以查找库文件中的文件,只是本地文件包含会优先查找源文件所在目录,然后再查找库文件中的文件。这样效率会低一些
在程序编译时,会先预编译,预编译会将#define宏定义的内容替换和包含的头文件替换,当一个文件中包含多个相同的头文件,那么头文件就会被替换多次,相当于多份头文件中的内容出现在文件中。这样会影响编译的效率。
因此使用条件编译的方式解决,即在每个头文件首部写这样代码段:
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
或者如下代码段:
#pragma once
这样就可以避免头文件的重复引入。