当编译C程序时,它按照预处理器,编译器,汇编器,链接器的顺序进行。
linker
的主要任务之一是使程序可以使用库函数代码。
链接器可以通过静态或动态两种方式链接它们。
stdio.h
仅包含声明,其中不存在任何定义。
我们只在程序中包含stdio.h
来说明编译器有关函数的返回类型和名称eg(printf(),scanf(),getc(),putc()...)..
然后如何printf()
和scanf()
相关联?
总计" C"库是否与程序动态链接?
#include" stdio.h"
int main() { int n;
printf("输入一个整数\ n"); scanf("%d",& n);
if(n%2 == 0) 的printf("即使\ n&#34); 其他 的printf("奇数\ n&#34);
返回0; }
答案 0 :(得分:7)
我认为您要问的问题是:“我知道printf
和scanf
等函数是由C运行时库实现的。但我可以使用它们而不告诉我的编译器和/或IDE将我的程序与C运行时库链接。为什么我不需要这样做?“
该问题的答案是:“不需要与C运行时库链接的程序非常非常罕见。即使您没有明确使用任何库函数,您仍然需要启动代码,并且编译器可能会在“引擎盖”下发出对memcpy
,浮点仿真函数等的调用。因此,为方便起见,编译器会自动将您的程序与C运行时库链接起来,除非您告诉它不这样做。“
您必须查阅编译器的文档,以了解如何告诉它不要在C运行时库中链接。 GCC使用-nostdlib
命令行选项。下面,我展示了你必须跳过才能完成这项工作的箍......
$ cat > test.c
#include <stdio.h>
int main(void) { puts("hello world"); return 0; }
^D
$ gcc -nostdlib test.c && { ./a.out; echo $?; }
/usr/bin/ld: warning: cannot find entry symbol _start
/tmp/cc8svIx5.o: In function ‘main’:
test.c:(.text+0xa): undefined reference to ‘puts’
collect2: error: ld returned 1 exit status
puts
显然在C库中,但这个神秘的“入口符号_start
”也是如此。关闭C库,你也必须自己提供 ......
$ cat > test.c
int _start(void) { return 0; }
^D
$ gcc -nostdlib test.c && { ./a.out; echo $?; }
Segmentation fault
139
它现在链接,但是我们遇到了分段错误,因为_start
无处可回!操作系统希望它调用_exit
。好吧,让我们这样做......
$ cat > test.c
extern void _exit(int);
void _start(void) { _exit(0); }
^D
$ gcc -nostdlib test.c && { ./a.out; echo $?; }
/tmp/ccuDrMQ9.o: In function `_start':
test.c:(.text+0xa): undefined reference to `_exit'
collect2: error: ld returned 1 exit status
...坚果,_exit
也是C运行时库中的一个函数!原始系统呼叫时间......
$ cat > test.c
#include <unistd.h>
#include <sys/syscall.h>
void _start(void) { syscall(SYS_exit, 0); }
^D
$ gcc -nostdlib test.c && { ./a.out; echo $?; }
/tmp/cchtZnbP.o: In function `_start':
test.c:(.text+0x14): undefined reference to `syscall'
collect2: error: ld returned 1 exit status
... nope,syscall
也是 C运行时中的一个函数。我想我们只需要使用汇编!
$ cat > test.S
#include <sys/syscall.h>
.text
.globl _start
.type _start, @function
_start:
movq $SYS_exit, %rax
movq $0, %rdi
syscall
$ gcc -nostdlib test.S && { ./a.out; echo $?; }
0
最后,这很有效。在我的电脑上。它不适用于不同的操作系统,具有不同的系统调用程序集级别约定。
如果您只是为了进行系统调用而必须使用汇编语言,那么您现在可能想知道什么是-nostdlib
甚至是好的。它旨在用于编译完全独立的低级系统程序,如引导程序,内核和C运行时本身(部分) - 无论如何都要实现自己的所有内容 / em>的
如果我们从头开始重新做一遍,将只是系统调用包装器,与语言无关的进程启动分离出一个低级语言无关运行时可能是有意义的代码,以及任何语言的编译器可能需要在“引擎盖下”调用的函数(memcpy
,_Unwind_RaiseException
,__muldi3
,这类事情。这个想法的问题是它迅速遭受任务蔓延 - 你是否包括errno
?通用线程原语? (哪些是哪种语义?)动态链接器? malloc
的实现,上面的几个需要什么? Windows的ntdll.dll
始于此概念,在Windows 10中的磁盘上为1.8MB,在我的Linux分区上(稍微)大于而不是libc.so
+ ld.so
。编写仅使用ntdll.dll
的程序是罕见且困难的,即使您是Microsoft(唯一的例子我确定是{{} 1}},也可能是一个内核组件。)
答案 1 :(得分:2)
通常,标准C库是动态链接的。这主要是因为一旦程序静态链接,其中的代码将永远固定。如果有人在printf
或scanf
中找到并修复了错误,则必须再次链接每个程序才能获取固定代码。
如果是动态链接,则所有可执行文件(在链接后创建)都不包含printf
或scanf
代码的副本。如果有printf
的新固定版本可用,则会在运行时获取它。
答案 2 :(得分:0)
-static-libstdc++
当g ++程序用于链接C ++程序时,通常是这样 自动链接libstdc ++。如果libstdc ++可用作 共享库,并没有使用-static选项,然后这个链接 针对libstdc ++的共享版本。这通常很好。 但是,冻结libstdc ++的版本有时很有用 程序使用,而不是一直到完全静态链接。 -static-libstdc ++选项指示g ++驱动程序链接libstdc ++ 静态地,不必静态地链接其他库。
有关详细信息,请查看此主题。 How can i statically link standard library to my c++ program?
答案 3 :(得分:-2)
它们是静态链接的,这样您的程序就可以在运行程序之前确定是否存在任何编译错误。