c语言多个.c文件编译以及操作

目录

多个.c文件

一、why?

二、如何将多个.c组合成一个?

三、示例

头文件

一、引入

二、需要一个媒介——头文件,才能知道函数到底要什么,又返回什么

三、关于#include

四、include的两种形式<> 还是""

五、#include的误区

声明

一、问题,我们在max.c里面声明的一个全局变量gAll怎么让main.c也知道有这个全局变量呢?

二、关于重复声明

下一篇:c语言格式化的输入输出 


多个.c文件

一、why?

main()里的代码太长了适合分成几个函数

一个源码文件太长了适合分成几个文件

两个独立的源代码文件不能编译形成可执行的程序

 

二、如何将多个.c组合成一个?

需要使用编译器新建项目,然后把源代码放进去,编译器会把项目中所有的源代码编译后,链接起来

编译器有编译和构建,编译指对单个源码进行编译,后者是对整个项目做链接

 

编译单元:一个.c是一个编译单元,编译器每次编译只能处理一个编译单元

 

三、示例

两个文件

max.c的内容

int max(int a,int b)

{

 return a>b?a:b;

}

main.c的内容

#include <stdio.h>

int max(int a,int b);

int main(int argc,char const *argv[]){

    int a=5;

    int b=6;

    printf("%d\n",max(a,b));

}

结果

 

头文件

一、引入

在上一章中

我们在main.c中留下了max函数的声明

在调用的时候,编译器知道我们对max函数的调用是正确的

但我们把声明去掉后?

虽然有红色提示但是还是能编译

还有结果

这是因为c语言古老的传统,会猜测max的参数全是int

假如我们把max.c的int类型改为double还能成功吗?

double max(double a,double b)

{

 return a>b?a:b;

}

但是编译又过了,但是里面肯定有问题

结果:

在mian.c和max.c都编译完成后

main中调用max.c的时候会链接max.c,可是传进去的东西错了,传出来的也错了

 

二、需要一个媒介——头文件,才能知道函数到底要什么,又返回什么

把函数原型放到一个头文件以.h结尾,在需要调用这个函数的源码文件.c中#include这个头文件,就能让编译器在编译的时候知道函数的原型

 

会发现这里就报错了,因为需要的是double类型传入的是int

将max函数改为int类型后运行正常

当然将%d改为%f,max函数类型是double也没问题

 

三、关于#include

#include是一个编译处理指令,和宏一样,在编译之前就处理了

它把那个文件的全部文本内容原封不动地插入到它所在的地方

所以也不是一定要在.c文件的最前面#include

 

现在我们用gcc看编译过程

gcc main.c max.h

使用gcc编译链接

执行成功

现在只编译main.c   gcc --save-temps main.c -c

查看main.i tail -n 50 main.i

所以include就是把max.c整个放了进来,注意这里的#是注释

 

四、include的两种形式<> 还是""

""要求编译器首先在当前目录(.c文件所在的目录)寻找这个文件,如果没有,到编译器指定的目录去找

<>让编译器只在指定的目录去找

编译器自己知道自己的标准库的头文件在哪里

linux和unix的库在这里

windows的在各种编译器下面的include里面

环境变量和编译器命令行参数也可以指定寻找头文件的目录

小结:一般来说自己写的用"",系统的用<>

 

看看stdio.h里面有什么?开始是版权声明,后面就是各种东西的定义

 

五、#include的误区

#include不是用来引入库的,做的只有一件事把这个文件原封不动的放进来

stdio.h里面只有printf的原型,printf的代码在另外的地方,某个.lib(Windows下)或.a(Unix)中

现在c语言的编译器默认会引入所有的标准库,当然用不到的会拿掉

#include<stdio.h>只是为了让编译器知道printf函数的原型,保证你调用时给出的参数值是正确的类型

 

所以说头文件:

在使用和定义(检查你对外宣称的和实际的函数的定义是不是一不一致)这个函数的地方都应该#include这个头文件

一般的做法就是任何.c都有对应的同名.h,把所有对外公开的函数的原型和全局变量(全局变量可以在多个.c中共享)的声明都放进去

那么要是有个函数不希望别人用?

在函数前面叫上static就使得它成为只能在所在的编译单元中被使用的函数

在全局变量前面加上static就使得它成为只能在编译单元(这个.c)中被使用的全局变量

 

较为推荐的头文件使用方式:

这里的max.c是max函数的原型,max.h是声明

 

 

 

声明

一、问题,我们在max.c里面声明的一个全局变量gAll怎么让main.c也知道有这个全局变量呢?

在max.h中声明

int max(int a, int b);

extern int gAll;

这样做后,我们告诉编译器在整个项目的某个地方有个叫gAll的东西

就可以在main函数中这么做了

 printf("%d\n",max(a,gAll));

结果:

但是没有这条声明编译就会报错

 

int I;是变量的定义

extern int i;是变量的声明,当然不能赋值,定义是定义声明是声明,不能搞混

声明是不产生代码的东西(就是只是默默的记下来)

函数原型

变量声明

结构声明

宏声明

枚举声明

类型声明

inline函数

而定义是产生代码的东西(函数、全局变量)

 

一个规则:只有声明可以被放在头文件中

是规则不是法律

否则会造成一个项目中多个编译单元有重名的实体

某些编译器允许几个编译单元中存在同名的函数,或者用weak修饰符来强调这种存在

 

二、关于重复声明

同一个编译单元里,同名的结构不能被重复声明

如果你的头文件里有结构的声明,很难这个头文件不会在一个编译单元里被#include多次

所以需要“标准头文件结构”

例如:

struct Node

{

    int value;

    char* name;

};

struct Node

{

    int value;

    char* name;

};

int main(int argc,char const *argv[]){

在一个main中定义两个同名的Node结构就会导致编译报错

当然我们把这个结构放入max.h中只用include就能用这个结构了

这时我们有另一个.h文件

也include了max.h,这是很常见的事情,可能有某个结构什么的要用到

我们同时也在main.c里面include了main.h

就相当于将max.h插入到main.c里面两遍,就产生了前面的问题,重复定义了两次Node

当程序结构很复杂的时候很难避免这样的事情

 

所以我们这么做

在max.h里面加入橙色的部分

#ifndef _MAX_H_  //判断该文件内有没有定义过这个宏

#define _MAX_H_  //若没有则定义

int max(int a, int b);

extern int gAll;

struct Node

{

    int value;

    char* name;

};

#endif // !_MAX_H_   结束  如果已经定义过这个宏就不会放入绿色部分的代码

注意:这种宏的定义的名字最好要加_ _保证和要用到的宏不重名

用这个机制,保证只引入过一次max.h

现在编译就对了

#include <stdio.h>

#include"max.h"

#include"main.h"

int main(int argc,char const *argv[]){

    int a=5;

    int b=6;

    printf("%d\n",max(a,gAll));

    return 0;

}

 

#pragma once 也能起到同样的作用,但是不是所有编译器都支持,例如gcc

猜你喜欢

转载自blog.csdn.net/weixin_46919419/article/details/112550651