为什么Git源代码中一些声明为extern和头文件的函数没有包含在source中?

2023-11-28

我想查看真实应用程序的源代码以了解良好的编程实践等。因此我选择了 Git 并下载了 1.8.4 版本的源代码。

随机浏览各种文件后,这两个文件中的一些内容引起了我的注意:strbuf.h strbuf.c

这两个文件显然定义了一个 API本文档.

我有两个问题:

  1. 为什么'strbuf.h'中第16,17,18,19行的函数声明和第6行的全局变量声明为 extern ?

  2. 为什么“strbuf.h”没有 #included 在 strbuf.c 中?

作为一名新手程序员,我一直了解到您在 .c 文件中编写函数定义,而函数声明、宏、内联等则编写在 .h 文件中,然后将其#included 到每个想要使用这些的 .c 文件中功能等

谁能解释一下吗?


strbuf.c包括cache.h and cache.h包括strbuf.h,所以问题 2 的前提(即strbuf.c不包括strbuf.h) 是错误的:它确实包含它,只是不是直接包含它。

extern应用于函数

The extern函数声明从来不需要关键字,但它确实有作用:它声明命名函数的标识符(即函数的名称)与任何先前可见的声明具有相同的链接,或者如果没有这样的声明可见,则标识符具有外部链接。这个相当令人困惑的措辞实际上意味着,考虑到:

static int foo(void); extern int foo(void);

第二次声明foo也声明了它static,赋予其内部联系。如果你写:

static int foo(void); int foo(void); /* wrong in 1990s era C */

you have declared it first as having internal linkage, and then second as having external linkage, and in pre-1999 versions of C,1 that produces undefined behavior. In one sense, then, the extern keyword adds some safety (at the price of confusion) as it can mean static when necessary. But you could always write static again, and extern is not a panacea:

extern int foo(void); static int foo(void); /* ERROR */

这第三种形式仍然是错误的。首先extern声明没有先前可见的声明,所以foo有外部链接,然后是第二个static声明给出foo内部链接,产生未定义的行为。

简而言之,extern函数声明中不需要。有些人只是出于风格原因更喜欢它。

(注:我省略了extern inline在 C99 中,这有点奇怪,并且实现各不相同。看http://www.greenend.org.uk/rjk/2003/03/inline.html更多细节。)

extern应用于变量声明

The extern变量声明上的关键字具有多种不同的效果。首先,与函数声明一样,它会影响标识符的链接。其次,对于任何函数外部的标识符(两种通常意义上的“全局变量”),它会导致声明成为声明,而不是定义,前提是该变量没有被初始化。

对于函数内部的变量(即具有“块作用域”),例如somevar in:

void f(void) {
    extern int somevar;
    ...
}

the extern关键字使标识符具有某种链接(内部或外部),而不是“无链接”(对于自动持续时间局部变量)。在这个过程中,也导致变量本身具有静态持续时间,而不是自动持续时间。 (自动持续时间变量从不具有链接,并且始终具有块作用域,而不是文件作用域。)

与函数声明一样,链接extern如果存在先前可见的内部链接声明,则指派为内部,否则为外部。所以x inside f()这里有内部联系,尽管extern关键词:

static int x;
void f(void) {
    extern int x; /* note: don't do this */
    ...
}

编写这种代码的唯一原因是为了迷惑其他程序员,所以不要这样做。 :-)

一般来说,使用注释“全局”(即文件范围、静态持续时间、外部链接)变量的原因extern关键字是为了防止该特定声明成为定义。当多次定义同一个名称时,使用所谓的“def/ref”模型的 C 编译器会在链接时出现消化不良。因此,如果file1.c says int globalvar; and file2.c还说int globalvar;,两者都是定义,代码可能无法编译(尽管大多数类 Unix 系统默认使用所谓的“通用模型”,这无论如何都可以实现)。如果您在头文件中声明这样的变量 - 它可能包含在许多不同的头文件中.c文件—使用extern使该声明“只是一个声明”。

其中,只有一个.c文件然后可以再次声明该变量,而无需extern关键字和/或包含初始值设定项。或者,有些人更喜欢头文件使用如下所示的样式:

/* foo.h */
#ifndef EXTERN
# define EXTERN extern
#endif
EXTERN int globalvar;

在这种情况下,其中一个(且仅有一个).c文件可以包含以下序列:

#define EXTERN
#include "foo.h"

在这里,自从EXTERN被定义,则#ifndef关闭后续的#define和线EXTERN int globalvar;扩展到只是int globalvar;这样这就成为一个定义而不是一个声明。就我个人而言,我不喜欢这种编码风格,尽管它确实满足“不要重复自己”的原则。我大多发现大写EXTERN具有误导性,并且这种模式对初始化没有帮助。那些喜欢它的人通常会添加第二个宏来隐藏初始化器:

#ifndef EXTERN
# define EXTERN extern
# define INIT_VAL(x) /*nothing*/
#else
# define INIT_VAL(x) = x
#endif

EXTERN int globalvar INIT_VAL(42);

但当要初始化的项需要复合初始化器(例如,struct应该初始化为{ 42, 23, 17, "hike!" }).

(注意:我在这里故意掩盖了整个“暂定定义”的事情。没有初始化程序的定义只是“暂定定义”,直到翻译单元结束。这允许某些类型的前向引用,否则这些引用太困难了来表达。这通常不是很重要。)

包括声明函数的标头f在定义函数的代码中f

这始终是一个好主意,原因很简单:编译器将比较宣言 of f()在标题中针对定义 of f()在代码中。如果两者不匹配(出于任何原因 - 通常是初始编码中的错误,或者在维护期间未能更新两者之一,但偶尔只是由于猫走在键盘综合症或类似情况),编译器可以捕获错误在编译时。


1The 1999 C standard says that omitting the extern keyword in a function declaration means the same thing as using the extern keyword there. This is much simpler to describe, and means you get defined (and sensible) behavior instead of undefined (and therefore maybe-good maybe-bad behavior).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么Git源代码中一些声明为extern和头文件的函数没有包含在source中? 的相关文章

  • 自动从 C# 代码进行调试过程并读取寄存器值

    我正在寻找一种方法来读取某个地址的 edx 注册表 就像这个问题中所问的那样 读取eax寄存器 https stackoverflow com questions 16490906 read eax register 虽然我的解决方案需要用
  • C++ 求二维数组每一行的最大值

    我已经设法用这个找到我的二维数组的每一行的最小值 void findLowest int A Cm int n int m int min A 0 0 for int i 0 i lt n i for int j 0 j lt m j if
  • 如何在没有 Control.Invoke() 的情况下从后台线程修改控件属性

    最近 我们遇到了一些旧版 WinForms 应用程序 我们需要更新一些新功能 在专家测试该应用程序时 发现一些旧功能被破坏 无效的跨线程操作 现在 在您认为我是新手之前 我确实有一些 Windows 窗体应用程序的经验 我不是专家 但我认为
  • Visual Studio 2017 - 无法将提交的更改推送到本地存储库

    我在 Win 10 Enterprise 上运行 VS 2017 Enterprise 15 7 3 我的本地硬盘上有一个 git 存储库 我在其中进行了一些更改 现在希望使用 VS 将这些更改 提交并推送 到我们位于网络文件共享之一的中央
  • SSH 主机密钥指纹与模式 C# WinSCP 不匹配

    我尝试通过 WinSCP 使用 C 连接到 FTPS 服务器 但收到此错误 SSH 主机密钥指纹 与模式不匹配 经过大量研究 我相信这与密钥的长度有关 当使用 服务器和协议信息 下的界面进行连接时 我从 WinSCP 获得的密钥是xx xx
  • 为什么禁止在 constexpr 函数中使用 goto?

    C 14 对你能做什么和不能做什么有规则constexpr功能 其中一些 没有asm 没有静态变量 看起来相当合理 但标准也不允许goto in constexpr功能 即使它允许其他控制流机制 这种区别背后的原因是什么 我以为我们已经过去
  • C# 中值类型和引用类型有什么区别? [复制]

    这个问题在这里已经有答案了 我知道一些差异 值类型存储在堆栈上 而引用类型存储在托管堆上 值类型变量直接包含它们的值 而引用变量仅包含对托管堆上创建的对象位置的引用 我错过了任何其他区别吗 如果是的话 它们是什么 请阅读 堆栈是一个实现细节
  • 如何在 WPF RichTextBox 中跟踪 TextPointer?

    我正在尝试了解 WPF RichTextBox 中的 TextPointer 类 我希望能够跟踪它们 以便我可以将信息与文本中的区域相关联 我目前正在使用一个非常简单的示例来尝试弄清楚发生了什么 在 PreviewKeyDown 事件中 我
  • 使用 C# 在 WinRT 中获取可用磁盘空间

    DllImport kernel32 dll SetLastError true static extern bool GetDiskFreeSpaceEx string lpDirectoryName out ulong lpFreeBy
  • 写入和读取文本文件 - C# Windows 通用平台应用程序 Windows 10

    有用 但在显示任何内容之前 您必须在文本框中输入内容 我想那是因为我使用了 TextChanged 事件处理程序 如果我希望它在没有用户交互的情况下显示文本文件的内容 我应该使用哪个事件处理程序 因此 我想在按下按钮时将一些数据写入 C W
  • 如何针对 Nancy 中的 Active Directory 进行身份验证?

    这是一篇过时的文章 但是http msdn microsoft com en us library ff650308 aspx paght000026 step3 http msdn microsoft com en us library
  • C# 用数组封送结构体

    假设我有一个类似于 public struct MyStruct public float a 我想用一些自定义数组大小实例化一个这样的结构 在本例中假设为 2 然后我将其封送到字节数组中 MyStruct s new MyStruct s
  • HttpClient 像浏览器一样请求

    当我通过 HttpClient 类调用网站 www livescore com 时 我总是收到错误 500 可能服务器阻止了来自 HttpClient 的请求 1 还有其他方法可以从网页获取html吗 2 如何设置标题来获取html内容 当
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • A* 之间的差异 pA = 新 A;和 A* pA = 新 A();

    在 C 中 以下两个动态对象创建之间的确切区别是什么 A pA new A A pA new A 我做了一些测试 但似乎在这两种情况下 都调用了默认构造函数 并且仅调用了它 我正在寻找性能方面的任何差异 Thanks If A是 POD 类
  • 本地分支显示在 GitHub 的“网络”视图上

    我们使用 Git 我们的工作流程由 dev 和 master 分支组成 它们位于 GitHub 和每个开发人员的本地存储库上 不会直接在 master 或 dev 上执行任何工作 而是在本地分支中执行工作 并且仅在 dev 上进行合并 然后
  • 初始化变量的不同方式

    在 C 中初始化变量有多种方法 int z 3 与 int 相同z 3 Is int z z 3 same as int z z 3 您可以使用 int z z 3 Or just int z 3 Or int z 3 Or int z i
  • 将日期参数传递给对 MVC 操作的 ajax 调用的安全方法

    我有一个 MVC 操作 它的参数之一是DateTime如果我通过 17 07 2012 它会抛出一个异常 指出参数为空但不能有空值 但如果我通过01 07 2012它被解析为Jan 07 2012 我将日期传递给 ajax 调用DD MM
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • GDK3/GTK3窗口更新的精确定时

    我有一个使用 GTK 用 C 语言编写的应用程序 尽管该语言对于这个问题可能并不重要 这个应用程序有全屏gtk window与单个gtk drawing area 对于绘图区域 我已经通过注册了一个刻度回调gtk widget add ti

随机推荐

  • C# 字段与属性[重复]

    这个问题在这里已经有答案了 可能的重复 C 中属性和字段的区别 我认为基本属性 get set 与公共字段相同 唯一的优点是能够在不破坏二进制兼容性的情况下更改它们 按照我在这里得到的答案https stackoverflow com a
  • 基于 FLEX 的应用程序的自动化测试

    建议使用哪些工具 最好是开源工具 在基于 FLEX 的 Web 应用程序上驱动自动化测试套件 如果同一工具还具有驱动 Web 服务的内置功能 那就太好了 Adobe 自己分发了一个测试框架 FlexUnit
  • 如何在 C# 中删除被另一个进程锁定的文件?

    我正在寻找一种方法来删除被另一个进程使用 C 锁定的文件 我怀疑该方法必须能够找到哪个进程正在锁定文件 也许通过跟踪句柄 尽管我不确定如何在 C 中执行此操作 然后在能够使用以下命令完成文件删除之前关闭该进程File Delete 杀死其他
  • Google 表格中的日期时间和时区?

    我想计算 Google 表格中不同时区的日期时间之间的时差 如果我将两个字段格式化为 日期时间 另一个字段格式化为 持续时间 我可以使用差异运算符成功计算同一时区内的差异 Example A1 1 10 2016 10 10 00 B2 1
  • Langford 序列实现 Haskell 或 C

    在组合数学中 兰福德配对 也称为 Langford 序列 是以下序列的排列2n数字1 1 2 2 n n 其中两个 1 相距 1 个单位 两个 2 相距 2 个单位 更一般地 每个数字 k 的两个副本相距 k 个单位 例如 兰福德配对n 3
  • Java 正则表达式线程安全吗?

    我有一个使用的函数Pattern compile and a Matcher在字符串列表中搜索模式 该函数在多线程中使用 每个线程都会有一个唯一的模式传递给Pattern compile当线程被创建时 线程和模式的数量是动态的 这意味着我可
  • 读取多个文件并根据用户输入计算平均值

    我正在尝试在 R 中编写一个需要 3 个输入的函数 目录 污染物 id 我的计算机上有一个目录 里面充满了 CSV 文件 即超过 300 个 该函数的功能如下所示 pollutantmean lt function directory po
  • 调用同步 xm​​lhttprequest 时 IE 挂起 5 分钟

    我有一个 Web 应用程序 并使用 ajax 回调我的网络服务器来获取数据 有时 在相当不可预测的时刻 但可以重现 IE 完全挂起 5 分钟 窗口显示 未响应 然后返回 xmlhttprequest 对象响应错误 12002 我可以重现它的
  • 点击NSView

    我有一个NSView包含多个子视图 其中一个子视图是透明的并分层在顶部 我需要能够点击这个视图到下面的子视图 以便下面的视图获得第一响应者状态 但是所有鼠标事件都卡在顶部视图上 alpha 是1 因为我在里面画了东西 所以它应该只点击透明区
  • 更改滚动条上的 div 高度

    我想要的是顶部 标题 的 div 当您第一次加载页面时 它将处于最大高度 50px 当您向下滚动页面时 我希望高度平滑地降低到 30px 的最小高度 我猜我应该使用 jQuery 但我没有那么丰富的经验 所以我现在不知道解决方案 这是我当前
  • Angular 2 - 如何在 addEventListener 处理函数内调用打字稿函数?

    我正在尝试在添加到页面跨度的处理程序函数内调用另一个打字稿函数 任何类型 当我这样做时 处理函数工作正常 并且会执行基本的操作 例如设置变量 console log 等 但是 当尝试调用任何类型的函数时 它会抛出 无法读取未定义的属性 fu
  • 如何在 PHP 中更好地使用 Smarty?

    我发现在 PHP 中使用 Smarty 有时需要花费额外的时间 1 使用与 PHP 本身完全不同的语法2 需要检查小案例 因为文档没有提供更详细的细节 例如 逃逸 http www smarty net manual en language
  • 是否可以从 C++ 中的模板类型获取 char* 名称

    我想获取模板类型的字符串名称 const char 不幸的是我无法访问 RTTI template lt typename T gt struct SomeClass const char GetClassName const return
  • 如何增加 Flutter 中特定列表项的计数器?

    就像下面的示例图片一样 我想在单击单个列表项的按钮时增加或减少数量 如果我增加 setState 中的计数器 它会在每个列表项中递增 我需要这方面的帮助 特别是在处理 Flutter 中的特定列表项时 示例图片 2 任何帮助表示赞赏 提前致
  • 更新 ElasticSearch 设置中的分析器

    我正在使用 Sense Chrome 插件 并且我已经成功设置了一个分析器并且它工作正常 如果我对设置发出 GET media settings 则会返回以下内容 media settings index creation date 142
  • 如何将 console.log 写入文件

    现在我使用以下方式显示信息 console log kraken id markets 但是 我想将所有发送到控制台的信息写入文件中 如何通过完成以下代码来完成此操作 use strict var ccxt require ccxt asy
  • 我可以限制通用堆栈的深度吗?

    是否有内置方法来限制 System Collection Generics Stack 的深度 那么 如果您处于最大容量 推入新元素会删除堆栈的底部吗 我知道我可以通过转换为数组并重建堆栈来做到这一点 但我认为可能已经有一个方法了 编辑 我
  • 使用 (Core)Foundation 折叠/规范化连字(例如 Æ 到 ae)

    我正在编写一个助手 它对输入字符串执行多次转换 以便创建该字符串的搜索友好表示 考虑以下场景 德语或法语文本全文搜索 The entries in your datastore contain M ller Gro mann inglet
  • 什么是三法则?

    什么是复制对象 mean 什么是复制构造函数和复制赋值运算符 我什么时候需要自己申报 如何防止我的对象被复制 介绍 C 处理用户定义类型的变量值语义 这意味着对象在各种上下文中隐式复制 我们应该理解 复制对象 的实际含义 让我们考虑一个简单
  • 为什么Git源代码中一些声明为extern和头文件的函数没有包含在source中?

    我想查看真实应用程序的源代码以了解良好的编程实践等 因此我选择了 Git 并下载了 1 8 4 版本的源代码 随机浏览各种文件后 这两个文件中的一些内容引起了我的注意 strbuf h strbuf c 这两个文件显然定义了一个 API本文