首页 > 其他分享 >【C语言】模块划分、编译器工作原理

【C语言】模块划分、编译器工作原理

时间:2024-01-13 14:45:42浏览次数:39  
标签:文件 func1 main 函数 int C语言 编译器 模块 include

模块划分

在实际应用中,一个较大的 C 程序并不会把所有代码都放入 main 主函数中,而是进行模块化设计,每个程序模块作为一个源程序文件,再由若干源程序文件组成一个 C 程序。这样处理便于分别编写、分别编译、进而提高调试效率。

#include <stdio.h>

void func1(){ //函数声明并定义
    printf("hello\n");
}

int func2(int i); //函数声明

int main() {
    func1(); //函数调用
    int i = func2(3); //函数调用
    printf("%d\n",i);
    return 0;
}

int func2(int i) { //函数定义
    printf("%d\n",i);
    return i+2;
}

现在将上述代码拆分为两个源程序文件 func.c 和 main.c,同时添加一个头文件 func.h。
一般情况下在 .h 文件中进行变量、函数和宏的声明,在 .c 文件中进行变量和函数的具体实现。

func.h 文件如下:

#include <stdio.h>

int func2(int i); 
void func1();

func.c 文件如下:

#include "func.h"

void func1(){ 
    printf("hello\n");
}

int func2(int i) {
    printf("%d\n",i);
    return i+2;
}

main.c 文件如下:

#include "func.h"

int main() {
    func1(); //函数调用
    int i = func2(3); //函数调用
    printf("%d\n",i);
    return 0;
}
Tips:
#include <> 是从 C 语言标准库中查找头文件;
#include "" 是从源代码所在目录中查找头文件,一般自己编写的头文件用引号。

ps:为什么经常见 xx.c 中 include 对应的 xx.h?因为如果 .c 中的函数也需要调用同个 .c 中的其他函数,那么这个 .c 往往会 include 同名的 .h,这样就不需要为声明和调用顺序而发愁。这已经成为了一种代码规范。

编译器工作原理

  1. 预处理阶段:实际上是处理 define 和 include 等宏命令,进行宏替换。例如 #include "xx.h" 就是把这一行删掉,把 xx.h 中的内容原封不动地插入在当前行位置。
  2. 编译阶段:以 .c 文件为基本单位进行。这一阶段为所有 .c 文件中的变量、函数分配空间,并将各个全局变量、函数进行符号描述,编译、汇编成二进制码从而生成 .o 目标文件。(这一阶段并不关心变量的具体定义和函数的具体实现,只要存在变量和函数的相关声明即可通过编译)
  3. 链接阶段:以 .o 文件为基本单位进行,主要的工作是重定位各个目标文件的函数、变量,生成可执行文件。这个过程主要是找到变量和函数的具体定义和实现。

注意点

注意点一:C是否支持函数重载?

函数重载是函数的一种特殊情况:
C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

C语言是不支持函数重载的。因为编译的时候,两个重载函数,函数名相同,一个名字对应多个地址,在 func.o 中的符号表中表示歧义,链接时也存在冲突,所以不支持。

如果有需求,可以用static关键字修饰其中一个函数,把该函数限制在其所在文件里。

拓展:C++引入了函数名修饰规则,用修饰过的函数名去符号表中匹配或者查找,保证不冲突。

注意点二:舍弃 .h 文件?

如果舍弃 .h 文件,在 .c 文件中包含变量、函数的声明以及实现,这时候其他 .c 文件用 #include 去包含这个 .c 文件就会出现问题。
func.c 文件:

#include <stdio.h>

void func1();

void func1(){
    printf("hello\n");
}

main.c 文件:

#include "func.c"

int main() {
    func1();
    return 0;
}


链接过程会出现 func1 函数重复定义的错误,因为编译生成的 main.o 和 func.o 中都有 func1 函数的具体实现。
可以对 main.c 做如下修改:

void func1();

int main() {
    func1(); 
    return 0;
}

如此编译后生成的 main.o 文件中就不再包含 func1 函数的实现。但是如果 func.c 中的函数很多,以及调用这些函数的 .c 文件也很多,这样在每个 .c 文件中手动挨个添加这些函数的声明就十分繁琐。.h 文件就是为了声明函数和变量方便而诞生的,将所有的变量和函数声明都写在 .h 文件中,其他 .c 文件只需 #include 相应的 .h 文件即可。

注意点三:.h 文件中 #ifndef、#define和#endif

.h 文件中 #ifndef、#define和#endif的作用是防止头文件在一个 .c 文件中被重复包含。
a.h 文件:

struct Node{
    int val1;
    int val2;
};

b.h 文件:

#include "a.h"

c.h 文件:

#include "a.h"

main.c 文件:

#include "b.h"
#include "c.h"

int main() {
    return 0;
}

a.h 中的内容被两次复制到 main.c 中,产生 struct Node 重复定义错误。
修改 a.h 内容:

#ifndef _A_H
#define _A_H

struct Node{
    int val1;
    int val2;
};

#endif

那么 a.h 在 main.c 中只会被替换一次,避免了头文件的重复包含。

标签:文件,func1,main,函数,int,C语言,编译器,模块,include
From: https://www.cnblogs.com/hzyuan/p/17962331

相关文章

  • 【CMake】5. 单项目多模块添加第三方依赖示例工程
    CMake示例工程代码https://github.com/LABELNET/cmake-simple单项目单模块示例工程https://github.com/LABELNET/cmake-simple/tree/main/simple-mod-deps这里引入C++gRPC依赖,进行示例1.多模块工程+第三方依赖CMake多模块工程,这是一个示例工程simple-mod-deps,项目名称de......
  • 【CMake】3.单项目单模块添加第三方依赖包示例工程
    CMake示例工程代码https://github.com/LABELNET/cmake-simple单项目单模块-添加第三方依赖示例工程https://github.com/LABELNET/cmake-simple/tree/main/simple-deps1.单模块工程+第三方依赖CMake单模块工程,这是一个示例工程simple-deps,项目名称cmake,第三方依赖demo......
  • 【CMake】2. 单项目单模块示例工程
    CMake示例工程代码https://github.com/LABELNET/cmake-simple单项目单模块示例工程https://github.com/LABELNET/cmake-simple/tree/main/simple1.单模块工程CMake单模块工程,这是一个示例工程simple,项目名称cmake,第三方依赖demo,主模块main2.目录结构$.SIMPLE......
  • TVM编译器原理与实践
    目录前言TVM编译器的实现过程关于《TVM编译器原理与实践》编辑推荐内容简介作者简介图书目录书中前言/序言《TVM编译器原理与实践》全书速览结束语前言随着人工智能的发展,计算机视觉、自然语言处理和语音识别等领域的需求不断增加。为了更好地满足这些需求,许多深度学习框架被开发出......
  • VS Code的C语言配置以及使用的傻瓜式教程
    VSCode的C语言配置以及使用的傻瓜式教程写在前面的话作者在学习使用vscode写C代码的时候,根据网上很多参差不齐的教程踩了不少的坑,很多教程在配置完成后总会出现一些普遍性的痛点,所以笔者决定写一篇傻瓜式的教程,帮助大家快速配置vscode,并成功运行C语言代码.作者水平有限,......
  • 【C语言】函数的声明、定义、调用
    函数要先声明后调用!函数的声明和定义函数的声明:把函数的名字、函数类型及形参类型、个数和顺序通知编译系统,以便在调用该函数时编译系统能正确识别函数并检查调用是否合法。函数的定义:对函数功能的确立,包括指定函数名、函数值类型、形参及其类型、函数体等,它是一个完整的、独......
  • 初识C语言struct关键字
    本人初学C语言,最近学习到了struct,分享以下自己的一些心得。struct是结构体关键字,里面可以包含多个成员,在描述一个复杂结构体时可以借助struct。打印时,“.”可以替代成“->”,即A->name。同时注意struct后是要加;的。#include<stdio.h>structPerson{ charname[10]; shortheigh......
  • openpyxl模块------------------------------------------统计加班时间
    #####################统计加班时间代码######################################fromopenpyxlimportWorkbook,load_workbookfromdatetimeimportdatedefcreate_data():wb=Workbook()sh=wb.activerows=[['Date','姓名','打卡时间&#......
  • 为中船710所提供芯片原子钟、铷原子钟、铷钟模块、铷钟基准
    近期中国船舶重工集团有限公司第七一〇研究所采购了一批我司自主研发的的芯片原子钟。中国船舶重工集团有限公司第七一〇研究所是中国军工科学研究单位,始建于1953年,原名中国军事科学院第七一〇研究所。该研究所的上级单位是中国军事科学院,隶属于中国科技部。该研究所设计和开发了大......
  • C/C++程序的内存开辟——《初学C语言第55天》
    //————C/C++程序的内存开辟C++程序内存分配的几个区域://intt=2;//staticintr=1;//voidtest()//{//  statice=1;//  intn=1;//  intarr[10]={1,2,3,4};//  charg[]="helloworld";//  char*p="abcd";//  int*a=(int*)malloc......