[译] C track: compiling C programs.
原文:C track: compiling C programs.
C track: compiling C programs.
尽管有些计算机语言(如 Schema 或者 Basic)通常使用交互式的解释器(当你输入命令后,就可立即执行),但 C 语言不是。C 的源文件总是要通过一个叫做编译器(compiler)的程序编译成二进制代码然后运行。这就是我们接下来要详细说明的几个步骤。
几种不同类型的文件
你需要4种文件进行编译C 程序:
常规的源代码文件(source code)。 这些文件包含了函数定义,并约定以 "
.c
" 作为结尾进行命名。头文件(Header). 这些文件包含了函数声明(也叫做函数原型)以及各种预处理语句。源文件可以通过头文件访问外部定义的函数。头文件的文件名约定以 "
.h
" 作为结尾.目标文件(Object). 这些文件由编译器的输出而产生。目标文件包含了二进制形式的函数定义,本身是不可执行文件。目标文件的文件名约定以"
.o
" 结尾,尽管在一些操作系统,如(Windows, MS-DOS),经常以".obj
" 结尾。二进制可执行文件(Binary executables)。这些文件由一个叫做链接器(linker)的程序的输出而产生。链接器链接一些目标文件并产生可以直接执行的二进制文件。二进制可执行文件在 Unix 操作系统上没有后缀名,但在 Windows 上,通常以"
.exe
" 作为后缀名。
还有其他的各种文件,尤其是静态库文件(".a
" files or ".lib" on Windows)以及共享库文件(".so
" files or ".dll" on Windows)。但通常,你不需要直接与他们打交道。
预处理
在编译器开始编译源文件之前,源文件由预处理器(preprocessor)进行处理。预处理器是一个真实的单独的程序(通常叫做"cpp
", for "C preprocessor"),而由编译器在编译前自动调用。预处理器的工作就是讲源文件转换成另外一个源文件(你也可以认为是对源文件的修改或者扩展)。修改后的文件可能作为一个真实的文件存在文件系统中,也可能仅仅是在发送给编译器之前在内存中作短暂的保留。另外,你不需要特别关注预处理,但是你需要知道预处理是干什么滴。
预处理指令以符号("#
")开始. 在多种预处理指令中,有两种最为重要:
#define
. 主要用于定义常量。如,#define BIGNUM 1000000
指定在剩下的程序中任何位置处理的字符串 BIGNUM 应该被替换为 1000000。例如,这个语句:
int a = BIGNUM;
变成了
int a = 1000000;
#define
语句用于避免一个常量值在源文件中多处重复出现。这在你随后需要对这常量值进行修改时是相当的重要,并且可以减少bug 的滋生,你只需要对 #define 的定义修改,而不是对常量值在整个源代码中多处的出现位置进行修改。#include
. 用于访问位于源文件之外的函数定义。例如:#include <stdio.h>
在源代码编译之前,预处理器将<stdio.h> 的内容替换
#include
语句所在的位置。#include
总是用于主要包含函数声明和#define 语句的头文件。 这时,我们可以通过#include
语句而使用一些函数,如printf
和scanf
, 这两个函数的声明就位于文件stdio.h 中
. 在源文件中,在函数声明或者定义之前,C compilers 是不允许我们使用函数的;#include
语句就是用于这种情况,从而使我们可以复用之前用C 编写的代码。
还有其他的各种预处理指令,我们将会根据需要进行有所处理。
生成目标文件: 编译器
在 C 预处理器包含了所有的头文件并且展开所有的 #define
和 #include
语句(也有其他一些在源文件中出现的预处理指令)后,编译器就可以编译程序了。编译器将 C 源文件编译成目标文件(object code),包含二进制版本源代码并以 ".o" 结尾的文件。 然而,目标文件并不能直接运行。为了能够生成可执行文件,你还需要加入被#include
d 包含的库函数代码(这个通过 #include 包含函数声明是不一样的)。这就是下一节要讲到的链接器 linker 的工作。
通常,编译由以下方式被调用:
% gcc -c foo.c
符号 %
是 unix 提示符. 它告诉编译器对文件 foo.c
运行预处理程序并编译成目标文件 foo.o。
-c
选项意思是由编译器将源文件编译成目标文件而不会调用链接器。如果你的整个程序就一个源文件,你也可以这么做:
% gcc foo.c -o foo
它告诉编译器在文件 foo.c 运行预处理器,编译并链接产生一个可执行文件
foo。
-o
表示二进制可执行文件(程序)将以其随后的单词作为文件名。 如果你不制定 -o 选项,或者仅仅是输入
gcc foo.c,由于某种历史原因,可执行文件将以
a.out
命名。
请注意编译器的名字,我们使用的是 gcc,
代表 "GNU C compiler" 或者 "GNU compiler collection" 。也有其他的编译器;他们中大多数都以 cc("C compiler")命名。在
Linux 操作系统中 cc 是
gcc 的别名。
揉成一团: 链接器
链接器的工作就是将一组目标文件(.o 文件)一起链接到一个二进制可执行文件。这包括从你的源代码文件编译的目标文件,以及预编译的库文件(library files)。 这些文件 .a
或者 .so 作为结尾命名,通常你不需要知道他们,因为他们中大多数可以由链接器(
linker)定位并根据需要自动链接。
像预处理器一样,链接器也是一个叫做 ld 独立的程序。也如预处理器一样,链接器在你使用编译器时自动被调用。链接器通常使用的方式如下:
% gcc foo.o bar.o baz.o -o myprog
这一行是告诉编译器一起将三个目标文件(foo.o
, bar.o
, and baz.o)
链接成一个名为 myprog 的二进制可执行文件
.
这就是你需要知道如何编译你的 C 程序的事情。通常,我们也推荐 -Wall
选项:
% gcc -Wall -c foo.cc
-Wall
选项让编译器对合法但是可以的代码结构发出警告,并且帮助你轻松捕获一些 bugs。如果你想要更多的编译检查项:
% gcc -Wall -Wstrict-prototypes -ansi -pedantic -c foo.cc
-Wstrict-prototypes
选项是让编译器对代码中没有正确原型的函数做出警告。-ansi
和 -pedantic
是让编译器对代码中不可移植的结构(e.g. 一些在 gcc 中合法而不满足标准 C compilers 的代码结构;这些结构通常是需要避免的)做出警告。
References
Kernighan and Ritchie, The C Programming Language, 2nd Ed.
The man page for
gcc
. Type:man gcc
at the unix prompt.The GNU Info documentation on
gcc
. Warning! This is far more information than most people could possibly absorb in the average millenium.Info documentation on
gcc
can be accessed through the GNU emacs editor by typing "M-x info" (where "M-x" means to hit the meta-key and "x" simultaneously), or "C-h i" (where "C-h" means to hit the control key and "i" simultaneously), followed by "mgcc<return>". Type "minfo<return>" instead for a quick tour of how to use info. You can also access the info documentation from the unix command line by typinginfo gcc
.
[译] C track: compiling C programs.的更多相关文章
- Linux内核Makefile文件(翻译自内核手册)
--译自Linux3.9.5 Kernel Makefiles(内核目录documention/kbuild/makefiles.txt) kbuild(kernel build) 内核编译器 Thi ...
- Beginning Scala study note(1) Geting Started with Scala
1. Scala is a contraction of "scalable" and "language". It's a fusion of objecte ...
- linux内核的makefile.txt讲解
linux内核的linux-3.6.5\Documentation\kbuild\makefiles.txt Linux Kernel Makefiles This document describe ...
- CUDA1-hello world
电脑配置:windows7 sp1 64bit + CUDA6.5 + GeForce GTX780 Ti 显卡中的GPU因为多核可以处理很多相同的操作,相比较来说cpu就像个健全的手,什么活都能干 ...
- 安装coreseek找不到mysql
1.安装 coreseek-3.2.14 遇到问题:“ERROR: cannot find MySQL include files,随即在网上搜索各种答案说是要找到mysql.h的正确路径加入./co ...
- OpenMP for Fortran
OpenMP for Fortran OpenMP Directive Syntax of OpenMP compiler directive for Fortran: !$OMP Directive ...
- mysql 升级方法
Performing an In-place Upgrade This section describes how to perform an in-place upgrade. Review Bef ...
- 《 UNIX网络编程》源码的使用
学习编程这东西,看代码,改代码,运行代码这样才能学到实际东西!本书说在www.unpbook.com可以获取源码,不过打不开!所以google unpv13e.tar.gz 并在网络上找到了:源码:h ...
- 嵌入式Linux-GNU Make 使用手册(中译版)
GNU Make 使用手册(中译版) 翻译:于凤昌 译者注:本人在阅读Linux源代码过程中发现如果要全面了解Linux的结构.理解Linux的编程总体设计及思想必须首先全部读通Linux源代码中各级 ...
随机推荐
- Python入门笔记(11):集合
一.目录 1.集合概述 2.关于集合的操作符.关系符号 3.集合的一系列操作(添加.更新.访问.删除) 4.关于集合的内建函数.内建方法 5.小结 二.集合概述 集合(set):把不同的元素组成一起形 ...
- jQuery实现隐藏标签
要求:用户进入该页面时,品牌列表默认是精简显示,用户可以单击商品列表下方的“显示全部品牌”按钮来显示全部的品牌. <%@ Page Language="C#" Inherit ...
- c#调用Aspose.Word组件操作word 插入文字/图片/表格 书签替换套打
由于NPOI暂时没找到书签内容替换功能,所以换用Apose.Word组件. using System; using System.Collections.Generic; using System.C ...
- asp.net.mvc4在vs2010怎样创建mvc项目及它的结构
1.打开vs2012,创建mvc项目 文件-->新建--> 项目--> web--> asp.net.Mvc 4web应用程序-->基本模板
- 变废为宝,将Discuz废弃的cache机制引入到memory体系中
Discuz的source/class/cache目录,代表着相应的缓存机制,但实际上废弃很多年了. Discuz用Memory代表了缓存,里面内置了memcache等多种缓存驱动. 但很多人的开发环 ...
- Java中Scanner类和BufferReader类之间的区别
java.util.Scanner类是一个简单的文本扫描类,它可以解析基本数据类型和字符串.它本质上是使用正则表达式去读取不同的数据类型. Java.io.BufferedReader类为了能够高效的 ...
- lambda 个人学习理解
lambda是简化代码量的写用更简单的方法来写匿名方法 lambda左边是参数,右边是代码块(方法执行语句). 整体运算结果是根据左边参数,执行右边语句,返回右边执行的结果: 匿名方法是简化方法 1. ...
- Virtual Environments for mac
A Virtual Environment is a tool to keep the dependencies required by different projects in separate ...
- mybatis hellworld
用maven来进行搭建项目的~~ 1. 搭建环境 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" x ...
- JavaScript焦点轮播图
在慕课学习了JavaScript焦点轮播图特效,在此做一个整理. 首先是html结构,我用的是本地同文件夹下的三张图片,多出来的第一张(pic3副本)和最后一张图片(pic1副本)是为了实现无缝切换效 ...