C语言学习笔记(浙大翁恺版)第十二周 文件(3)

目录

12.3.1 大程序

多个.c文件

编译单元

12.3.2 头文件

头文件 

#include

一些误区:

" " Or <> ?

12.3.3 声明

声明 vs 定义 

 声明与头文件

重复声明

标准头文件结构


12.3.1 大程序

多个.c文件

  • 当main()中的代码太长了,可以分成多个函数
  • 当一个源代码文件太长,合适的做法是分成多个文件
  • 两个独立的源代码文件不能编译形成可执行程序

那么如何才能将分开的多个.c文件中的函数合起来呢?

简单来说,你需要新建一个项目,在Devcpp这个软件里,在项目中新建.c文件,在文件里放上函数。(也可以直接将写好的文件放进去)之后编译就可以通过,软件会将其中的源代码文件编译后链接起来。DevCpp可以不建立项目而加入文件

对于其他的IDE,必须要先新建项目再加入。有分开的编译和构建两个选项。编译是对单个源代码文件进行编译,构建是对整个项目做链接。

main.c 

#include<stdio.h>

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文件是一个编译单元
  • 编译器每次编译只处理一个编译单元,再由链接器链接起来

12.3.2 头文件

在上一节提到的程序中,我们在main函数里保留了max的函数声明,这点会影响程序编译吗?

答案是不会,删掉之后编译仍然可以通过,这是为什么?

我们再将max函数里的数据类型改为double

double max(double a,double b)
{
    return a>b?a:b;
}

编译仍然可以通过,但是我们给的值可都是int类型啊?

实际上,两个C文件是分开来编译的,在编译main函数时,C编译器会猜测这里使用的是int类型,而在max.c里用了double类型编译,当main调用函数,编译器链接两个程序时,传入的是int,而函数需要的和返回的都是double,所以,Boom,出错了。

因此想要双方协调一致,需要有一个中间人来通知双方,告诉main函数你不要猜了

头文件 

把函数原型放到一个头文件(.h)里,在需要调用这个函数的源代码文件(.c)里用#include引入头文件,救恩那个让编译器知道函数原型。

  • 在使用和定义函数的地方都应该#include对应的头文件
  • 一般做法是对于任何.c,都有对应的同名.h,把所有对外公开的函数原型和全局变量的声明(因为全局变量可以在多个.c之间共享)都放进去
  • 不对外公开的函数,指那些在函数前面加上static关键词限制其只能在当前编译单元中被使用。对于全局变量也可以这样限制,让它只能在当前编译单元使用。

所以这是之前的项目写法:

main.c

#include<stdio.h>
#include"max.h"

int main(void)
{
    int a=5;
    int b=6;
    printf("%d\n",max(a,b));

    return 0;
}

max.h 

int max(int a,int b);

max.c 

#include"max.h"
int max(int a,int b)
{
    return a>b?a:b;
}

#include

  • 和宏一样,都属于编译预处理指令,在编译之前就处理完毕 
  • 它会将所在的文件中全部文本内容原封不动地插入所在的地方
  • 因此也不是必须要在.c文件的最开始写明#include

一些误区:

  • include不是用来引入一个库,只是做文本替换
  • stdio.h中,你只能找到printf的函数原型,其代码在另外的地方(.lib)
  • 现在的C编译器默认引入所有标准库
  • #include<stdio.h>只是为了让编译器知道printf函数的原型,确保你调用时给出正确类型的参数值

" " Or <> ?

  • " "表示编译器会首先在当前目录寻找文件,如果找不到,就会到编译器指定的目录去找
  • <>表示编译器只在指定的目录寻找(标准库头文件)
  • 类似能指定寻找头文件的还有计算机环境变量和编译器命令行参数

12.3.3 声明

想要在整个项目中都能访问到全局变量,需要在.h文件里写相关声明。

max.h

extern int gALL;
int max(int a,int b);

声明 vs 定义 

  • 声明是不产生代码的东西 

函数原型、变量声明、结构声明、宏声明、枚举声明、类型声明、inline函数

  • 定义是产生代码的东西

函数、全局变量

 声明与头文件

  • 只有声明可以放在头文件里,是规则,但不是法律
  • (可以放定义,但是会出问题)会造成一个项目中多个编译单元里有重名实体
  • * 有些编译器允许几个编译单元里存在同名函数,或者用weak修饰符强调这种存在

重复声明

  • 同一个编译单元里,同名的结构不能被重复声明(正常来说不会写两遍,)
  • 如果头文件里有结构的声明,这个头文件大概率会被在一个编译单元里被#include多次
  • 所以需要“标准头文件结构”

比如说,整个项目有两个.h文件,max.h、min.h,然后在max.h文件里声明了结构,然后在min.h里include了max.h,由于include做的是文本替换,相当于是写了两次声明结构的语句,所以会报错。

因此,重复引用经常会造成这种事情。如何解决呢?

标准头文件结构

#ifndef _MAX_H_//如果这个.c里没定义过
#define _MAX_H_//那就定义一个
...
...
...//各种有关MAX_H的语句
#endif//结束if

 如果已经存在过(别的头文件已经写了一遍这个宏),中间的语句都不会执行

使用条件编译和宏,就可以保证这个头文件在一个编译单元中只会被#include一次

#pargma once(vs)也可以起到相同的作用,但不是所有编译器都支持

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值