二. 编译C程序
1. 编译单个C程序
C语言程序示例 : 简单的Hello World;
/************************************************************************* > File Name: main.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年04月19日 星期六 16时22分26秒 ************************************************************************/ #include<stdio.h> int main(int argc, char **argv) { printf("Hello World! \n"); return 0; }
简单编译 : 使用 gcc main.c 命令, 会生成 a.out 可执行文件, 使用 ./a.out 可以执行编译好的C程序;
octopus@octopus:~/gcc$ gcc main.c octopus@octopus:~/gcc$ ./a.out Hello World!
指定输出文件编译 : 如果不想使用 a.out 作为输出文件, 可以使用 -o 参数指定输出文件, 如果该文件存在就会覆盖;
-- 命令 : gcc main.c -o main;
octopus@octopus:~/gcc$ gcc main.c -o main octopus@octopus:~/gcc$ ./main Hello World!
显示警告选项 : -Wall 选项, 可以在编译的时候, 将警告信息输出到终端中;
-- 编译输出警告信息 : gcc -Wall main.c;
人为制造警告 : 在 printf 输出的时候, 使用 %s 作为一个 int 数据的占位符;
/************************************************************************* > File Name: main.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年04月19日 星期六 16时22分26秒 ************************************************************************/ #include<stdio.h> int main(int argc, char **argv) { printf("Hello World! num = %s\n", 4); return 0; }
-- 执行编译 : gcc -Wall main.c, 编译的时候报出警告, 但是编译通过, 但是运行的时候就出错了;
/************************************************************************* > File Name: main.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年04月19日 星期六 16时22分26秒 ************************************************************************/ #include<stdio.h> int main(int argc, char **argv) { printf("Hello World! num = %s\n", 4); return 0; }
2. 编译多个文件
由三个文件组成的程序 : kill.h, kill.c, main.c, 其中 main.c 是主函数入口, 调用 kill.c 定义的方法;
-- kill.h 内容 : 声明 kill 方法, 引用了该头文件, 即可使用 kill 方法;
/************************************************************************* > File Name: kill.h > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年04月19日 星期六 20时51分59秒 ************************************************************************/ #ifndef KILL int kill(char *); #endif -- kill.c 内容 : 主要实现 kill.h 中声明的 kill 方法; /************************************************************************* > File Name: kill.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年04月19日 星期六 20时53分53秒 ************************************************************************/ #include<stdio.h> int kill(char *ch) { printf("%s \n", ch); return 0; } -- mian.c内容 : 引用 kill.h 库; /************************************************************************* > File Name: main.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年04月19日 星期六 16时22分26秒 ************************************************************************/ #include<stdio.h> #include"kill.h" int main(int argc, char **argv) { printf("Hello World! \n"); kill("fuck"); return 0; }
引用头文件库符号区别 : #include"kill.h" #include<kill.h> ;
-- #include "kill.h" : 先在当前目录搜索 kill.h 头文件, 在到系统中搜索该头文件;
-- #include <kill.h> : 直接去系统库中寻找头文件, 不会搜索当前目录;
编译文件 : 使用 gcc -Wall main.c kill.c -o kill 进行编译;
octopus@octopus:~/gcc$ gcc -Wall main.c kill.c -o kill octopus@octopus:~/gcc$ ./kill Hello World! fuck
3. 独立编译文件
开发需求 : 当一个项目比较大的时候, 整个项目编译时间会很长, 如果改变一个函数就需要重新编译整个项目, 就会很浪费时间;
-- 解决方案 : 程序被存储在多个源文件中, 每个源文件都单独进行编译;
单独编译多个源文件步骤 : 首先生成 对象文件, 再将对象文件链接生成可执行文件;
-- 编译对象文件 : 将源程序编译成不可执行的文件, 生成 .o 后缀的对象文件;
-- 链接程序 : gcc 中有一个链接器将所有的对象文件链接到一起, 生成一个可执行文件;
解析对象文件 : 文件中存放的是机器码, 机器码中对其他文件中的 函数 或者 变量引用的地址没有解析, 当链接程序的时候才将这些地址写入;
生成对象文件 : -c 参数用于生成 对象文件;
-- 生成kill.o对象文件 : gcc -Wall -c kill.c , 会生成 kill.o 文件, 该对象文件中引用 kill 方法, 该方法对应的地址没有被解析;
octopus@octopus:~/gcc$ gcc -Wall -c kill.c octopus@octopus:~/gcc$ ls kill.c kill.h kill.o main.c -- 生成 main.o对象文件 : gcc -Wall -c mian.c, 生成 main.o 文件;
octopus@octopus:~/gcc$ gcc -Wall -c main.c octopus@octopus:~/gcc$ ls kill.c kill.h kill.o main.c main.o
链接对象文件 : gcc main.o kill.o -o main 命令, 链接 main.o 和 kill.o 两个对象文件;
-- 不许要-Wall参数 : 链接程序只有两种结果, 成功 或者 失败, 不许要警告信息了;
-- 链接器 : gcc中ld链接器 用来链接对象文件;
octopus@octopus:~/gcc$ gcc main.o kill.o -o main octopus@octopus:~/gcc$ ./main Hello World! fuck
对象文件的链接次序 : 大部分编译器都可以随意排列顺序, 但是有的编译器需要注意链接次序;
-- 编译器和连接器次序 : 编译器和链接器搜索外部函数 是 从左到右进行查找;
-- 文件次序 : 调用函数的 对象文件, 该文件应该先于 定义函数的 对象文件, 这里 main.o 应该在 kill.o 之前;
-- 错误排查 : 如果在编译程序的时候, 列出了所有的文件, 但是还出现了 未定义 错误, 就需要注意 文件排列的问题;
修改文件流程 : 当修改了一个文件之后, 只需要 重新编译这个文件即可, 之后将这个新编译的对象文件 与 原来的对象文件进行链接, 即可生成新的可执行文件;
-- 重新编译 : 当修改了一个文件之后, 只需要将这个文件重新编译成 对象文件即可;
-- 重新链接 : 将新编译的对象文件, 与之前已经编译好的 其它源文件的对象文件进行链接即可;