宏通常被用于执行简单的运算。
宏相比于函数的优势:
1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
2.更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。
当然和宏相比函数也有劣势的地方:
1.每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度
2.宏是没法调试的
3.宏由于类型无关,也就不够严谨。
4.宏可能会带来运算符优先级的问题,导致程容易出现错
宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
命名约定:
一般来讲函数和宏的使用语法很相似。所以语言本身没法帮我们区分二者。那我们平时的一个习惯是:把宏名全部大写函数名不要全部大写
#undef
这条指令可以移除一个宏定义
例如:
#undef NAME
命令行定义:
许多C的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。
例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大写,我们需要一个数组能够大写。
#include <stdio.h>
int main()
{
int array[size];
int i=0;
for(i=0;i<size;i++)
{
array[i]=i;
}
for(i=0;i<size;i++)
{
printf("%d ",array[i]);
}
printf("\n");
return 0;
}
条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
比如说:
调试件的代码,删除可惜保留又碍事,所以我们可以选择性的编译
举例:
#include <stdio.h>
#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]);//如果DEBUG被定义了则这条语句会使用,否则不会使用
#endif DEBUG
}
}
常见的条件编译指令:
文件包含
我们已经知道,#include指令可以使另外一个文件被编译。就像它实际出现于#include 指令的地方一样.
这种替换的方式很简单:预处理器先删除这条指令,并用包含文件的内容替换。这样一个源文件被包含10次,那就实际被编译10次。
头文件被包含的方式:
本地文件包含
#include “filename"
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误。
库文件包含
#include<filename.h>
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
这样是不是可以说,对于库文件也可以使用“”的形式包含? 答案是肯定的,可以。但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。
嵌套文件包含
如果出现这样的场景
comm.h和comm.c是公共模块。test1.h和test1.c使用了公共模块test2.h和test2.c使用了公共模块。test.h和test.c使用了test1模块和test2模块。这样最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复。
如何解决这个问题?答案:条件编译
每个头文件的开头写
#ifndef _TEST_H_
#define _TEST_H_
//头文件的内容
#endif //_TEST_H_
或者
#pragma once
就可以避免头文件的重复引入。
其他预处理指令
#error
#pragma
#line
…