🤔️深入浅出地讲讲,C语言中,如何引用自定义文件中的函数

前言

在我们用C语言编程的时候,会遇到这么一个问题。

A文件用到了一个功能,B文件也用到了这个功能,那对于初学者来说,就只能将A文件实现这个功能的代码粘贴一份到B文件。之后如果C文件D文件,也需要这个功能,也只能粘贴代码了。不得不说,这很麻烦。

举个实际的例子,用C语言作数据结构练习的时候,需要输出线性表这个功能:

c 复制代码
void printList(int *data, int length)
{
  for (int i = 0; i < length; i++)
  {
    printf("%d ", data[i]);
  }
  printf("\n");
};

这个功能的使用频率是非常高的,在每个文件都复制这样一份代码是很不划算的一个做法。

可不可以将这个代码放在一个文件里,然后其他文件要用到这个函数,直接引用就好了,没必要直接复制代码。

当然是可以的,C语言提供了这一功能。

从其他文件中引入自定义函数

假设现在有个index.c文件, 这个文件的就是要将data数组中的内容打印出来

c 复制代码
#include <stdio.h>

void printList(int *data, int length)
{
  for (int i = 0; i < length; i++)
  {
    printf("%d ", data[i]);
  }
  printf("\n");
};

int main()
{
  int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
  printList(data, 10);
  return 0;
}

现在编译这个文件:

没有任何提示就是不报错了,

-o 表示编译后的文件名是index,如果不指定文件名,就会默认a.out

运行一下:

正确输出,没有问题

index.c中,负责打印的功能是函数printList,现在将printList放到util文件中去:

c 复制代码
//uitl.c

#include <stdio.h>
int printList(struct List *L)
{
  for (int i = 0; i < L->length; i++)
  {
    printf("%d\n", L->data[i]);
  }

  return 0;
};

index.c文件只剩下main函数了,除此之外,还需要保留printList的函数声明:

函数声明就是含有函数返回类型,函数名称,函数的参数;

函数的定义就是带有函数体的函数声明

util.c中的printList函数就是一个函数定义

c 复制代码
//index.c
#include <stdio.h>

int printList(struct List *L);

int main()
{
  int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};

  struct List list;
  list.data = data;
  list.length = 10;

  printList(&list);
  return 0;
}

这样就可以了,我们来编译下index.c文件

报错了。它说printList这个标识符未定义(undefined symbols: printList),也就是说只有声明,但找不到uitl.c中的printList函数的定义。

这说明gcc命令并不会帮我们自动寻找util.c文件,需要我们手动告诉gcc

在命令行中加入了util.c后,编译就不报错了。咱们来执行一下:

运行成功

大致的过程就是这样,只是成熟的开发一般不这么干。想想,如果有数十个函数需要从其他文件中引入,那就需要在每个文件的前面加上数十个函数声明吗,这也很累人啊。

头文件

我们可以将函数声明放在一个头文件里面,然后再头文件引入头文件就可以了。 头文件就是以.h结尾的文件,里面主要放一些函数声明。 现在创建一个util.h头文件

c 复制代码
// util.h
void printList(int *data, int length);

然后在index.c引入这个头文件:

c 复制代码
//index.c

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

void printList(int *data, int length)
{
  for (int i = 0; i < length; i++)
  {
    printf("%d ", data[i]);
  }
  printf("\n");
};

这样就可以了,编译命令和上面一样,需要告诉gcc,去util中找函数定义,不然又会出现undefined symbols: printList这样的错误

你们注意到没有,index.c中还有一个头文件,stdio.h,这里面其实也放着大量的函数声明。stdio大致意思是standard I/OI/O就是input/ouput的缩写,整体意思就是标准输入输出流

当我们需要在屏幕上输出内容,或者需要在从键盘输入的时候,这个头文件的引入就是必不可少的。

头文件原理

在C文件编译之前,其实还有一个预处理的过程,对于头文件来说,预处理会将头文件里面的内容插入到#include所在的地方。我们来看看:

-E 的作用就是将预处理之后,立即停止翻译过程,不进行后续的编译过程,并且显示预处理的内容

这是index.c文件的预处理输出截图,因为有stdio.h头文件,所以内容很多,我只截了其中一部分。

重点关注输出内容的后面,后面有我们的源代码,源代码上面就是util.h的内容了。 看到这里,相信大家对头文件的作用理解更深刻了吧

链接

在gcc经过对文件的预处理,编译之后,会得到一个目标文件(通常以.o后缀结尾)。

编译的作用是对C语言源文件的内容进行语法分析,进而生成对应的机器指令。但是,对有些函数来说,它的解析是无法完成的。因为它们可能并不是在当前源文件内定义的。也就是说目标文件中还没有函数的定义,需要在链接这一步才会去找函数的定义。

所以这个目标文件不能直接执行,还需要经过链接这一步。

下面将之前的编译命令拆开来看:

-c 表示仅输出目标文件

现将index.c文件编译成目标文件index.o

目标文件是二进制文件,里面就是一大堆的0和1。用编辑器打开,大概长这样:

编辑器对二进制文件做了显示的优化,将二进制用十六进制表示,并且右边会显示二进制对应的字符内容

然后将util.c编译成util.o

最后将两个目标文件链接起来

运行没问题,搞定

总结

这篇文章讲了如何在C语言中,引用其他文件中自己编写的函数,过程中,串讲了一些编译和链接的概念。思路清晰,例子详细,是个不错的文章。

本篇例子运行机器是Mac,window系统可能看起来不同,但其中原理是一样的

相关推荐
mitt_3 分钟前
go语言变量
开发语言·后端·golang
无限大619 分钟前
二维数组搜索:从暴力地毯到二分神技的奇幻之旅
后端
sakabu20 分钟前
cJSON库应用
c语言·笔记·学习
bobz9651 小时前
最近玩了好多把 LOL
后端
爱欲无极1 小时前
基于Flask的微博话题多标签情感分析系统设计
后端·python·flask
cwkiller1 小时前
heapdump深度利用之信息泄露篇
后端
大阳1232 小时前
数据结构2.(双向链表,循环链表及内核链表)
c语言·开发语言·数据结构·学习·算法·链表·嵌入式
CUC-MenG3 小时前
2025牛客多校第六场 D.漂亮矩阵 K.最大gcd C.栈 L.最小括号串 个人题解
c语言·c++·算法·矩阵
knight043 小时前
C语言:预处理
c语言
longxiangam3 小时前
retro-go 1.45 编译及显示中文
c语言·单片机·嵌入式硬件