① #开头的是编译预处理指令
② 它们不是C语言的成分,但是C语言程序离不开它们
③ #define用来定义一个宏
#define PI 3.14159
/*这行define在定义一个符号,把这样一个定义叫做宏,
PI是这个宏的名字,3.14159是这个宏的值*/
printf("%f\n",PI*3);//3.14159*3
/*C语言在所有编译之前进行编译预处理,
把程序里的所有PI都替换成后面的数值3.14159*/
#define <名字> <值>
① 注意没有结尾的分号,因为不是C的语句;
② 名字必须是一个单词,值可以是各种东西;
③ 在C语言的编译器开始编译之前,编译预处理程序(compile-preprocess,cpp)会把程序中的名字换成值,把出现了定义的宏的名字的地方都替换成后面的那个值;
④ 完全的文本替换。
宏中的数据参数都是没有类型的;
宏结尾不要加分号。
#define FORMAT "%f\n"
#define PI 3.14159
/*这两行define在定义一个符号,把这样一个定义叫做宏,
format是这个宏的名字,%f\n是这个宏的值
PI是这个宏的名字,3.14159是这个宏的值*/
printf(FORMAT,PI*3);//3.14159*3
/*C语言在所有编译之前进行编译预处理,
把程序里的所有"%f\n"替换成FORMAT,
所有PI都替换成后面的数值3.14159*/
① 如果一个宏的值中有其他的宏的名字,也是会被替换的;
② 如果一个宏的值超过一行,最后一行之前的行末需要加 \ ;
③ 宏的值中出现的任何标点符号都会被当作宏的值的一部分;
④ 宏的值后面出现的注释不会被当作宏的值的一部分。
#define FORMAT "%f\n"
#define PI 3.14159
#define PI2 2*PI
printf(FORMAT,PI+PI2);//3.14159+2*3.14159
eg:#define _DEBUG
这类宏是用于条件编译的,后面有其他的编译预处理指令来检查这个宏是否已经被定义过了。
一般用来表达一些特殊的东西,让编译器插入一些特殊的值。
常用:
LINE:文件当前的行号
FILE:进行编译的源文件的名字
DATE:文件被编译的日期(格式:mmm dd yyyy)
TIME:文件被编译的时间(格式:hh: mm: ss)
……
【注意:前后分别是两个短杠】
#include
int main(){
printf("%s:%d\n",__FILE__,__LINE__);
printf("%s,%s\n",__DATE__,__TIME__);
return 0;
}
#include
#define cube(x) ((x)*(x)*(x))//带参数的宏
int main(){
printf("%d\n",cube(5));
return 0;
}
输出:125
一切都要括号:整个值要括号;参数出现的每个地方都要括号。
eg:#define RADTODEG(x) ((x)*57.29578)
eg:#define MIN(a,b) ((a)>(b)?(b):(a))
也可以组合(嵌套)使用其他宏
在大型程序的代码中使用非常普遍;
可以非常复杂,如“产生”函数,在#和##这两个运算符的帮助下;
部分宏会被inline函数替代,inline可以做参数检查。
main()里的代码太长了适合分成几个函数;
一个源代码文件太长了适合分成几个文件;
两个独立的源代码文件不能编译形成可执行的程序。
① 在Dev C++中新建一个项目,然后把几个源代码文件加入进去;
② 对于项目,Dev C++的编译会把一个项目中所有的源代码文件都编译后,链接起来;
③ 有的IDE有分开的编译和构建两个按钮,前者是对单个源代码文件编译,后者是对整个项目做链接。
例:这个项目有两个文件
main.c文件
#include
int max(int a,int b);//函数声明
int main(void){
int a=5;
int b=6;
printf("%d\n",max(a,b));
return 0;
}
max.c文件
int max(int a,int b)
{
return a>b?a:b;
}
一个.c文件是一个编译单元;
编译器每次编译只处理一个编译单元。
① 把函数原型放到一个头文件(以.h结尾)中,在需要调用这个函数的源代码文件(.c文件) 中#include这个头文件,就能让编译器在编译的时候知道函数的原型;
例:
max.h头文件
int max(int a,int b);
main.c文件
#include
#include"max.h"
int main(void){
int a=5;
int b=6;
printf("%d\n",max(a,b));
return 0;
}
max.c文件
#include"max.h"
int max(int a,int b)
{
return a>b?a:b;
}
② 在使用和定义这个函数的地方都应该#include这个头文件;一般的做法就是任何.c都有对应的同名的.h,把所有对外公开的函数的原型和全局变量的声明都放进去。
全局变量是可以在多个.c之间共享的。
不对外公开的函数
① 在函数前面加上static就使得它成为只能在所在的编译单元中被使用的函数;
② 在全局变量前面加上static就使得它成为只能在所在的编译单元中被使用的全局变量。
一般情况下,头文件只能声明变量或函数,不能定义变量或函数。因为头文件要被其他文件包含#include,如果把定义放在头文件的话,就有可能出现多次定义变量或函数造成错误。一个程序中对指定变量或函数的定义只有一次,声明可以又多次。
头文件可以定义变量或函数的情况:
① 只有一个.c或.cpp文件包含了这个头文件;
② 头文件中可以定义const或者static修饰的变量或函数;
③ 可以定义类。
① #include是一个编译预处理指令,和宏一样,在编译之前就处理了;
② 它把那个文件的全部文本内容原封不动地插入到它所在的地方(#include只做这一件事情,其它的什么都不做),所以也不是一 定要在.c文件的最前面#include。
#include有两种形式来指出要插入的文件:“”和<>
① “”要求编译器首先在当前目录(.c文件所在的目录)寻找这个文件,如果没有,到编译器指定的目录去找;
② <>让编译器只在指定的目录去找
编译器自己知道自己的标准库的头文件在哪里;
环境变量和编译器命令行参数也可以指定寻找头文件的目录。
① #include不是用来引入库的,它把那个文件的全部文本内容原封不动地插入到它所在的地方(#include只做这一件事情,其它的什么都不做)。
② stdio.h里只有printf的原型,printf的代码在另外的地方,某个.lib(Windows)或.a(Unix)中。
③ 现在的C语言编译器默认会引入所有的标准库#include
有时候没有引入头文件,但是编译运行都正确了,这是因为编译器会猜测,猜测对了就运行正确。
就像函数里面的参数类型,如果没有输入参数类型,编译器默认猜测int,如果匹配,则运行正确。
同一个编译单元里,同名的结构不能被重复声明;
如果你的头文件里有结构的声明,很难这个头文件不会在一个编译单元里被#include多次,所以需要“标准头文件结构”。
标准头文件结构:
#ifndef _LIST_HEAD__ //检查,如果没有定义则下一步定义,如果已经定义了则不再定义
#define _LIST_HEAD__ //如果没有定义,于是就定义这个头文件
……
……(定义的东西)
……
#endif //定义完成则执行#endif后面的语句
运用条件编译和宏,保证这个头文件在一个编译单元中只会被#include一次;
#pragma once也能起到相同的作用,但是不是所有的编译器都支持。
声明不用初始化,初始化是定义做的事情。
int i;//是变量的定义
extern int i;//加一个extern关键字,这是变量的声明
声明是不产生代码的东西
函数原型、变量声明、结构声明、宏声明、枚举声明、类型声明、inline函数。
定义是产生代码的东西
函数、全局变量。