嵌入式技术
从C语言变成最终的可执行文件,需要经过四步:
预处理;
编译;
汇编;
链接。
下面就以Linux环境为例,来分析下四个步骤。
预处理
写一段简单的代码:
#include其中,井号键开头的代码有两行:包含头文件和宏定义。 预处理命令:#define OK 0 int main() { printf("hellowrld "); return OK; }
root@Turbo:t# gcc -E test.c -o test.i root@Turbo:t# ls test test.c test.i root@Turbo:t#预处理后的test.i代码:
# 1 "test.c" # 1 "代码量从原来的10行变成了七百多行,主要的变化有两个:" # 1 " " # 31 " " # 1 "/usr/include/stdc-predef.h" 1 3 4 # 32 " " 2 # 1 "test.c" # 1 "/usr/include/stdio.h" 1 3 4 # 27 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 1 3 4 # 33 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 3 4 # 1 "/usr/include/features.h" 1 3 4 # 461 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4 # 452 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4 # 453 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/long-double.h" 1 3 4 # 454 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4 # 462 "/usr/include/features.h" 2 3 4 ............... # 5 "test.c" int main() { printf("hellowrld "); return 0; }
头文件没了;
return OK 变成了 return 0。
所以基本上可以得出预处理的作用: 处理所有以井号键开头的代码,包括头文件、宏定义、条件编译等等。
头文件展开。以stdio.h为例,编译器会去默认的目录下(一般是/usr/include)找到这个文件,然后把里面的内容复制一份,粘贴到C文件中。这就是为什么预处理后的文件变成了七百多行。
宏定义替换。预处理的时候如果遇到了宏定义,直接把宏替换掉,比如代码中的OK就变成了数字0。
条件编译。下面的代码就属于条件编译:
#ifndef _STDIO_H #define _STDIO_H #endif条件编译会在预处理的时候做出判断,满足条件的代码留下来,不满足的去掉。
root@Turbo:t# gcc -S test.i -o test.s root@Turbo:t# ls test test.c test.i test.s root@Turbo:t#查看编译后的文件test.s内容:
.file "test.c" .text .section .rodata .LC0: .string "hellowrld" .text .globl main .type main, @function main: .LFB0: .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 leaq .LC0(%rip), %rdi call puts@PLT movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0" .section .note.GNU-stack,"",@progbits .section .note.gnu.property,"a" .align 8 .long 1f - 0f .long 4f - 1f .long 5 0: .string "GNU" 1: .align 8 .long 0xc0000002 .long 3f - 2f 2: .long 0x3 3: .align 8 4:如果你搞过单片机的话,一定能看出来这是汇编代码。
编译的作用:
语法检查;
把C代码翻译成汇编代码。
汇编 汇编的命令:
root@Turbo:t# gcc -c test.s -o test.o root@Turbo:t# ls test test.c test.i test.o test.s查看汇编后test.o的内容:
很显然, 这是一个二进制文件。
既然是二进制文件,那能不能执行呢?答案是不行,原因也很简单,因为此时代码还不知道printf函数在哪。
汇编的作用:
把汇编代码翻译成二进制代码。
链接
链接的命令:
root@Turbo:t# gcc test.o -o test root@Turbo:t# ls test test.c test.i test.o test.s root@Turbo:t# ./test hellowrld如果工程里面有多个C文件,就会产生多个.o文件,链接的时候,把所有.o文件加上就行。
链接代码中需要用到的库(比如printf,需要链接C库)。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !