提问人:Ether 提问时间:7/7/2023 最后编辑:Govind ParmarEther 更新时间:7/8/2023 访问量:79
关于 C 运行时库的两个问题
Two questions about C run time library
问:
我想检查信号量头文件中函数 sem_post() 的详细信息,如下所示
extern int sem_post (sem_t *__sem) __THROWNL __nonnull ((1));
然后我在 glibc 的源代码中找到 sem_post.c 文件,但在这个文件中,函数的名称是 __new_sem_post()。
我知道这是关于CRT的,但我不知道怎么做。所以,有一些问题:
- 为什么会有“外部”以及它是如何工作的?
- 名称如何从 __new_sem_post() 更改为 sem_post()?
答:
extern
意味着您声明的内容不是(不一定)在这里定义的,而是在其他一些单位中定义的。
所以不为变量分配内存。它只是向编译器声明,在此代码的其他地方(可能在另一个编译单元中,即另一个文件)将定义该变量。也就是说,在其他地方,有一个宣言。extern int x;
x
.c
int x;
这使编译器知道,如果某些代码使用 .它不会说“x: symbol undefined”,而是知道这是某个类型的全局变量,并编译任何引用这样的代码。x
x
int
x
然后,当然,在某个时候,有必要检查确实存在这样的变量。但这是在链接阶段完成的。也就是说,当您将每个文件单独编译为一个文件时,则不会这样做。但是,当您将所有文件以及库放在一起以创建可执行文件时。.c
.o
.o
理解,主要是,这就是文件的作用:它们不包含代码(我的意思是不是应该生成汇编代码的代码),而是声明。声明,其目的是在编译文件时为编译器提供一些上下文信息。.h
.c
例
让我们考虑一个示例案例,在文件中,您这样做testsin.c
extern double sin(double x);
int main(){
printf("%f\n", sin(1.0));
}
然后使用 编译该文件。
然后将给定的库和库链接在一起,创建一个可执行文件,使用gcc -c testsin.c
.o
libm
testsin
gcc -o testsin testsin.o -lm
在编译 () 时,编译器没有任何关于函数的代码,也没有任何理由知道你在说什么,以及这个函数在哪里。
稍后,在链接 () 时,您将提供包含函数的库 (,此处使用 )。但那是以后的事了。没有任何 DeLorean,它无法在编译时猜测 () 这是什么。所以你必须告诉它“别担心,不要提出错误,我发誓这个函数存在,有这个签名,我稍后会提供给你”。gcc -c
testsin
sin
sin
gcc -o testsin testsin.o -lm
sin
libm.so
-lm
gcc -c
sin
在C语言中,这是由声明说的extern double sin(double);
现在,当然,更现实地说,你要做的不是那样,而是
#include <math.h>
int main(){
printf("%f\n", sin(1.0));
}
但这是完全一样的事情。顾名思义,只需在代码中“复制和粘贴”内容即可。而 math.h 只包含一些这样的声明。#include
math.h
extern double sin(double);
含蓄extern
我把主要信息留到最后,因为我想,一旦澄清了这一切,你之所以惊讶地看到这一点,是因为你已经看到了一些其他代码,只是为了做同样的声明。extern
double 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_
我想检查信号量头文件中函数 sem_post() 的详细信息,
这可能是一个错误。头文件主要用于机器使用,而不是用于人类使用。它们通常很难遵循,尤其是标准库的那些。请改为阅读面向人类的文档。例如,手册页。
下面
extern int sem_post (sem_t *__sem) __THROWNL __nonnull ((1));
然后我在 glibc 的源代码中找到 sem_post.c 文件,但在 此文件,函数的名称为 __new_sem_post()。
嗯,不。您找到了一个看似名为 的函数的源。从它的名称和上下文来看,这个函数无疑与 有关,但如果这真的是它的名字(见下文),那么它是一个单独的函数。__new_sem_post
sem_post
我知道这是关于CRT的
题外:除了在Microsoft领域,“C运行时库”是一个不常见的短语,其初始化为“CRT”更是如此。“[C] 标准库”更传统,尤其是当您谈论其规范时,而不是将其表达为可执行代码时。
- 为什么会有“外部”以及它是如何工作的?
因为有人迂腐。 声明该函数具有外部链接,这意味着任何源文件中的任何函数都可以通过其名称调用该函数。另一种选择是内部链接,这意味着指定的名称仅将函数标识到同一源文件/翻译单元中的其他函数。但是是函数声明的默认值,因此很少为函数显式声明(变量是另一回事)。extern
extern
- 名称如何从 __new_sem_post() 更改为 sem_post()?
事实并非如此。
现在,有可能应用了一些预处理器的魔力,让你认为有一个名为 的函数,而它的名字实际上是 。也许你会倾向于将其描述为名称更改,但事实并非如此。__new_sem_post
sem_post
也可能有一个您忽略的单独功能。无论哪种方式,函数都具有定义它们时使用的名称。C 没有定义任何可以改变这些的机制。sem_post
下一个:验证签名
评论
__new_sem_post
old_sem_post
sem_post
nm -D /lib64/libc.so.6 | grep sem_post