🤔️深入浅出地讲讲,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系统可能看起来不同,但其中原理是一样的

相关推荐
大帅哥_几秒前
访问限定符
c语言·c++
小林熬夜学编程30 分钟前
【Linux系统编程】第五十弹---构建高效单例模式线程池、详解线程安全与可重入性、解析死锁与避免策略,以及STL与智能指针的线程安全性探究
linux·运维·服务器·c语言·c++·安全·单例模式
我qq不是4515165238 分钟前
C语言指针作业
c语言
苏言の狗40 分钟前
小R的二叉树探险 | 模拟
c语言·数据结构·算法·宽度优先
加载中loading...40 分钟前
C/C++实现tcp客户端和服务端的实现(从零开始写自己的高性能服务器)
linux·运维·服务器·c语言·网络
捂月1 小时前
Spring Boot 核心逻辑与工作原理详解
java·spring boot·后端
Nightselfhurt1 小时前
RPC学习
java·spring boot·后端·spring·rpc
Heisenberg~2 小时前
详解八大排序(五)------(计数排序,时间复杂度)
c语言·数据结构·排序算法
lb36363636365 小时前
分享一下arr的意义(c基础)(必看)(牢记)
c语言·知识点
南东山人7 小时前
一文说清:C和C++混合编程
c语言·c++