关于 C 运行时库的两个问题

Two questions about C run time library

提问人:Ether 提问时间:7/7/2023 最后编辑:Govind ParmarEther 更新时间:7/8/2023 访问量:79

问:

我想检查信号量头文件中函数 sem_post() 的详细信息,如下所示

extern int sem_post (sem_t *__sem) __THROWNL __nonnull ((1));

然后我在 glibc 的源代码中找到 sem_post.c 文件,但在这个文件中,函数的名称是 __new_sem_post()。

我知道这是关于CRT的,但我不知道怎么做。所以,有一些问题:

  1. 为什么会有“外部”以及它是如何工作的?
  2. 名称如何从 __new_sem_post() 更改为 sem_post()?
C 显像管

评论

0赞 n. m. could be an AI 7/7/2023
编译器和链接器魔术(也称为“语言扩展”)的“名称如何更改”,允许人们在库中创建经过验证的符号。所以两者都改成了不同的版本。要查看它们,请执行(或您的 libc.so.6 所在的任何地方),__new_sem_postold_sem_postsem_postnm -D /lib64/libc.so.6 | grep sem_post
0赞 Clifford 7/8/2023
在 SO 上,习惯是在一篇文章中提出一个问题。你的标题显然是在宣传你违反了这一规范,同时几乎没有提供关于这个问题的具体信息。你有一个问题是隐含的 - 毕竟这是一个问答 - 关于C的事实在标签中。最好不要说你“知道”什么,只是让别人告诉你。如果有人选择挑战你的断言,那么徒劳无功的分心的机会就会减少。

答:

2赞 chrslg 7/7/2023 #1

extern意味着您声明的内容不是(不一定)在这里定义的,而是在其他一些单位中定义的。 所以不为变量分配内存。它只是向编译器声明,在此代码的其他地方(可能在另一个编译单元中,即另一个文件)将定义该变量。也就是说,在其他地方,有一个宣言。extern int x;x.cint x;

这使编译器知道,如果某些代码使用 .它不会说“x: symbol undefined”,而是知道这是某个类型的全局变量,并编译任何引用这样的代码。xxintx

然后,当然,在某个时候,有必要检查确实存在这样的变量。但这是在链接阶段完成的。也就是说,当您将每个文件单独编译为一个文件时,则不会这样做。但是,当您将所有文件以及库放在一起以创建可执行文件时。.c.o.o

理解,主要是,这就是文件的作用:它们不包含代码(我的意思是不是应该生成汇编代码的代码),而是声明。声明,其目的是在编译文件时为编译器提供一些上下文信息。.h.c

让我们考虑一个示例案例,在文件中,您这样做testsin.c

extern double sin(double x);

int main(){
    printf("%f\n", sin(1.0));
}

然后使用 编译该文件。
然后将给定的库和库链接在一起,创建一个可执行文件,使用
gcc -c testsin.c.olibmtestsingcc -o testsin testsin.o -lm

在编译 () 时,编译器没有任何关于函数的代码,也没有任何理由知道你在说什么,以及这个函数在哪里。 稍后,在链接 () 时,您将提供包含函数的库 (,此处使用 )。但那是以后的事了。没有任何 DeLorean,它无法在编译时猜测 () 这是什么。所以你必须告诉它“别担心,不要提出错误,我发誓这个函数存在,有这个签名,我稍后会提供给你”。gcc -ctestsinsinsingcc -o testsin testsin.o -lmsinlibm.so-lmgcc -csin

在C语言中,这是由声明说的extern double sin(double);

现在,当然,更现实地说,你要做的不是那样,而是

#include <math.h>

int main(){
    printf("%f\n", sin(1.0));
}

但这是完全一样的事情。顾名思义,只需在代码中“复制和粘贴”内容即可。而 math.h 只包含一些这样的声明。#includemath.hextern double sin(double);

含蓄extern

我把主要信息留到最后,因为我想,一旦澄清了这一切,你之所以惊讶地看到这一点,是因为你已经看到了一些其他代码,只是为了做同样的声明。externdouble sin(double);

嗯,是的,隐含在函数声明中:如果没有代码(没有包含actula代码),那么以任何方式隐含。extern{...}extern

对于变量,您必须始终明确地说出来,因为这是变量的创建,而不是声明。
因此,变量创建 = .函数创建 = 变量声明 =
。函数声明 = OR,因为在这种情况下这是明确的。
int x;int x;double sin(double x){...}extern int x;extern double sin(double x);double sin(double);

至于你的第二个问题,我不知道(无论如何,这里的规则是你应该问一个问题)。但是,在内部代码中,例如特定于架构的函数的“私有”实现是很常见的,用一些命名,然后在其他一些地方,一个简单的别名来表示指向或类似的东西。_somefunc__someprefix_somefunc_somesuffix_

1赞 John Bollinger 7/8/2023 #2

我想检查信号量头文件中函数 sem_post() 的详细信息,

这可能是一个错误。头文件主要用于机器使用,而不是用于人类使用。它们通常很难遵循,尤其是标准库的那些。请改为阅读面向人类的文档。例如,手册页

下面

extern int sem_post (sem_t *__sem) __THROWNL __nonnull ((1));

然后我在 glibc 的源代码中找到 sem_post.c 文件,但在 此文件,函数的名称为 __new_sem_post()。

嗯,不。您找到了一个看似名为 的函数的源。从它的名称和上下文来看,这个函数无疑与 有关,但如果这真的是它的名字(见下文),那么它是一个单独的函数。__new_sem_postsem_post

我知道这是关于CRT的

题外:除了在Microsoft领域,“C运行时库”是一个不常见的短语,其初始化为“CRT”更是如此。“[C] 标准库”更传统,尤其是当您谈论其规范时,而不是将其表达为可执行代码时。

  1. 为什么会有“外部”以及它是如何工作的?

因为有人迂腐。 声明该函数具有外部链接,这意味着任何源文件中的任何函数都可以通过其名称调用该函数。另一种选择是内部链接,这意味着指定的名称仅将函数标识到同一源文件/翻译单元中的其他函数。但是是函数声明的默认值,因此很少为函数显式声明(变量是另一回事)。externextern

  1. 名称如何从 __new_sem_post() 更改为 sem_post()?

事实并非如此。

现在,有可能应用了一些预处理器的魔力,让你认为有一个名为 的函数,而它的名字实际上是 。也许你会倾向于将其描述为名称更改,但事实并非如此。__new_sem_postsem_post

也可能有一个您忽略的单独功能。无论哪种方式,函数都具有定义它们时使用的名称。C 没有定义任何可以改变这些的机制。sem_post