文章目录
  1. Answers

Answers

(1)程序为什么要被编译器编译之后才能运行?

解:程序的源代码属于高级语言,而能让计算机理解执行的只有机器语言;因此,编译器的编译就是做将高级语言转换为机器语言的工作的。

 

(2)编译器是如何将C语言程序转化成可执行的机器码的?

解:经4个步骤:预处理、编译、汇编、链接。详见编译与链接

 

(3)可执行文件中有哪些内容,它们是如何组织的?

解:一个动态链接的可执行文件具有如下的段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Sections:
Idx Name Size VMA LMA File off Algn
0 .interp 0000001c 0000000000000238 0000000000000238 00000238 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 0000000000000254 0000000000000254 00000254 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 0000000000000274 0000000000000274 00000274 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 0000001c 0000000000000298 0000000000000298 00000298 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 000000a8 00000000000002b8 00000000000002b8 000002b8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 00000084 0000000000000360 0000000000000360 00000360 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 0000000e 00000000000003e4 00000000000003e4 000003e4 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 00000000000003f8 00000000000003f8 000003f8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rela.dyn 000000c0 0000000000000418 0000000000000418 00000418 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rela.plt 00000018 00000000000004d8 00000000000004d8 000004d8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 00000017 00000000000004f0 00000000000004f0 000004f0 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000020 0000000000000510 0000000000000510 00000510 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .plt.got 00000008 0000000000000530 0000000000000530 00000530 2**3
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .text 000001f2 0000000000000540 0000000000000540 00000540 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .fini 00000009 0000000000000734 0000000000000734 00000734 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
15 .rodata 0000001e 0000000000000740 0000000000000740 00000740 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame_hdr 00000044 0000000000000760 0000000000000760 00000760 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .eh_frame 00000128 00000000000007a8 00000000000007a8 000007a8 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
18 .init_array 00000008 0000000000200db8 0000000000200db8 00000db8 2**3
CONTENTS, ALLOC, LOAD, DATA
19 .fini_array 00000008 0000000000200dc0 0000000000200dc0 00000dc0 2**3
CONTENTS, ALLOC, LOAD, DATA
20 .dynamic 000001f0 0000000000200dc8 0000000000200dc8 00000dc8 2**3
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000048 0000000000200fb8 0000000000200fb8 00000fb8 2**3
CONTENTS, ALLOC, LOAD, DATA
22 .data 00000014 0000000000201000 0000000000201000 00001000 2**3
CONTENTS, ALLOC, LOAD, DATA
23 .bss 0000000c 0000000000201014 0000000000201014 00001014 2**2
ALLOC
24 .comment 00000029 0000000000000000 0000000000000000 00001014 2**0
CONTENTS, READONLY

可执行文件主要包含

  • .text代码段(存放执行指令);
  • .data数据段(已初始化的全局变量与局部静态变量);
  • .rodata只读数据段(存放只读数据);
  • .bss段(未初始化的全局变量与局部静态变量);
  • .interp段(指出程序所需动态链接器版本);
  • .dynamic段(动态链接的基本信息);
  • .dynsym段(保存动态链接的共享模块间符号的导入导出关系);
  • .dynstr段(保存.dynsym段中符号的名称);
  • .got段(全局偏移表);
  • .rela.dyn段(用来修正.got段中全局变量和.data段中符号的地址);
  • .plt段(程序链接表,用于实现延迟绑定);
  • .rela.plt段(用来修正.got段中外部函数的地址);
  • .init段(程序初始化)、fini段(程序退出清理);

 

(4)解释#include<stdio.h>的作用,C语言库是什么?要如何实现?

解:包含处理标准输入输出的C语言标准库。C语言库是支撑C语言程序可以正常运行的代码集合。

 

(5)不同的编译器、硬件平台以及操作系统,编译同一源代码的结果是否相同?为什么?

解:不相同。不同的编译器在编译过程中所做的优化不同,不同的硬件平台所支持的汇编指令集不同,不同的操作系统对C语言标准库中函数的具体实现不同;因此,即使编译同一源代码,所得结果也不会相同。

 

(7)一个程序在没有操作系统的机器上如何才能运行?

解:略。

 

(8)printf函数是如何实现的?

解:要实现printf函数有两点,一个是使用write系统调用向屏幕写数据、一个是支持变长参数;在printf函数内部定义va_list类型的变量ap,先用宏函数va_start将其初始化

1
va_start(ap, lastarg);//lastarg为printf函数的最后一个具名参数

接下来,可以通过va_arg宏函数来获取下一个不定参数;

1
type next=va_arg(ap, type);//type为下一个不定参数的类型

在调用完write系统调用之后,用宏函数va_end来清理现场。

 

(9)程序在运行时,它在内存中的组成?

解:程序在运行时,它在内存中的组成包括Linux内核、堆栈、动态装载库、数据段、代码段以及保留部分。这些部分的组织如下图。