如何使用gcc编译器
开始...
首先,我们应该知道如何调用编译器。实际上,这很简单。我们将从那个著名的第一个C程序开始。
#include <stdio.h>
int main()
{
printf("Hello World!\n");
}
把这个文件保存为lxy.c,保存到当前目录下,在命令行下编译它:
gcc game.c
在默认情况下,C编译器将生成一个名为 a.out 的可执行文件。 你可以键入如下命令运行它:
a.out
Hello World
每一次编译程序时,新的 a.out 将覆盖原来的程序。你无法知道是哪个 程序创建了a.out。我们可以通过使用 -o 编译选项:
- gcc –o lxy lxy.c //对文件进行编译连接,-o选项指定创建的可执行文件名称
- ./lxy //运行刚才生成的lxy程序
- Hello World! //程序输出
看起来上面生成可执行文件的过程中,像是一步就完成了,但是实际上它要经历4个步骤:
下一步为我们的lxy编写一个头文件。头文件把数据类型和函数声明集中到了一处。 这可以保证数据结构定义的一致性,以便程序的每一部分都能以同样的方式看待一切事情。
#ifndef DECK_H
#define DECK_H
#define DECKSIZE 52
typedef struct deck_t
{
int card[DECKSIZE];
/* number of cards used */
int dealt;
}deck_t;
#endif /* DECK_H */
把这个文件保存为 deck.h。只能编译 .c 文件, 所以我们必须修改 lxy.c。即:
#include <stdio.h>
#include "deck.h"
int main()
{
printf("Hello World!\n");
deck_t deck;
}
gcc -o lxy lxy .c
如果没有错误,就没有问题。如果编译不能通过,那么就修改它直到能通过为止。
预编译
编译器是怎么知道 deck_t 类型是什么的呢?因为在预编译期间, 它实际上把"deck.h"文件复制到了"lxy.c"文件中。源代码中的预编译指示以"#"为前缀。 你可以通过在gcc后加上 -E 选项来调用预编译器。
gcc -E -o game_precompile.txt game.c
wc -l game_precompile.txt
3199 game_precompile.txt
几乎有3200行的输出!其中大多数来自 stdio.h 包含文件,但是如果 你查看这个文件的话,我们的声明也在那里。如果你不用 -o 选项指定 输出文件名的话,它就输出到控制台。预编译过程通过完成三个主要任务给了代码很大的 灵活性。
- 把"include"的文件拷贝到要编译的源文件中。
- 用实际值替代"define"的文本。
- 在调用宏的地方进行宏替换。
这就使你能够在整个源文件中使用符号常量(即用DECKSIZE表示一付牌中的纸牌数量), 而符号常量是在一个地方定义的,如果它的值发生了变化,所有使用符号常量的地方 都能自动更新。在实践中,你几乎不需要单独使用 -E 选项,而是让它 把输出传送给编译器。
编译
作为一个中间步骤,gcc把你的代码翻译成汇编语言。它一定要这样做,它必须通过分析 你的代码搞清楚你究竟想要做什么。如果你犯了语法错误,它就会告诉你,这样编译就失败了。 人们有时会把这一步误解为整个过程。但是,实际上还有许多工作要gcc去做呢。
汇编
as 把汇编语言代码转换为目标代码。事实上目标代码并不能在CPU上运行, 但它离完成已经很近了。编译器选项 -c 把 .c 文件转换为以 .o 为扩展名 的目标文件。 如果我们运行
gcc -c lxy.c
我们就自动创建了一个名为lxy.o的文件。这里我们碰到了一个重要的问题。我们可以用任意一个 .c 文件创建一个目标文件。正如我们在下面所看到的,在连接步骤中我们可以把这些目标文件组合成可执行文件。让我们继续介绍我们的例子。因为我们正在编写一个纸牌游戏,我们已经把一付牌定义为 deck_t,我们将编写一个洗牌函数。 这个函数接受一个指向deck类型的指针,并把一付随机的牌装入deck类型。它使用'drawn' 数组跟踪记录那些牌已经用过了。这个具有DECKSIZE个元素的数组可以防止我们重复使用 一张牌。
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "deck.h"
static time_t seed = ;
void shuffle(deck_t *pdeck)
{
/* Keeps track of what numbers have been used */
int drawn[DECKSIZE] = {};
int i;
/* One time initialization of rand */
if( == seed)
{
seed = time(NULL);
srand(seed);
}
for(i = ; i < DECKSIZE; i++)
{
int value = -;
do
{
value = rand() % DECKSIZE;
}
while(drawn[value] != );
/* mark value as used */
drawn[value] = ;
/* debug statement */
printf("%i\n", value);
pdeck->card[i] = value;
}
pdeck->dealt = ;
return;
}
把这个文件保存为 shuffle.c。我们在这个代码中加入了一条调试语句, 以便运行时,能输出所产生的牌号。这并没有为我们的程序添加功能,因为我们的游戏还在初级阶段,我们没有别的办法确定我们的函数是否实现了我们要求的功能。使用那条printf语句,我们就能准确 地知道现在究竟发生了什么,以便在开始下一阶段之前我们知道牌已经洗好了。在我们 对它的工作感到满意之后,我们可以把那一行语句从代码中删掉。这种调试程序的技术 看起来很粗糙,但它使用最少的语句完成了调试任务。
请注意两个问题:
- 我们用传址方式传递参数,你可以从'&'(取地址)操作符看出来。这把变量的机器地址 传递给了函数,因此函数自己就能改变变量的值。也可以使用全局变量编写程序,但是应该 尽量少使用全局变量。指针是C的一个重要组成部分,你应该充分地理解它。
- 我们在一个新的 .c 文件中使用函数调用。操作系统总是寻找名为'main'的函数,并从 那里开始执行。 shuffle.c 中没有'main'函数,因此不能编译为独立的可执行文件。 我们必须把它与另一个具有'main'函数并调用'shuffle'的程序组合起来。
运行命令
gcc -c shuffle.c
并确定它创建了一个名为 shuffle.o 的新文件。编辑lxy.c文件,在第7行,在 deck_t类型的变量 deck 声明之后,加上下面这一行:
shuffle(&deck);
现在,如果我们还象以前一样创建可执行文件,我们就会得到一个错误
gcc -o lxy lxy.c
/tmp/ccmiHnJX.o: In function `main':
/tmp/ccmiHnJX.o(.text+0xf): undefined reference to `shuffle'
collect2: ld returned 1 exit status
编译成功了,因为我们的语法是正确的。但是连接步骤却失败了,因为 我们没有告诉编译器'shuffle'函数在哪里。 那么,到底什么是连接?我们怎样告诉编译器到哪里寻找这个函数呢?
连接
连接器ld,使用下面的命令,接受前面由 as 创建的目标文件并把它转换为可执行文件gcc -o lxy lxy.o shuffle.o这将把两个目标文件组合起来并创建可执行文件 lxy。
连接器从shuffle.o目标文件中找到 shuffle 函数,并把它包括进可执行文件。 目标文件的真正好处在于,如果我们想再次使用那个函数,我们所要做的就是包含"deck.h" 文件并把 shuffle.o 目标文件连接到新的可执行文件中。
象这样的代码重用是经常发生的。虽然我们并没有编写前面作为调试语句调用的 printf 函数,连接器却能从我们用 #include <stdlib.h> 语句包含的文件中 找到它的声明,并把存储在C库(/lib/libc.so.6)中的目标代码连接进来。 这种方式使我们可以使用已能正确工作的其他人的函数,只关心我们所要解决的问题。 这就是为什么头文件中一般只含有数据和函数声明,而没有函数体。
如何使用gcc编译器的更多相关文章
- 在CentOS 7.2下升级gcc编译器的版本
默认情况下,CentOS 7.2预装的gcc版本是4.8.x,通过执行命令 gcc -v 可以看到,一般情况下这个版本的编译器已经满足需要了,但是某些特殊的时候为了支持C++更高的特性,需要对gcc编 ...
- C语言的编译过程、安装gcc编译器以及设置环境变量
以我对C语言编译过程的了解,我用了一点时间画了一个图,提供给大家参考一下,希望有些能对您的问题提上帮助. 前几天刚初步学习了C语言的编译过程,感触挺深的.在C语言中头文件其实起了一个很大的作用. 1. ...
- 利用GCC编译器生成动态链接库和静态链接库
转载请标明:http://www.cnblogs.com/winifred-tang94/ 1.编译过程 gcc –fPIC –c xxx.c 其中-fPIC是通知gcc编译器产生位置独立的目标代码. ...
- GCC编译器编译链接
在gcc编译器环境下,常见的文件扩展名的含义如下: .c:C源程序,经过预编译后的源程序也为.c文件,它可以通过-E参数输出. .h:头文件 .s:经过编译得到的汇编程序代码,它可以通过-S参数输出. ...
- Linux安装gcc编译器详解
本人使用的是CentOS 6.5 64位系统,由于在安装系统的时候并没有勾选安装gcc编译器,因此需要自行安装gcc编译器. 使用yum安装gcc 对于配备了yum的Linux发行版而言,安装gcc编 ...
- GCC编译器使用
一.GCC简介 通常所说的GCC是GUN Compiler Collection的简称,除了编译程序之外,它还含其他相关工具,所以它能把易于人类使用的高级语言编写的源代码构建成计算机能够直接执行的二进 ...
- GCC编译器和GDB调试器常用选项
http://blog.csdn.net/u014328976/article/details/46745349 GCC编译器 gcc hello.c -o hello ...
- 临时改GCC编译器,重启后失效
临时改GCC编译器,重启后失效.例如,用如下命令: export CROSS_COMPILE= <gcc 文件所在的目录>/arm-linux-gnueabihf- 本例中使用的命令如下: ...
- windows下安装,配置gcc编译器
在Windows下使用gcc编译器: 1.首先介绍下MinGW MinGW是指仅仅用自由软件来生成纯粹的Win32可运行文件的编译环境,它是Minimalist GNU on Windows的略称. ...
- gcc编译器与基本类型3
C语言发展史 1969年贝尔实验室 肯尼斯·蓝·汤普逊,丹尼斯·李奇开发了B语言 ->Unix,New B语言,改名C语言83年提出C语言标准 1989年十二月正式通过C语言标准,C89标准 C ...
随机推荐
- CodeForce---Educational Codeforces Round 3 The best Gift 解题报告
对于这题笔者认为可以用数学排列来算,但是由于笔者很懒所以抄了一段大神的代码来交个大家了, 这位大神的基本想法就是通过记录各类书的数量,再暴力破解: 下面贴出这位大神的代码吧: #include< ...
- Hadoop安装教程_单机/伪分布式配置
环境 本教程使用 CentOS 6.4 32位 作为系统环境,请自行安装系统(可参考使用VirtualBox安装CentOS).如果用的是 Ubuntu 系统,请查看相应的 Ubuntu安装Hadoo ...
- 【转】C数据存储(包括const存储在哪,C++不同部分我在文中用红字已指出)
非原创(文中红字为自己见解,如有不对,请大神指点) 程序由指令和数据组成,C语言程序亦是如此.开发者在编写程序的时候往往需要根据不同数据的特点以及程序需求来选择不同的数据存储方式,那么在C语言中数据的 ...
- Jquery UI的datepicker插件使用方法
原文链接;http://www.ido321.com/375.html Jquery UI是一个非常丰富的Jquery插件,并且UI的各部分插件可以独自分离出来使用,这是其他很多Jquery插件没有的 ...
- C++11 并发指南------std::thread 详解
参考: https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Int ...
- Express细节探究(1)——app.use(express.static)
express相信是很多人用nodejs搭建服务器的首选框架,相关教程有很多,也教会了大家来如何使用.如果你想更深的了解他的细节,不妨和我一起来研究一下. 先来看一个每个人都用到的方法app.use( ...
- 第十三章、学习 Shell Scripts 循环 (loop)
while do done, until do done (不定循环) while [ condition ] <==中括号内的状态就是判断式 do <==do 是回圈的开始! 程序段落 ...
- .net MVC全局定时器执行作业
首先的一个需求是在OA系统中定时跑一些定时作业,例如发放年假等事务,之前的做法是在服务器上加入一个服务,用系统定时作业去跑服务,这样有个问题就是当系统在发布的过程中,有可能忘记启动服务而导致无法定时执 ...
- Stage3D学习笔记(六):旋转动画效果
我们这节在上一篇代码的基础上再进一步,让显示的纹理进行旋转. 实现旋转,只需要每帧修改_modelViewMatrix的旋转角度即可,我们需要一个变量来记录旋转: //旋转度数 private var ...
- Java的面向对象思想
多态性: 一种方法有多种实现,采用哪一种实现由Java虚拟机在运行时动态决定,这种能力成为动态绑定(dynamic binding),也称为多态性(polymorphism)(源于一个希腊单词,意为“ ...