(1)
首先用vi编辑器创建一个c程序文件(以.c结尾)
如:
vi hello.c
#include <stdio.h>
int main()
{
printf(“Hello World!\n”);
return 0;
}
创建好hello.c文件后,保存退出,接下来就是进行编译程序。
gcc hello.c
此时,用ls -al命令查看当前目录下的文件,可以发现,多出了一个a.out文件,注意该文件权限的最后一列是x,即表示该文件是可执行文件,让我们执行文件看看会有什么结果!
./a.out
命令窗口出现了“Hello World”。但现在有个问题,如果我们不想生成的可执行使用默认名称,那要怎么办呢?这里可以使用-o这一选项决定生成文件的名称,我们就给执行文件命名为run吧。
gcc hello.c -o run
这次,在用ls -al查看当前目录,发现出现了run可执行文件,再次运行该文件:
./run
结果和./a.out是一样的。
(2)
有了上面的基础后,我们继续接下来的教程。这次我们修改上面的.c文件:
vi hello.c
使之内容如下,实现简单的相加功能:
#include <stdio.h>
int main()
{
double x = 1.3, y = 2.4;
printf(“sum:%d”, x + y);
return 0;
}
仔细的观察,会发现,输出格式上问题,我们要输出的是一个double型的数据,但输出格式是按照整形输出,虽说这不影响程序的运行,但却可能是一个隐藏的错误。让我们按照上一节的方法编译文件:
gcc hello.c -o run
结果,编译器没有提示任何的警告。这对于一些对代码规范要求比较严格的人来说,是一个严重的漏洞。那么,要怎样才可以看到提示警告呢。我们可以使用-Wall选项来显示警告。
gcc -Wall hello.c -o run
这次编译器就显示警告消息了。接下来,我们的大部分例子都是使用-Wall这一选项,我会在之后的内容中对-Wall的使用进一步详解。
(3)
在编程中,我们经常会调用其他文件的函数,因为不可能把所有的函数都在一个文件上实现,这样会让代码很难管理。那么如果在linux上,存在多文件关系又要怎样编译呢?下面我举一个简单例子:
首先是,add.c文件
vi add.c
int add(int x, int y){
return x + y;
}
接着就是包含该函数声明的头文件math.h
vi math.h
int add(int x, int y);
然后是调用add这一函数的main.c文件:
vi main.c
#include <stdio.h>
#include”math.h”
int main()
{
int sum = 0;
sum = add(4, 3);
printf(“sum:%d\n”, sum);
return 0;
}
这里,我简单地讲讲gcc编译器的工作流程,首先是对源程序进行预处理,然后是生成目标文件,最后是将目标文件链接在一起形成可执行文件。大概的流程是这样,想要知道更具体的细节,大家可以自己到网上去找相关的资料,这里我就不展开讲了。接下来就是生成目标文件:
gcc -Wall -c add.c
gcc -Wall -c main.c
使用ls -al查看当前目录,可以见到新增了两个目标文件,分别是add.o 和main.o。这里,我们使用-c表示只生成目标文件,不生成可执行文件。所以编译其并没有报错。接下来就是将两个目标文件链接在一起形成最终的可执行文件。
gcc -Wall main.o add.o -o result
查看当前目录,这是就出现了result这一可执行文件,让我们运行看看是否程序是否正确。
./result
没错,输出的结果就是7。这是大家就会疑问了,难道就没有简单一点的方法吗,其实上面的步骤可以用一句命令来代替:
gcc -Wall main.c add.c -o result
我之所以将上面一种比较麻烦的方法是想让大家了解一下gcc的编译流程,因为到了后面我们还会继续用到这种方法。
这时,一些细心的朋友可能会发现math.h文件没有在命令行列,那是因为#include”math.h”中编译器已经将math.h中的内容包含到main.c中了。
(4)上面的程序很简单,这次我们创建一个更复杂点的程序,让我们把math.h头文件下的函数补充完善
, 分别追加minus.c divide.c以及multiply.c其实就是把我们常用的加减乘除功能全部实现。
vi minus.c
int minus(int x, int y){
return x – y;
}
vi divide.c
int divide(int x, int y){
return x / y;
}
vi multiply.c
int multiply(int x, int y){
return x * y;
}
然后是将各个函数的声明添加之math.h文件中,
vi math.h
int add(int x, int y);
int minus(int x, int y);
int divide(int x, int y);
int multiply(int x, int y);
同时,我们修改一下main.c,让其调用divide函数:
vi main.c
#include <stdio.h>
#include”math.h”
int main()
{
int sum = 0;
sum = add(4, 3);
printf(“sum:%d\n”, sum);
int result = 0;
result = divide(4,2);
printf(“result:%d”, result);
return 0;
}
这次由于main.c中,我们只调用了add和divide函数,则编译命令如下:
gcc -Wall main.c add.c divide.c -o result
查看当前目录,result已经生成,运行一下程序:
./result
结果正常。我们继续把剩下的两个minus.c和multiply.c生成目标文件,供以后使用。这时用ls -al查看当前目录,我们就有add.o、minus.o、divide.o、multiply.o和main.o四个目标文件。
让我们回过头来看看前面的代码,细心的朋友会发现,我们的divide函数有个漏洞,如果分母y为0,那么程序就会出错,我们再次编辑divide.c文件:
vi divide.c
添加一个判断语句即可。
#include <stdio.h> //由于我们调用了printf系统自带的函数
int divide(int x, int y){
if(y == 0){
printf(“y can't be zero\n”);
return 0;
}
return x / y;
}
然后,我们修改main.c,测试一下分母为0时是否可以正常运行。
vi main.c
#include <stdio.h>
#include”math.h”
int main()
{
int sum = 0;
sum = add(4, 3);
printf(“sum:%d\n”, sum);
int result = 0;
result = divide(4,0);
printf(“result:%d”, result);
return 0;
}
这时我们就有疑问了,当前目录下的result可执行程序是修改之前的编译生成的可执行文件,那么修改之后就得重新编译一次。这里,由于我们使用的是多文件的方法,那么只需重新编译那些改动过的文件
,这里我们需要重新编译divide.c和main.c文件,前面一个大家自然理解,至于后面一个呢,那是因为main.c函数调用了divide函数,那要想使用更新后的函数,自然就的重新编译编译一边才可以享受更新服务了,这其实就是linux上常说的文件依赖。一个文件A依赖文件B,当文件B发生改动时,不仅文件B要重新编译,文件A也要重新编译一遍。这里由于其他文件没有发生改动,就无需重新编译一遍,
这样就大大的提高了编译的效率,这也是我们使用多文件管理的原因之一。
好了,说了那么多,让我们直接运行下代码看看结果先吧!
gcc -Wall main.o add.o divide.o -o result
结果显示说,分母y不能为0,看来程序正常运行了。
这次教程就到此为止。
(5)这一次教程,我们来说说关于linux下的库。库说白了其实就是一个个目标文件的集合。linux上一共有两种库文件类型,一种是以.a格式的静态链接库,另一种是以.so格式的动态链接库。下面我们来讲讲静态链接库。上一次的教程,我们生成了四个目标文件,分别add.o、minus.o、divide.o、multiply.o。这次,我们使用ar程序把这四个目标文件打包成一个静态链接库,以后要调用多个函数时,就不必一个一个的链接对应的目标文件。废话不多说,我们直接用代码来解释:
ar的使用格式:
ar cr libName.a file1.o file2.o file3.o …...
注释:libName.a中前缀lib和后缀.a固定,Name是静态链接库的名称。
ar cr libtest.a add.o、minus.o、divide.o、multiply.o
执行命令后,我们查看当前目录下的文件,就多出了一个libtest.a文件。接着,我们就直接使用libtest.a来重新编译源程序:
gcc -Wall main.c libtest.a -o result2
程序结果和之前的一样。我们也可以使用下面这种方法:
gcc -Wall main.c -L -ltest. -o result2
注意,上面的”-L.”不能缺少,因为,使用”-l“,编译器查找的是系统默认的
补充:软件开发 , C++ ,