本文以一个C语言版的hello world例子阐述编译系统四个阶段的工作内容。源程序hello.c如下:
代码语言:javascript复制#include <stdio.h>
int main() {
printf("hello world!n");
}
作为一个精通各种语言的hello world的程序员,我相信你一定看得懂上面这段C代码。总所周知,像C语言这类的编译语言,都是将人类可读的源代码“编译”成机器能识别的“机器代码”,然后方能执行的。而我们通常所说的“编译”,实际上是指的是编译系统,一共包含4个阶段。即:预处理,编译,汇编,链接。而正是这四个阶段所需要的预处理器、编译器、汇编器、链接器构成了编译系统(compilation system)。下图是hello.c经过“编译”成为可执行的目标程序的过程示意图,接下来,将围绕此图阐述各个阶段的工作内容。
1. 预处理阶段
预处理器(cpp)根据以字符#号开头的命令,修改原始的c程序。比如hello.c中的第一行#include <stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入到程序文本中,结果得到了另一个C程序,通常是以.i为扩展名。在Linux下我们用GCC命令:
代码语言:javascript复制gcc -E hello.c -o hello.i
得到一个hello.i文件,然后查看文件内容如下:
代码语言:javascript复制 1 # 1 "hello.c"
2 # 1 "<built-in>"
3 # 1 "<command-line>"
4 # 1 "/usr/include/stdc-predef.h" 1 3 4
5 # 1 "<command-line>" 2
6 # 1 "hello.c"
7 # 1 "/usr/include/stdio.h" 1 3 4
8 # 27 "/usr/include/stdio.h" 3 4
9 # 1 "/usr/include/features.h" 1 3 4
10 # 375 "/usr/include/features.h" 3 4
11 # 1 "/usr/include/sys/cdefs.h" 1 3 4
12 # 392 "/usr/include/sys/cdefs.h" 3 4
13 # 1 "/usr/include/bits/wordsize.h" 1 3 4
14 # 393 "/usr/include/sys/cdefs.h" 2 3 4
15 # 376 "/usr/include/features.h" 2 3 4
16 # 399 "/usr/include/features.h" 3 4
17 # 1 "/usr/include/gnu/stubs.h" 1 3 4
18 # 10 "/usr/include/gnu/stubs.h" 3 4
19 # 1 "/usr/include/gnu/stubs-64.h" 1 3 4
20 # 11 "/usr/include/gnu/stubs.h" 2 3 4
21 # 400 "/usr/include/features.h" 2 3 4
22 # 28 "/usr/include/stdio.h" 2 3 4
23
24
25
26
27
28 # 1 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 1 3 4
29 # 212 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 3 4
30 typedef long unsigned int size_t;
31 # 34 "/usr/include/stdio.h" 2 3 4
32
33 # 1 "/usr/include/bits/types.h" 1 3 4
34 # 27 "/usr/include/bits/types.h" 3 4
35 # 1 "/usr/include/bits/wordsize.h" 1 3 4
36 # 28 "/usr/include/bits/types.h" 2 3 4
37
38
39 typedef unsigned char __u_char;
40 typedef unsigned short int __u_short;
41 typedef unsigned int __u_int;
42 typedef unsigned long int __u_long;
43
44
45 typedef signed char __int8_t;
46 typedef unsigned char __uint8_t;
47 typedef signed short int __int16_t;
48 typedef unsigned short int __uint16_t;
49 typedef signed int __int32_t;
50 typedef unsigned int __uint32_t;
51
52 typedef signed long int __int64_t;
53 typedef unsigned long int __uint64_t;
54
55
56
57
58
59
60
61 typedef long int __quad_t;
62 typedef unsigned long int __u_quad_t;
63 # 130 "/usr/include/bits/types.h" 3 4
64 # 1 "/usr/include/bits/typesizes.h" 1 3 4
65 # 131 "/usr/include/bits/types.h" 2 3 4
66
67
68 typedef unsigned long int __dev_t;
69 typedef unsigned int __uid_t;
70 typedef unsigned int __gid_t;
71 typedef unsigned long int __ino_t;
72 typedef unsigned long int __ino64_t;
73 typedef unsigned int __mode_t;
74 typedef unsigned long int __nlink_t;
75 typedef long int __off_t;
76 typedef long int __off64_t;
77 typedef int __pid_t;
78 typedef struct { int __val[2]; } __fsid_t;
79 typedef long int __clock_t;
80 typedef unsigned long int __rlim_t;
81 typedef unsigned long int __rlim64_t;
82 typedef unsigned int __id_t;
83 typedef long int __time_t;
84 typedef unsigned int __useconds_t;
85 typedef long int __suseconds_t;
86
87 typedef int __daddr_t;
88 typedef int __key_t;
89
90
91 typedef int __clockid_t;
92
93
94 typedef void * __timer_t;
95
96
97 typedef long int __blksize_t;
98
99
100
101
102 typedef long int __blkcnt_t;
103 typedef long int __blkcnt64_t;
104
105
106 typedef unsigned long int __fsblkcnt_t;
107 typedef unsigned long int __fsblkcnt64_t;
108
109
110 typedef unsigned long int __fsfilcnt_t;
111 typedef unsigned long int __fsfilcnt64_t;
112
113
114 typedef long int __fsword_t;
115
116 typedef long int __ssize_t;
117
118
119 typedef long int __syscall_slong_t;
120
121 typedef unsigned long int __syscall_ulong_t;
122
123
124
125 typedef __off64_t __loff_t;
126 typedef __quad_t *__qaddr_t;
127 typedef char *__caddr_t;
128
129
130 typedef long int __intptr_t;
131
132
133 typedef unsigned int __socklen_t;
134 # 36 "/usr/include/stdio.h" 2 3 4
135 # 44 "/usr/include/stdio.h" 3 4
136 struct _IO_FILE;
137
138
139
140 typedef struct _IO_FILE FILE;
141
142
143
144
145
146 # 64 "/usr/include/stdio.h" 3 4
147 typedef struct _IO_FILE __FILE;
148 # 74 "/usr/include/stdio.h" 3 4
149 # 1 "/usr/include/libio.h" 1 3 4
150 # 32 "/usr/include/libio.h" 3 4
151 # 1 "/usr/include/_G_config.h" 1 3 4
152 # 15 "/usr/include/_G_config.h" 3 4
153 # 1 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 1 3 4
154 # 16 "/usr/include/_G_config.h" 2 3 4
155
156
157
158
159 # 1 "/usr/include/wchar.h" 1 3 4
160 # 82 "/usr/include/wchar.h" 3 4
161 typedef struct
162 {
163 int __count;
164 union
165 {
166
167 unsigned int __wch;
168
169
170
171 char __wchb[4];
172 } __value;
173 } __mbstate_t;
174 # 21 "/usr/include/_G_config.h" 2 3 4
175 typedef struct
176 {
177 __off_t __pos;
178 __mbstate_t __state;
179 } _G_fpos_t;
180 typedef struct
181 {
182 __off64_t __pos;
183 __mbstate_t __state;
184 } _G_fpos64_t;
185 # 33 "/usr/include/libio.h" 2 3 4
186 # 50 "/usr/include/libio.h" 3 4
187 # 1 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stdarg.h" 1 3 4
188 # 40 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stdarg.h" 3 4
189 typedef __builtin_va_list __gnuc_va_list;
190 # 51 "/usr/include/libio.h" 2 3 4
191 # 145 "/usr/include/libio.h" 3 4
192 struct _IO_jump_t; struct _IO_FILE;
193 # 155 "/usr/include/libio.h" 3 4
194 typedef void _IO_lock_t;
195
196
197
198
199
200 struct _IO_marker {
201 struct _IO_marker *_next;
202 struct _IO_FILE *_sbuf;
203
204
205
206 int _pos;
207 # 178 "/usr/include/libio.h" 3 4
208 };
209
210
211 enum __codecvt_result
212 {
213 __codecvt_ok,
214 __codecvt_partial,
215 __codecvt_error,
216 __codecvt_noconv
217 };
218 # 246 "/usr/include/libio.h" 3 4
219 struct _IO_FILE {
220 int _flags;
221
222
223
224
225 char* _IO_read_ptr;
226 char* _IO_read_end;
227 char* _IO_read_base;
228 char* _IO_write_base;
229 char* _IO_write_ptr;
230 char* _IO_write_end;
231 char* _IO_buf_base;
232 char* _IO_buf_end;
233
234 char *_IO_save_base;
235 char *_IO_backup_base;
236 char *_IO_save_end;
237
238 struct _IO_marker *_markers;
239
240 struct _IO_FILE *_chain;
241
242 int _fileno;
243
244
245
246 int _flags2;
247
248 __off_t _old_offset;
249
250
251
252 unsigned short _cur_column;
253 signed char _vtable_offset;
254 char _shortbuf[1];
255
256
257
258 _IO_lock_t *_lock;
259 # 294 "/usr/include/libio.h" 3 4
260 __off64_t _offset;
261 # 303 "/usr/include/libio.h" 3 4
262 void *__pad1;
263 void *__pad2;
264 void *__pad3;
265 void *__pad4;
266 size_t __pad5;
267
268 int _mode;
269
270 char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
271
272 };
273
274
275 typedef struct _IO_FILE _IO_FILE;
276
277
278 struct _IO_FILE_plus;
279
280 extern struct _IO_FILE_plus _IO_2_1_stdin_;
281 extern struct _IO_FILE_plus _IO_2_1_stdout_;
282 extern struct _IO_FILE_plus _IO_2_1_stderr_;
283 # 339 "/usr/include/libio.h" 3 4
284 typedef __ssize_t __io_read_fn (void *__cookie, char *__buf, size_t __nbytes);
285
286
287
288
289
290
291
292 typedef __ssize_t __io_write_fn (void *__cookie, const char *__buf,
293 size_t __n);
294
295
296
297
298
299
300
301 typedef int __io_seek_fn (void *__cookie, __off64_t *__pos, int __w);
302
303
304 typedef int __io_close_fn (void *__cookie);
305 # 391 "/usr/include/libio.h" 3 4
306 extern int __underflow (_IO_FILE *);
307 extern int __uflow (_IO_FILE *);
308 extern int __overflow (_IO_FILE *, int);
309 # 435 "/usr/include/libio.h" 3 4
310 extern int _IO_getc (_IO_FILE *__fp);
311 extern int _IO_putc (int __c, _IO_FILE *__fp);
312 extern int _IO_feof (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__));
313 extern int _IO_ferror (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__));
314
315 extern int _IO_peekc_locked (_IO_FILE *__fp);
316
317
318
319
320
321 extern void _IO_flockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__));
322 extern void _IO_funlockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__));
323 extern int _IO_ftrylockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__));
324 # 465 "/usr/include/libio.h" 3 4
325 extern int _IO_vfscanf (_IO_FILE * __restrict, const char * __restrict,
326 __gnuc_va_list, int *__restrict);
327 extern int _IO_vfprintf (_IO_FILE *__restrict, const char *__restrict,
328 __gnuc_va_list);
329 extern __ssize_t _IO_padn (_IO_FILE *, int, __ssize_t);
330 extern size_t _IO_sgetn (_IO_FILE *, void *, size_t);
331
332 extern __off64_t _IO_seekoff (_IO_FILE *, __off64_t, int, int);
333 extern __off64_t _IO_seekpos (_IO_FILE *, __off64_t, int);
334
335 extern void _IO_free_backup_area (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__));
336 # 75 "/usr/include/stdio.h" 2 3 4
337
338
339
340
341 typedef __gnuc_va_list va_list;
342 # 90 "/usr/include/stdio.h" 3 4
343 typedef __off_t off_t;
344 # 102 "/usr/include/stdio.h" 3 4
345 typedef __ssize_t ssize_t;
346
347
348
349
350
351
352
353 typedef _G_fpos_t fpos_t;
354
355
356
357
358 # 164 "/usr/include/stdio.h" 3 4
359 # 1 "/usr/include/bits/stdio_lim.h" 1 3 4
360 # 165 "/usr/include/stdio.h" 2 3 4
361
362
363
364 extern struct _IO_FILE *stdin;
365 extern struct _IO_FILE *stdout;
366 extern struct _IO_FILE *stderr;
367
368
369
370
371
372
373
374 extern int remove (const char *__filename) __attribute__ ((__nothrow__ , __leaf__));
375
376 extern int rename (const char *__old, const char *__new) __attribute__ ((__nothrow__ , __leaf__));
377
378
379
380
381 extern int renameat (int __oldfd, const char *__old, int __newfd,
382 const char *__new) __attribute__ ((__nothrow__ , __leaf__));
383
384
385
386
387
388
389
390
391 extern FILE *tmpfile (void) ;
392 # 209 "/usr/include/stdio.h" 3 4
393 extern char *tmpnam (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ;
394
395
396
397
398
399 extern char *tmpnam_r (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ;
400 # 227 "/usr/include/stdio.h" 3 4
401 extern char *tempnam (const char *__dir, const char *__pfx)
402 __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ;
403
404
405
406
407
408
409
410
411 extern int fclose (FILE *__stream);
412
413
414
415
416 extern int fflush (FILE *__stream);
417
418 # 252 "/usr/include/stdio.h" 3 4
419 extern int fflush_unlocked (FILE *__stream);
420 # 266 "/usr/include/stdio.h" 3 4
421
422
423
424
425
426
427 extern FILE *fopen (const char *__restrict __filename,
428 const char *__restrict __modes) ;
429
430
431
432
433 extern FILE *freopen (const char *__restrict __filename,
434 const char *__restrict __modes,
435 FILE *__restrict __stream) ;
436 # 295 "/usr/include/stdio.h" 3 4
437
438 # 306 "/usr/include/stdio.h" 3 4
439 extern FILE *fdopen (int __fd, const char *__modes) __attribute__ ((__nothrow__ , __leaf__)) ;
440 # 319 "/usr/include/stdio.h" 3 4
441 extern FILE *fmemopen (void *__s, size_t __len, const char *__modes)
442 __attribute__ ((__nothrow__ , __leaf__)) ;
443
444
445
446
447 extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __attribute__ ((__nothrow__ , __leaf__)) ;
448
449
450
451
452
453
454 extern void setbuf (FILE *__restrict __stream, char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__));
455
456
457
458 extern int setvbuf (FILE *__restrict __stream, char *__restrict __buf,
459 int __modes, size_t __n) __attribute__ ((__nothrow__ , __leaf__));
460
461
462
463
464
465 extern void setbuffer (FILE *__restrict __stream, char *__restrict __buf,
466 size_t __size) __attribute__ ((__nothrow__ , __leaf__));
467
468
469 extern void setlinebuf (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
470
471
472
473
474
475
476
477
478 extern int fprintf (FILE *__restrict __stream,
479 const char *__restrict __format, ...);
480
481
482
483
484 extern int printf (const char *__restrict __format, ...);
485
486 extern int sprintf (char *__restrict __s,
487 const char *__restrict __format, ...) __attribute__ ((__nothrow__));
488
489
490
491
492
493 extern int vfprintf (FILE *__restrict __s, const char *__restrict __format,
494 __gnuc_va_list __arg);
495
496
497
498
499 extern int vprintf (const char *__restrict __format, __gnuc_va_list __arg);
500
501 extern int vsprintf (char *__restrict __s, const char *__restrict __format,
502 __gnuc_va_list __arg) __attribute__ ((__nothrow__));
503
504
505
506
507
508 extern int snprintf (char *__restrict __s, size_t __maxlen,
509 const char *__restrict __format, ...)
510 __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 4)));
511
512 extern int vsnprintf (char *__restrict __s, size_t __maxlen,
513 const char *__restrict __format, __gnuc_va_list __arg)
514 __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 0)));
515
516 # 412 "/usr/include/stdio.h" 3 4
517 extern int vdprintf (int __fd, const char *__restrict __fmt,
518 __gnuc_va_list __arg)
519 __attribute__ ((__format__ (__printf__, 2, 0)));
520 extern int dprintf (int __fd, const char *__restrict __fmt, ...)
521 __attribute__ ((__format__ (__printf__, 2, 3)));
522
523
524
525
526
527
528
529
530 extern int fscanf (FILE *__restrict __stream,
531 const char *__restrict __format, ...) ;
532
533
534
535
536 extern int scanf (const char *__restrict __format, ...) ;
537
538 extern int sscanf (const char *__restrict __s,
539 const char *__restrict __format, ...) __attribute__ ((__nothrow__ , __leaf__));
540 # 443 "/usr/include/stdio.h" 3 4
541 extern int fscanf (FILE *__restrict __stream, const char *__restrict __format, ...) __asm__ ("" "__isoc99_fscanf")
542
543 ;
544 extern int scanf (const char *__restrict __format, ...) __asm__ ("" "__isoc99_scanf")
545 ;
546 extern int sscanf (const char *__restrict __s, const char *__restrict __format, ...) __asm__ ("" "__isoc99_sscanf") __attribute__ ((__nothrow__ , __leaf__))
547
548 ;
549 # 463 "/usr/include/stdio.h" 3 4
550
551
552
553
554
555
556
557
558 extern int vfscanf (FILE *__restrict __s, const char *__restrict __format,
559 __gnuc_va_list __arg)
560 __attribute__ ((__format__ (__scanf__, 2, 0))) ;
561
562
563
564
565
566 extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg)
567 __attribute__ ((__format__ (__scanf__, 1, 0))) ;
568
569
570 extern int vsscanf (const char *__restrict __s,
571 const char *__restrict __format, __gnuc_va_list __arg)
572 __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__format__ (__scanf__, 2, 0)));
573 # 494 "/usr/include/stdio.h" 3 4
574 extern int vfscanf (FILE *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vfscanf")
575
576
577
578 __attribute__ ((__format__ (__scanf__, 2, 0))) ;
579 extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vscanf")
580
581 __attribute__ ((__format__ (__scanf__, 1, 0))) ;
582 extern int vsscanf (const char *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vsscanf") __attribute__ ((__nothrow__ , __leaf__))
583
584
585
586 __attribute__ ((__format__ (__scanf__, 2, 0)));
587 # 522 "/usr/include/stdio.h" 3 4
588
589
590
591
592
593
594
595
596
597 extern int fgetc (FILE *__stream);
598 extern int getc (FILE *__stream);
599
600
601
602
603
604 extern int getchar (void);
605
606 # 550 "/usr/include/stdio.h" 3 4
607 extern int getc_unlocked (FILE *__stream);
608 extern int getchar_unlocked (void);
609 # 561 "/usr/include/stdio.h" 3 4
610 extern int fgetc_unlocked (FILE *__stream);
611
612
613
614
615
616
617
618
619
620
621
622 extern int fputc (int __c, FILE *__stream);
623 extern int putc (int __c, FILE *__stream);
624
625
626
627
628
629 extern int putchar (int __c);
630
631 # 594 "/usr/include/stdio.h" 3 4
632 extern int fputc_unlocked (int __c, FILE *__stream);
633
634
635
636
637
638
639
640 extern int putc_unlocked (int __c, FILE *__stream);
641 extern int putchar_unlocked (int __c);
642
643
644
645
646
647
648 extern int getw (FILE *__stream);
649
650
651 extern int putw (int __w, FILE *__stream);
652
653
654
655
656
657
658
659
660 extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
661 ;
662 # 638 "/usr/include/stdio.h" 3 4
663 extern char *gets (char *__s) __attribute__ ((__deprecated__));
664
665
666 # 665 "/usr/include/stdio.h" 3 4
667 extern __ssize_t __getdelim (char **__restrict __lineptr,
668 size_t *__restrict __n, int __delimiter,
669 FILE *__restrict __stream) ;
670 extern __ssize_t getdelim (char **__restrict __lineptr,
671 size_t *__restrict __n, int __delimiter,
672 FILE *__restrict __stream) ;
673
674
675
676
677
678
679
680 extern __ssize_t getline (char **__restrict __lineptr,
681 size_t *__restrict __n,
682 FILE *__restrict __stream) ;
683
684
685
686
687
688
689
690
691 extern int fputs (const char *__restrict __s, FILE *__restrict __stream);
692
693
694
695
696
697 extern int puts (const char *__s);
698
699
700
701
702
703
704 extern int ungetc (int __c, FILE *__stream);
705
706
707
708
709
710
711 extern size_t fread (void *__restrict __ptr, size_t __size,
712 size_t __n, FILE *__restrict __stream) ;
713
714
715
716
717 extern size_t fwrite (const void *__restrict __ptr, size_t __size,
718 size_t __n, FILE *__restrict __s);
719
720 # 737 "/usr/include/stdio.h" 3 4
721 extern size_t fread_unlocked (void *__restrict __ptr, size_t __size,
722 size_t __n, FILE *__restrict __stream) ;
723 extern size_t fwrite_unlocked (const void *__restrict __ptr, size_t __size,
724 size_t __n, FILE *__restrict __stream);
725
726
727
728
729
730
731
732
733 extern int fseek (FILE *__stream, long int __off, int __whence);
734
735
736
737
738 extern long int ftell (FILE *__stream) ;
739
740
741
742
743 extern void rewind (FILE *__stream);
744
745 # 773 "/usr/include/stdio.h" 3 4
746 extern int fseeko (FILE *__stream, __off_t __off, int __whence);
747
748
749
750
751 extern __off_t ftello (FILE *__stream) ;
752 # 792 "/usr/include/stdio.h" 3 4
753
754
755
756
757
758
759 extern int fgetpos (FILE *__restrict __stream, fpos_t *__restrict __pos);
760
761
762
763
764 extern int fsetpos (FILE *__stream, const fpos_t *__pos);
765 # 815 "/usr/include/stdio.h" 3 4
766
767 # 824 "/usr/include/stdio.h" 3 4
768
769
770 extern void clearerr (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
771
772 extern int feof (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
773
774 extern int ferror (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
775
776
777
778
779 extern void clearerr_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
780 extern int feof_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
781 extern int ferror_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
782
783
784
785
786
787
788
789
790 extern void perror (const char *__s);
791
792
793
794
795
796
797 # 1 "/usr/include/bits/sys_errlist.h" 1 3 4
798 # 26 "/usr/include/bits/sys_errlist.h" 3 4
799 extern int sys_nerr;
800 extern const char *const sys_errlist[];
801 # 854 "/usr/include/stdio.h" 2 3 4
802
803
804
805
806 extern int fileno (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
807
808
809
810
811 extern int fileno_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
812 # 873 "/usr/include/stdio.h" 3 4
813 extern FILE *popen (const char *__command, const char *__modes) ;
814
815
816
817
818
819 extern int pclose (FILE *__stream);
820
821
822
823
824
825 extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
826 # 913 "/usr/include/stdio.h" 3 4
827 extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
828
829
830
831 extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
832
833
834 extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
835 # 943 "/usr/include/stdio.h" 3 4
836
837 # 2 "hello.c" 2
838
839 int main() {
840 printf("hello world!n");
841 }
该程序依然是C语言程序,只不过多了头文件stdio.h的内容。
2. 编译阶段
编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。汇编语言中每条语句都以一种标准的文本格式确切地描述了一条低级机器语言指令。其实汇编语言是非常有用的,它为所有的高级语言提供了一种通用的输出语言。比如C编译器和Fortran编译器产生的输出文件用的都是一样的汇编语言。在Linux下,我们用命令:
代码语言:javascript复制gcc -S hello.i -o hello.s
得到一个hello.s汇编程序,内容如下:
代码语言:javascript复制 .file "hello.c"
.section .rodata
.LC0:
.string "hello world!"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, �i
call puts
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-11)"
.section .note.GNU-stack,"",@progbits
3. 汇编阶段
汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可定位目标程序的格式,并将结果保存在目标文件hello.o中,hello.o是一个二进制文件,它的字节编码是“机器语言指令”而不是“字符”,所以,我们用文本编辑器打开hello.o文件看到是回事一堆乱码。使用gcc命令:
代码语言:javascript复制gcc -c hello.s -o hello.o
将得到hello.o文件,用vim打开看一下是如下乱码:
4. 链接阶段
我们注意到,hello.c中有一个printf函数,它是每个C编译器都会提供的标准库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好的目标文件中,而这个文件必须以某种方式合并到我们的hello.o程序中。链接器(ld)就是负责处理这种合并。最后得到hello文件,一个可执行目标文件(可执行文件),可被加载到内存中,由系统执行。使用命令:
代码语言:javascript复制gcc hello.o -o hello
得到hello文件,内容如下:
自此,编译系统的整个过程大致如此。总结一下,从源程序到目标文件(可执行文件)的转化是通过编译系统完成的,编译系统包含四个阶段:预处理,编译,汇编,链接。一般的编译驱动程序如GCC都实现了编译系统的所有功能,我们用编译驱动程序直接就可以实现源程序到目标文件的转化。