【嵌入式开发】gcc 学习笔记(一) - 编译C程序 及 编译过程(二)

二. 编译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 之前;


-- 错误排查 : 如果在编译程序的时候, 列出了所有的文件, 但是还出现了 未定义 错误, 就需要注意 文件排列的问题;




修改文件流程 : 当修改了一个文件之后, 只需要 重新编译这个文件即可, 之后将这个新编译的对象文件 与 原来的对象文件进行链接, 即可生成新的可执行文件;


-- 重新编译 : 当修改了一个文件之后, 只需要将这个文件重新编译成 对象文件即可;


-- 重新链接 : 将新编译的对象文件, 与之前已经编译好的 其它源文件的对象文件进行链接即可;


上一篇:LoadRunner能录制微信登录过程么?


下一篇:IT的开始之路——微信小程序(2)