从下面这个最简单的例子开始:
qingsong@ubuntu:~/test$ ls
hello.c
2. 编译(Compilation):
编译的目的是将预处理之后的文件转换为汇编代码,当然,这中间经过了词法分析、语法分析、语义分析及优化,示例如下:
qingsong@ubuntu:~/test$ gcc -S hello.i -o hello.s
qingsong@ubuntu:~/test$ ls
hello.c hello.i hello.s
3. 汇编(Assembly):
汇编的目的是将汇编转换为机器语言(二进制),示例如下:
qingsong@ubuntu:~/test$ gcc -c hello.s -o hello.o
qingsong@ubuntu:~/test$ ls
hello.c hello.i hello.o hello.s
也可以直接将源代码一步转换为机器语言:
qingsong@ubuntu:~/test$ rm hello.o
qingsong@ubuntu:~/test$ gcc -c hello.c -o hello.o
qingsong@ubuntu:~/test$ ls
hello.c hello.i hello.o hello.s
这里的hello.o文件就是目标文件了
4. 链接(Linking):
大型的软件可能有多个模块,每个都有目标文件生成,这时候就需要链接器将这些不同的目标文件链接起来,生成单个的可执行文件。在上面的例子中只有一个目标文件,但仍然需要链接库文件(库其实就是一组目标文件的包,就是最常见的代码编译成目标文件后打包)
qingsong@ubuntu:~/test$ gcc hello.o -o a.out
qingsong@ubuntu:~/test$ ls
a.out hello.c hello.i hello.o hello.s
qingsong@ubuntu:~/test$ ./a.out
Hello,world
Circumference is: 25.132720
qingsong@ubuntu:~/test$
这里再举一个例子:
qingsong@ubuntu:~/test2$ ls
main.c mymath.h mymul.c myplus.c
qingsong@ubuntu:~/test2$ cat main.c
#include <stdio.h>
#include "mymath.h"
int main(void)
{
int x = 3, y = 4;
printf("x + y is:%d\n", myplus(x,y));
printf("x * y is:%d\n", mymul(x,y));
return 0;
}
qingsong@ubuntu:~/test2$ cat mymath.h
extern int myplus(int x, int y);
extern int mymul(int x, int y);
qingsong@ubuntu:~/test2$ cat mymul.c
int mymul(int x, int y)
{
return x * y;
}
qingsong@ubuntu:~/test2$ cat myplus.c
int myplus(int x, int y)
{
return x + y;
}
qingsong@ubuntu:~/test2$ gcc -c myplus.c -o myplus.o
qingsong@ubuntu:~/test2$ gcc -c mymul.c -o mymul.o
qingsong@ubuntu:~/test2$ gcc -c main.c -o main.o
qingsong@ubuntu:~/test2$ gcc myplus.o mymul.o main.o -o mycalc.exe
qingsong@ubuntu:~/test2$ ./mycalc.exe
x + y is:7
x * y is:12
qingsong@ubuntu:~/test2$
qingsong@ubuntu:~/test2$ rm *.exe
qingsong@ubuntu:~/test2$ ls
main.c mymath.h mymul.c myplus.c
qingsong@ubuntu:~/test2$ vi Makefile
mycalc.exe:myplus.o mymul.o main.o
gcc myplus.o mymul.o main.o -o mycalc.exe
myplus.o:myplus.c
gcc -c myplus.c -o myplus.o
mymul.o:mymul.c
gcc -c mymul.c -o mymul.o
main.o:main.c
gcc -c main.c -o main.o
clean:
rm *.o
rm mycalc.exe
qingsong@ubuntu:~/test2$ make
gcc -c myplus.c -o myplus.o
gcc -c mymul.c -o mymul.o
gcc -c main.c -o main.o
gcc myplus.o mymul.o main.o -o mycalc.exe
qingsong@ubuntu:~/test2$ ls
main.c main.o Makefile mycalc.exe mymath.h mymul.c mymul.o myplus.c myplus.o
qingsong@ubuntu:~/test2$ ./mycalc.exe
x + y is:7
x * y is:12
qingsong@ubuntu:~/test2$ make clean
rm *.o
rm mycalc.exe
qingsong@ubuntu:~/test2$ ls
main.c Makefile mymath.h mymul.c myplus.c
qingsong@ubuntu:~/test2$
qingsong@ubuntu:~/test2$ make
gcc -c myplus.c -o myplus.o
gcc -c mymul.c -o mymul.o
gcc -c main.c -o main.o
gcc myplus.o mymul.o main.o -o mycalc.exe
qingsong@ubuntu:~/test2$ vi myplus.c
int myplus(int x, int y)
{
int z;
z = x + y;
return z;
}
qingsong@ubuntu:~/test2$ make
gcc -c myplus.c -o myplus.o
gcc myplus.o mymul.o main.o -o mycalc.exe
qingsong@ubuntu:~/test2$
可以看到,由于只修改了myplus.c,所以只对这个文件进行了重新编译。
https://www.cnblogs.com/zhanghongfeng/p/7813172.html
最后的clean操作清除执行过程中产生的临时文件。当用make命令执行的时候,clean下的命令不会执行,要以make clean方式单独执行。执行后,所有*.o和main都被删除。
qingsong@ubuntu:~/test$ ls
hello.c
qingsong@ubuntu:~/test$ cat hello.c
#include <stdio.h>
#define PI 3.14159
#define RADIUS 4
int main(void)
{
printf("Hello,world\n");
//Calculate
printf("Circumference is: %f\n", 2 * PI * RADIUS);
}
qingsong@ubuntu:~/test$ gcc hello.c
qingsong@ubuntu:~/test$ ls
a.out hello.c
qingsong@ubuntu:~/test$ ./a.out
Hello,world
Circumference is: 25.132720
qingsong@ubuntu:~/test$ rm a.out
其实,上面的步骤包括了预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)这4个步骤,作用分别如下:
1. 预处理(Preprocessing): 主要处理文本中以#开头的部分,比如删除所有的#define并展开宏定义、删除所有注释、添加行号和文件名标识等。可以使用gcc的-E选项对源文件只进行预处理,示例如下:
qingsong@ubuntu:~/test$ gcc -E hello.c -o hello.i
qingsong@ubuntu:~/test$ ls
hello.c hello.i
qingsong@ubuntu:~/test$ more hello.i
..
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
..
extern void setlinebuf (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
extern int fprintf (FILE *__restrict __stream,
const char *__restrict __format, ...);
extern int printf (const char *__restrict __format, ...);
extern int puts (const char *__s);
extern int fseek (FILE *__stream, long int __off, int __whence);
# 6 "hello.c"
int main(void)
{
printf("Hello,world\n");
printf("Circumference is: %f\n", 2 * 3.14159 * 4);
}
能够看到经过预处理之后的文件 hello.i中内容如上面所示
2. 编译(Compilation):
编译的目的是将预处理之后的文件转换为汇编代码,当然,这中间经过了词法分析、语法分析、语义分析及优化,示例如下:
qingsong@ubuntu:~/test$ gcc -S hello.i -o hello.s
qingsong@ubuntu:~/test$ ls
hello.c hello.i hello.s
qingsong@ubuntu:~/test$ more hello.s
.file "hello.c"
.text
.section .rodata
.LC0:
.string "Hello,world"
.LC2:
.string "Circumference is: %f\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
leaq .LC0(%rip), %rdi
call puts@PLT
movq .LC1(%rip), %rax
movq %rax, -8(%rbp)
movsd -8(%rbp), %xmm0
leaq .LC2(%rip), %rdi
movl $1, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.section .rodata
.align 8
.LC1:
.long 4028335726
.long 1077486073
.ident "GCC: (Ubuntu 7.3.0-16ubuntu3) 7.3.0"
.section .note.GNU-stack,"",@progbits
可以看到,汇编语言仍然是可读的
3. 汇编(Assembly):
汇编的目的是将汇编转换为机器语言(二进制),示例如下:
qingsong@ubuntu:~/test$ gcc -c hello.s -o hello.o
qingsong@ubuntu:~/test$ ls
hello.c hello.i hello.o hello.s
也可以直接将源代码一步转换为机器语言:
qingsong@ubuntu:~/test$ rm hello.o
qingsong@ubuntu:~/test$ gcc -c hello.c -o hello.o
qingsong@ubuntu:~/test$ ls
hello.c hello.i hello.o hello.s
这里的hello.o文件就是目标文件了
4. 链接(Linking):
大型的软件可能有多个模块,每个都有目标文件生成,这时候就需要链接器将这些不同的目标文件链接起来,生成单个的可执行文件。在上面的例子中只有一个目标文件,但仍然需要链接库文件(库其实就是一组目标文件的包,就是最常见的代码编译成目标文件后打包)
qingsong@ubuntu:~/test$ gcc hello.o -o a.out
qingsong@ubuntu:~/test$ ls
a.out hello.c hello.i hello.o hello.s
qingsong@ubuntu:~/test$ ./a.out
Hello,world
Circumference is: 25.132720
qingsong@ubuntu:~/test$
这里再举一个例子:
qingsong@ubuntu:~/test2$ ls
main.c mymath.h mymul.c myplus.c
qingsong@ubuntu:~/test2$ cat main.c
#include <stdio.h>
#include "mymath.h"
int main(void)
{
int x = 3, y = 4;
printf("x + y is:%d\n", myplus(x,y));
printf("x * y is:%d\n", mymul(x,y));
return 0;
}
qingsong@ubuntu:~/test2$ cat mymath.h
extern int myplus(int x, int y);
extern int mymul(int x, int y);
qingsong@ubuntu:~/test2$ cat mymul.c
int mymul(int x, int y)
{
return x * y;
}
qingsong@ubuntu:~/test2$ cat myplus.c
int myplus(int x, int y)
{
return x + y;
}
qingsong@ubuntu:~/test2$ gcc -c myplus.c -o myplus.o
qingsong@ubuntu:~/test2$ gcc -c mymul.c -o mymul.o
qingsong@ubuntu:~/test2$ gcc -c main.c -o main.o
qingsong@ubuntu:~/test2$ gcc myplus.o mymul.o main.o -o mycalc.exe
qingsong@ubuntu:~/test2$ ./mycalc.exe
x + y is:7
x * y is:12
qingsong@ubuntu:~/test2$
上面的例子中,如果每次都要运行那么多gcc命令,会特别麻烦,可以编写一个Makefile文件,将各个组件的依赖关系及生成方式写进去:
qingsong@ubuntu:~/test2$ rm *.oqingsong@ubuntu:~/test2$ rm *.exe
qingsong@ubuntu:~/test2$ ls
main.c mymath.h mymul.c myplus.c
qingsong@ubuntu:~/test2$ vi Makefile
mycalc.exe:myplus.o mymul.o main.o
gcc myplus.o mymul.o main.o -o mycalc.exe
myplus.o:myplus.c
gcc -c myplus.c -o myplus.o
mymul.o:mymul.c
gcc -c mymul.c -o mymul.o
main.o:main.c
gcc -c main.c -o main.o
clean:
rm *.o
rm mycalc.exe
qingsong@ubuntu:~/test2$ make
gcc -c myplus.c -o myplus.o
gcc -c mymul.c -o mymul.o
gcc -c main.c -o main.o
gcc myplus.o mymul.o main.o -o mycalc.exe
qingsong@ubuntu:~/test2$ ls
main.c main.o Makefile mycalc.exe mymath.h mymul.c mymul.o myplus.c myplus.o
qingsong@ubuntu:~/test2$ ./mycalc.exe
x + y is:7
x * y is:12
qingsong@ubuntu:~/test2$ make clean
rm *.o
rm mycalc.exe
qingsong@ubuntu:~/test2$ ls
main.c Makefile mymath.h mymul.c myplus.c
qingsong@ubuntu:~/test2$
qingsong@ubuntu:~/test2$ make
gcc -c myplus.c -o myplus.o
gcc -c mymul.c -o mymul.o
gcc -c main.c -o main.o
gcc myplus.o mymul.o main.o -o mycalc.exe
qingsong@ubuntu:~/test2$ vi myplus.c
int myplus(int x, int y)
{
int z;
z = x + y;
return z;
}
qingsong@ubuntu:~/test2$ make
gcc -c myplus.c -o myplus.o
gcc myplus.o mymul.o main.o -o mycalc.exe
qingsong@ubuntu:~/test2$
可以看到,由于只修改了myplus.c,所以只对这个文件进行了重新编译。
https://www.cnblogs.com/zhanghongfeng/p/7813172.html
最后的clean操作清除执行过程中产生的临时文件。当用make命令执行的时候,clean下的命令不会执行,要以make clean方式单独执行。执行后,所有*.o和main都被删除。