多个.c文件 项目
- main()里的代码太长了适合分成几个函数
- 一个源代码文件太长了适合分成几个文件
- 两个独立的源代码文件不能编译形成可执行的程序
main.c
`#include<stdio.h>`
`int max(int a,int b);`
`int main()`
`{`
`int a=5;`
`int b=6;`
`printf("%d\n",max(a,b));`
`return 0;`
`}`
`int max(int a,int b)`
`{`
`return a>b?a:b;`
`}
max.c
int max(int a,int b)
{
return a>b?a:b;
}
项目
- Dev C++中新建一个项目,把几个源代码文件加进去
- 对于项目,Dev C++的编译会把一个项目中的所有的源代码文件都编译后,链接起来
- 有的IDE有分开的编译和构建两个按钮,前者是对单个源代码文件编译,后者是对整个项目做链接
编译单元
- 一个.c文件是一个编译单元
- 编译器每次编译只处理一个编译单元
头文件
-
把函数原型放到一个头文件(以.h结尾)中,在需要调用这个函数的源代码文件(.c文件)中#include这个头文件,就能让编译器在编译的时候知道函数的原型
-
在使用和定义这个函数的地方都应该#include这个头文件
-
一般的做法就是任何.c都有对应的同名的.h,把所有对外公开的函数的原型和全局变量的声明都放进去
-
只有声明可以被放在头文件
-
是规律不是法律
-
否则会造成一个项目中多个编译单元里有重名的实体
-
*某些编译器允许几个编译单元中存在同名的函数,或者用weak修饰符来强调这种存在
#include
- #include是一个编译预处理指令,和宏一样,在编译之前就处理了
- 它把那个文件的全部文本内容全部原封不动地插入到它所在的地方
- 所以也不一定要在.c文件的最前面#include
""还是<>
-
#include有两种形式来指出要插入的文件
-
""要求编译器首先在当前目录(.c文件所在的目录)寻找这个文件,如果没有,到编译器指定的目录去找
-
<>然编译器只在指定的目录去找
-
编译器自己知道自己的标准库的头文件在哪里
-
环境变量和编译器命令行参数也可以指定寻找头文件的目录
#include的误区
声明
-
main.c
中的int max(int a,int b);
什么注释掉,可以运行 -
max.c中的int
改成double,结果出现了一个奇怪的数字解决办法:找一个中间媒介——头文件
变量的声明
-
int i;
是变量的定义 -
extern int i;
是变量的声明 -
声明是不产生代码的东西
- 函数原型
- 变量声明
- 结构声明
- 宏声明
- 枚举声明
- inline函数
-
定义是产生代码的东西
- 函数
- 全局变量
重复声明
- 在同一个编译单元里,同名的结构不能被重复声明
- 如果你的头文件里有结构的声明,很难这个头文件不会再一份编译单元里被#include多次
- 所以需要"标准头文件结构" #ifndef_MAX_H_ #define_MAX_H_ #endif_MAX_H_(作用:运用条件编译宏,保证这个头文件在编译单元中只会被#include一次)(ps:#pragma once也能起到相同的作用,但是不是所有的编译器都支持)
格式化的输入和输出
-
printf
-
%[flags] [width] [.prec] [ hIL] type
-
scanf
-
%[flag] type
printf和scanf的返回值
- 读入的项目数
- 读出的字符数
- 在要求严格的程序中,应该判断每次调用的scanf或printf的返回值,从而了解程序运行中是否存在问题
文件输入输出
- 用>和<做定向
FILE
-
FILE *fopen(const char * restrict path, conse char *restrict mode);
-
int fclose(FILE *stream);
-
fscnaf(FILE*,...)
-
fprintf(FILE*,...)
打开文件的标准代码
FILE* fp = fopen("file","r");
if(fp){
fscanf(fp,...);
fclose(fp);
}else{
...
}
fopen
二进制文件
-
其实所有的文件始终都是二进制的
-
文本文件无非是用最简单的方式可以以读写的文件
-
more、tail
-
cat
-
vi
-
而二进制文件时需要专门的程序来读写的文件
-
文本文件的输入输出时格式化,可能经过转码
文本VS二进制
- Unix喜欢用文本文件来做数据储存和程序配置
- 交互式终端的出现使得人们喜欢用文本和计算机"talk"
- Unix的shell提供了一些读写文本的程序
- Windows喜欢用二进制文件
- DOS时草根文化,并不继承和熟悉Unix文化
- PC刚开始的时候能力有限,DOS的能力更有限,二进制更接近底层
- 文本的有事时方柏霓人类读写,而且跨平台
- 文本的缺点时程序输入输出要经过格式化,开销大
- 二进制的缺点时人类读写困难,而且不跨平台
- int的大小不一致,大小端的问题…
- 二进制的优点时程序读写快
程序为什么要文件
- 配置
- Unix用文本,Windows用注册表
- 数据
- 稍微有点量的数据都放数据结构了
- 媒体
- 这个只能是二进制的
- 现实是,程序通过第三方库来读写文件,很少直接读写二进制文件了
二进制的读写
-
size_t fread(void *restrict ptr, size_t size, size_t nitems,FILE *restrict stream);
-
注意FILE指针是最后一个参数
-
返回时成功读写的字节数
为什么nitem?
- 因为二进制文件的读写一般都是通过对一个结构变量的操作来进行的
- 于是nitem就是用来说明这次读写几个结构变量!
在文件中定位
long ftell(FILE 8stream)
int fseek(FILE 8stream,long offset ,int whence);
SEES_SET
:从头开始SEEK_CUR
:从当前位置开始SEEK_END
:从尾开始(倒过来)
可移植性
- 这样的二进制文件不具有可移植性
- 在int为32位的机器上写成的数据文件无法直接在int为64位的机器上正确读出
- 解决方案之一是放弃使用int,而是typedef具有明确大小的类型
- 更好的方案是用文本