KEIL 编译器高级使用与调试技巧【一】 手工编译、编译选项、预处理分析

一、手工编译bin文件

1.1 KEIL 自带的编译组件

ARM v6编译器(Arm Compiler for Embedded)包含以下几个工具:

armclang

编译器和汇编器,可对 C、C++ 汇编文件进行编译和汇编

armlink

链接器,用于将目标文件和库文件进行链接,形成可执行文件。

fromelf

文件转换工具,可将可执行文件转换成二进制格式、HEX 格式,也可生成反汇编文件,包含代码、数据大小信息的文件

armar

归档工具,可将多个 ELF 格式的目标文件进行归档或者集合成库文件,从而避免每次都重新编译或者公开源码。

1.2 手工编译过程

  1. 使用编辑器编写 C/C++ 源文件,使用 armclang 编译器进行编译,生成相应的目标文件。
  2. 使用编辑器编写汇编文件,使用 armclang 进行汇编,生成对应的目标文件。
  3. 使用 armlink 将所有的目标文件和库一起链接,形成最终的可执行文件(ELF格式)。
  4. 如需要,可使用 fromelf 进行文件转换,生成二进制文件格式,或者显示代码和数据的使用信息。

1.3 手动编译开始

1.3.1 配置环境变量

首先确保环境变量包括 keil 下的 armclang 目录,这里找自己的 keil 安装目录下的 ARMCLANG 即可。

1.3.2 新建目录与文件

新建两个文件,目录结构与内容如下:

 hello.c 内容如下:

int main (void) {
    for (;;) {
    }
    return 0;
}

start.c 内容如下: 

int main (void);

void start (void) {
    main();
}

1.3.3 执行编译命令

之后在这个目录执行:

armclang --target=arm-arm-none-eabi -mcpu=cortex-m3 -c .\start.c
armclang --target=arm-arm-none-eabi -mcpu=cortex-m3 -c .\hello.c
armlink start.o hello.o -o hello.axf

上述代码工作流程如下: 

  • 将 start.c 编译为目标代码,并使用默认名称 start.o 保存。
  • 将 hello.c 编译为目标代码,并使用默认名称 hello.o 保存。
  • 链接目标文件 start.o 和 hello.o,生成名为 hello.axf 的可执行文件。

之后该目录下就多了 hello.axf、hello.o 和 start.o 三个文件

 

1.3.4 执行编译命令

选项描述

-c

执行编译步骤,但不进行链接。

-x

指定后续源文件的语言,例如 -xc inputfile.s 或 -xc++ inputfile.s。

-std

指定要编译的语言标准,例如 -std=c90,其它可用选项如c99等等。

--target=arch-vendor-os-abi

为选定的执行状态(AArch32 或 AArch64)生成代码,例如 --target=aarch64-arm-none-eabi 或 --target=arm-arm-none-eabi。

-march=name

为指定的架构生成代码,例如 -march=armv8-a 或 -march=armv7-a。

-march=list

显示所选执行状态支持的所有架构列表。

-mcpu=name

为指定的处理器生成代码,例如 -mcpu=cortex-a53、-mcpu=cortex-a57 或 -mcpu=cortex-a15。

-mcpu=list

显示所选执行状态支持的所有处理器列表。

-marm

请求编译器针对仅由32位指令组成的A32指令集生成代码。例如,--target=arm-arm-none-eabi -march=armv7-a -marm。此选项强调性能。

-mthumb

请求编译器针对由16位和32位指令组成的T32指令集生成代码。例如,--target=arm-arm-none-eabi -march=armv8-a -mthumb。此选项强调代码密度。

-mfloat-abi

指定是否使用硬件指令或软件库函数进行浮点运算。

-mfpu

指定目标FPU架构。

-g (armclang)

生成与DWARF 4标准兼容的DWARF调试表。

-e

仅执行预处理步骤。

-o (armclang)

指定输出文件的名称。

-Onum

指定编译源文件时使用的性能优化级别。

-Os

在代码大小与速度之间取得平衡。

-Oz

针对代码大小进行优化。

-S

输出编译器生成的机器代码的反汇编代码。

-###

显示调用编译器和链接器时将使用的选项的诊断输出,但不执行编译或链接步骤。

1.3.5 反汇编输出

可以查看反汇编文件,执行以下命令:

fromelf --text -c hello.axf

至此,编译器就给我们输出了反汇编文件:

PS C:\Users\P793\Desktop\new> fromelf --text -c hello.axf

========================================================================

** ELF Header Information

    File Name: hello.axf

    Machine class: ELFCLASS32 (32-bit)
    Data encoding: ELFDATA2LSB (Little endian)
    Header version: EV_CURRENT (Current version)
    Operating System ABI: none
    ABI Version: 0
    File Type: ET_EXEC (Executable) (2)
    Machine: EM_ARM (ARM)

    Image Entry point: 0x00008001
    Flags: EF_ARM_HASENTRY + EF_ARM_ABI_FLOAT_SOFT (0x05000202)

    ARM ELF revision: 5 (ABI version 2)

    Conforms to Soft float procedure-call standard

    Built with
    Component: ARM Compiler 6.16 Tool: armlink [5dfeaa00]

    Header size: 52 bytes (0x34)
    Program header entry size: 32 bytes (0x20)
    Section header entry size: 40 bytes (0x28)

    Program header entries: 1
    Section header entries: 9

    Program header offset: 7728 (0x00001e30)
    Section header offset: 7760 (0x00001e50)

    Section header string table index: 8

========================================================================

** Program header #0 (PT_LOAD) [PF_X + PF_W + PF_R + PF_ARM_ENTRY]
    Size : 416 bytes (320 bytes in file)
    Virtual address: 0x00008000 (Alignment 4)


========================================================================

** Section #1 'ER_RO' (SHT_PROGBITS) [SHF_ALLOC + SHF_EXECINSTR]
    Size   : 320 bytes (alignment 4)
    Address: 0x00008000

    $t
    !!!main
    __main
        0x00008000:    f000f802    ....    BL       __scatterload ; 0x8008
        0x00008004:    f000f82c    ..,.    BL       __rt_entry ; 0x8060
    !!!scatter
    __scatterload
    __scatterload_rt2
    __scatterload_rt2_thumb_only
        0x00008008:    a00a        ..      ADR      r0,{pc}+0x2c ; 0x8034
        0x0000800a:    e8900c00    ....    LDM      r0,{r10,r11}
        0x0000800e:    4482        .D      ADD      r10,r10,r0
        0x00008010:    4483        .D      ADD      r11,r11,r0
        0x00008012:    f1aa0701    ....    SUB      r7,r10,#1
    __scatterload_null
        0x00008016:    45da        .E      CMP      r10,r11
        0x00008018:    d101        ..      BNE      0x801e ; __scatterload_null + 8
        0x0000801a:    f000f821    ..!.    BL       __rt_entry ; 0x8060
        0x0000801e:    f2af0e09    ....    ADR      lr,{pc}-7 ; 0x8017
        0x00008022:    e8ba000f    ....    LDM      r10!,{r0-r3}
        0x00008026:    f0130f01    ....    TST      r3,#1
        0x0000802a:    bf18        ..      IT       NE
        0x0000802c:    1afb        ..      SUBNE    r3,r7,r3
        0x0000802e:    f0430301    C...    ORR      r3,r3,#1
        0x00008032:    4718        .G      BX       r3
    $d
        0x00008034:    000000fc    ....    DCD    252
        0x00008038:    0000010c    ....    DCD    268
    $t
    !!handler_zi
    __scatterload_zeroinit
        0x0000803c:    2300        .#      MOVS     r3,#0
        0x0000803e:    2400        .$      MOVS     r4,#0
        0x00008040:    2500        .%      MOVS     r5,#0
        0x00008042:    2600        .&      MOVS     r6,#0
        0x00008044:    3a10        .:      SUBS     r2,r2,#0x10
        0x00008046:    bf28        (.      IT       CS
        0x00008048:    c178        x.      STMCS    r1!,{r3-r6}
        0x0000804a:    d8fb        ..      BHI      0x8044 ; __scatterload_zeroinit + 8
        0x0000804c:    0752        R.      LSLS     r2,r2,#29
        0x0000804e:    bf28        (.      IT       CS
        0x00008050:    c130        0.      STMCS    r1!,{r4,r5}
        0x00008052:    bf48        H.      IT       MI
        0x00008054:    600b        .`      STRMI    r3,[r1,#0]
        0x00008056:    4770        pG      BX       lr
    .ARM.Collect$$libinit$$00000000
    __rt_lib_init
        0x00008058:    b51f        ..      PUSH     {r0-r4,lr}
    .ARM.Collect$$libinit$$00000002
    .ARM.Collect$$libinit$$00000004
    .ARM.Collect$$libinit$$0000000A
    .ARM.Collect$$libinit$$0000000C
    .ARM.Collect$$libinit$$0000000E
    .ARM.Collect$$libinit$$00000011
    .ARM.Collect$$libinit$$00000013
    .ARM.Collect$$libinit$$00000015
    .ARM.Collect$$libinit$$00000017
    .ARM.Collect$$libinit$$00000019
    .ARM.Collect$$libinit$$0000001B
    .ARM.Collect$$libinit$$0000001D
    .ARM.Collect$$libinit$$0000001F
    .ARM.Collect$$libinit$$00000021
    .ARM.Collect$$libinit$$00000023
    .ARM.Collect$$libinit$$00000025
    .ARM.Collect$$libinit$$0000002C
    .ARM.Collect$$libinit$$0000002E
    .ARM.Collect$$libinit$$00000030
    .ARM.Collect$$libinit$$00000032
    .ARM.Collect$$libinit$$00000033
    __rt_lib_init_alloca_1
    __rt_lib_init_argv_1
    __rt_lib_init_atexit_1
    __rt_lib_init_clock_1
    __rt_lib_init_cpp_1
    __rt_lib_init_exceptions_1
    __rt_lib_init_fp_1
    __rt_lib_init_fp_trap_1
    __rt_lib_init_getenv_1
    __rt_lib_init_heap_1
    __rt_lib_init_lc_collate_1
    __rt_lib_init_lc_ctype_1
    __rt_lib_init_lc_monetary_1
    __rt_lib_init_lc_numeric_1
    __rt_lib_init_lc_time_1
    __rt_lib_init_preinit_1
    __rt_lib_init_rand_1
    __rt_lib_init_return
    __rt_lib_init_signal_1
    __rt_lib_init_stdio_1
    __rt_lib_init_user_alloc_1
        0x0000805a:    bd1f        ..      POP      {r0-r4,pc}
    .ARM.Collect$$libshutdown$$00000000
    __rt_lib_shutdown
        0x0000805c:    b510        ..      PUSH     {r4,lr}
    .ARM.Collect$$libshutdown$$00000002
    .ARM.Collect$$libshutdown$$00000004
    .ARM.Collect$$libshutdown$$00000007
    .ARM.Collect$$libshutdown$$0000000A
    .ARM.Collect$$libshutdown$$0000000C
    .ARM.Collect$$libshutdown$$0000000F
    .ARM.Collect$$libshutdown$$00000010
    __rt_lib_shutdown_cpp_1
    __rt_lib_shutdown_fp_trap_1
    __rt_lib_shutdown_heap_1
    __rt_lib_shutdown_return
    __rt_lib_shutdown_signal_1
    __rt_lib_shutdown_stdio_1
    __rt_lib_shutdown_user_alloc_1
        0x0000805e:    bd10        ..      POP      {r4,pc}
    .ARM.Collect$$rtentry$$00000000
    .ARM.Collect$$rtentry$$00000002
    .ARM.Collect$$rtentry$$00000004
    __rt_entry
    __rt_entry_presh_1
    __rt_entry_sh
        0x00008060:    f000f80d    ....    BL       __user_setup_stackheap ; 0x807e
        0x00008064:    4611        .F      MOV      r1,r2
    .ARM.Collect$$rtentry$$00000009
    .ARM.Collect$$rtentry$$0000000A
    __rt_entry_li
    __rt_entry_postsh_1
        0x00008066:    f7fffff7    ....    BL       __rt_lib_init ; 0x8058
    .ARM.Collect$$rtentry$$0000000C
    .ARM.Collect$$rtentry$$0000000D
    __rt_entry_main
    __rt_entry_postli_1
        0x0000806a:    f000f85b    ..[.    BL       main ; 0x8124
        0x0000806e:    f000f82b    ..+.    BL       exit ; 0x80c8
    .ARM.Collect$$rtexit$$00000000
    __rt_exit
        0x00008072:    b403        ..      PUSH     {r0,r1}
    .ARM.Collect$$rtexit$$00000002
    .ARM.Collect$$rtexit$$00000003
    __rt_exit_ls
    __rt_exit_prels_1
        0x00008074:    f7fffff2    ....    BL       __rt_lib_shutdown ; 0x805c
    .ARM.Collect$$rtexit$$00000004
    __rt_exit_exit
        0x00008078:    bc03        ..      POP      {r0,r1}
        0x0000807a:    f000f84d    ..M.    BL       _sys_exit ; 0x8118
    .text
    __user_setup_stackheap
        0x0000807e:    4675        uF      MOV      r5,lr
        0x00008080:    f000f82c    ..,.    BL       __user_libspace ; 0x80dc
        0x00008084:    46ae        .F      MOV      lr,r5
        0x00008086:    0005        ..      MOVS     r5,r0
        0x00008088:    4669        iF      MOV      r1,sp
        0x0000808a:    4653        SF      MOV      r3,r10
        0x0000808c:    f0200007     ...    BIC      r0,r0,#7
        0x00008090:    4685        .F      MOV      sp,r0
        0x00008092:    b018        ..      ADD      sp,sp,#0x60
        0x00008094:    b520         .      PUSH     {r5,lr}
        0x00008096:    f000f825    ..%.    BL       __user_initial_stackheap ; 0x80e4
        0x0000809a:    e8bd4020    .. @    POP      {r5,lr}
        0x0000809e:    f04f0600    O...    MOV      r6,#0
        0x000080a2:    f04f0700    O...    MOV      r7,#0
        0x000080a6:    f04f0800    O...    MOV      r8,#0
        0x000080aa:    f04f0b00    O...    MOV      r11,#0
        0x000080ae:    46ac        .F      MOV      r12,r5
        0x000080b0:    e8ac09c0    ....    STM      r12!,{r6-r8,r11}
        0x000080b4:    e8ac09c0    ....    STM      r12!,{r6-r8,r11}
        0x000080b8:    e8ac09c0    ....    STM      r12!,{r6-r8,r11}
        0x000080bc:    e8ac09c0    ....    STM      r12!,{r6-r8,r11}
        0x000080c0:    f0210107    !...    BIC      r1,r1,#7
        0x000080c4:    468d        .F      MOV      sp,r1
        0x000080c6:    4770        pG      BX       lr
    .text
    exit
        0x000080c8:    b510        ..      PUSH     {r4,lr}
        0x000080ca:    4604        .F      MOV      r4,r0
        0x000080cc:    f3af8000    ....    NOP.W
        0x000080d0:    4620         F      MOV      r0,r4
        0x000080d2:    e8bd4010    ...@    POP      {r4,lr}
        0x000080d6:    f7ffbfcc    ....    B.W      __rt_exit ; 0x8072
        0x000080da:    0000        ..      MOVS     r0,r0
    .text
    __user_libspace
    __user_perproc_libspace
    __user_perthread_libspace
        0x000080dc:    4800        .H      LDR      r0,[pc,#0] ; [0x80e0] = 0x8140
        0x000080de:    4770        pG      BX       lr
    $d
        0x000080e0:    00008140    @...    DCD    33088
    $t
    .text
    __user_initial_stackheap
        0x000080e4:    b500        ..      PUSH     {lr}
        0x000080e6:    b085        ..      SUB      sp,sp,#0x14
        0x000080e8:    4669        iF      MOV      r1,sp
        0x000080ea:    aa01        ..      ADD      r2,sp,#4
        0x000080ec:    600a        .`      STR      r2,[r1,#0]
        0x000080ee:    f04f0016    O...    MOV      r0,#0x16
        0x000080f2:    beab        ..      BKPT     #0xab
        0x000080f4:    9801        ..      LDR      r0,[sp,#4]
        0x000080f6:    2800        .(      CMP      r0,#0
        0x000080f8:    bf02        ..      ITTT     EQ
        0x000080fa:    4805        .H      LDREQ    r0,_RW_Limit ; [0x8110] = 0x81a0
        0x000080fc:    1dc0        ..      ADDEQ    r0,r0,#7
        0x000080fe:    f0200007     ...    BICEQ    r0,r0,#7
        0x00008102:    9903        ..      LDR      r1,[sp,#0xc]
        0x00008104:    9a02        ..      LDR      r2,[sp,#8]
        0x00008106:    9b04        ..      LDR      r3,[sp,#0x10]
        0x00008108:    b005        ..      ADD      sp,sp,#0x14
        0x0000810a:    bd00        ..      POP      {pc}
    $d
        0x0000810c:    00000009    ....    DCD    9
    _RW_Limit
        0x00008110:    000081a0    ....    DCD    33184
    $t
    .text
    __I$use$semihosting
    __use_no_semihosting_swi
        0x00008114:    4770        pG      BX       lr
    .text
    __semihosting_library_function
        0x00008116:    0000        ..      MOVS     r0,r0
    .text
    _sys_exit
        0x00008118:    4901        .I      LDR      r1,[pc,#4] ; [0x8120] = 0x20026
        0x0000811a:    2018        .       MOVS     r0,#0x18
        0x0000811c:    beab        ..      BKPT     #0xab
        0x0000811e:    e7fe        ..      B        0x811e ; _sys_exit + 6
    $d
        0x00008120:    00020026    &...    DCD    131110
    $t.0
    main
        0x00008124:    b081        ..      SUB      sp,sp,#4
        0x00008126:    2000        .       MOVS     r0,#0
        0x00008128:    9000        ..      STR      r0,[sp,#0]
        0x0000812a:    e7ff        ..      B        0x812c ; main + 8
        0x0000812c:    e7fe        ..      B        0x812c ; main + 8
        0x0000812e:    0000        ..      MOVS     r0,r0
    $d.realdata
    Region$$Table$$Base
        0x00008130:    00008140    @...    DCD    33088
        0x00008134:    00008140    @...    DCD    33088
        0x00008138:    00000060    `...    DCD    96
        0x0000813c:    0000803c    <...    DCD    32828
    Region$$Table$$Limit

** Section #2 'ER_ZI' (SHT_NOBITS) [SHF_ALLOC + SHF_WRITE]
    Size   : 96 bytes (alignment 4)
    Address: 0x00008140


** Section #3 '.debug_frame' (SHT_PROGBITS)
    Size   : 428 bytes


** Section #4 '.symtab' (SHT_SYMTAB)
    Size   : 2960 bytes (alignment 4)
    String table #5 '.strtab'
    Last local symbol no. 118


** Section #5 '.strtab' (SHT_STRTAB)
    Size   : 3260 bytes


** Section #6 '.note' (SHT_NOTE)
    Size   : 24 bytes (alignment 4)


** Section #7 '.comment' (SHT_PROGBITS)
    Size   : 608 bytes


** Section #8 '.shstrtab' (SHT_STRTAB)
    Size   : 76 bytes

这段输出是 ARM 编译器生成的 ELF 格式可执行文件 hello.axf 的反汇编和段信息。以下是关键点的中文分析:

  1. 文件头信息
  • 32位ARM架构(ELFCLASS32)
  • 小端格式(ELFDATA2LSB)
  • 入口地址:0x00008001
  • 使用软件浮点(Soft float)
  • ARM编译器版本:6.16
  1. 程序执行流程
  • __main开始(0x8000)
  • 调用__scatterload初始化数据(0x8008)
  • 调用__rt_entry进入运行时环境(0x8060)
  • 最终跳转到main函数(0x8124)
  1. 关键函数
  • __scatterload:负责加载初始化数据(0x8008-0x8032)
  • __scatterload_zeroinit:清零ZI(未初始化)数据区(0x803c-0x8056)
  • main函数:位于0x8124,当前是一个空循环
  1. 内存布局
  • 代码段(ER_RO):0x8000-0x813F (320字节)
  • 未初始化数据段(ER_ZI):0x8140开始(96字节)
  1. 特殊说明
  • 使用半主机模式(semihosting)进行调试输出
  • 包含ARM库的初始化代码(__rt_lib_init等)
  • 程序最终进入无限循环(0x812c处的B指令)

1.4.6 bin 文件输出

而如果希望生成bin文件,则命令如下:

fromelf --bin --output=hello.bin hello.axf

bin 文件和反汇编文件的数据一样只是变成了大端模式:

1.4.6 总结

无论是我们使用手动方案还是 keil 自动所编译的,均是这个流程:

 预处理部分包括:

包含头文件:将标准库头文件或自定义头文件插入到当前文件的当前位置。

宏定义与替换:处理 #define 指令,将宏定义的常量、函数式宏或代码片段替换为相应的结果。

条件编译:处理条件编译指令,用于根据条件决定是否编译某段代码

删除所有注释:删除源代码中的所有注释(/*...*/和 课//..),以减少编译器的处理负担。

#Pragma等指令:保留#pragma等指令,从而向编译器传递特定实现相关的指令。

某些特殊指令:处理某些特殊指令,如#error,用于在预处理阶段生成错误信息并停止编译,或者完成其它操作。

二、Keil 的工程构建分析

2.1 编译选项

在Option for Target中,提供了对整个工程中所有文件的相关编译、链接配置选项。

2.1.1 预处理(Preprocessor Symbols):

这里主要就是预定义功能,相当于在程序中的#define xxxx。

2.1.2 语言/代码生成(Language / Code Generation)

  • Execute only Code:只生成执行代码 【设置编译器命令行:--execute_only】 只生成执行代码防止编译器生成任何数据访问代码部分。
  • Warnings:警告 【No Warnings设置编译器命令行:-W】 No Warnings:不会有警告提示和输出; All Warnings:所有警告提示和输出。 
  • Language C。选择 C 语言规范风格。
  • Optimize:优化选择项,有Level0 - Level3四个选项 【设置编译器命令行:-Onum】 初学者、在线调试建议使用Level0,也就是不优化,这样执行的效果才和代码一样。如果配置成Level3,在线调试可能有些地方优化而不能打断点。
  • Plain Char is Signed:纯字符标记为字符 【设置编译器命令行:--signed_chars】 代码举例:char a[] = “abcd”; 也就是说将“abcd”标记为字符型。 
  • Split Load and Store Multiple:加载和存储多个分裂 【设置编译器命令行:--split_ldm】 非对齐数据采用多次访问方式。当 LMD/STM 指令有 4 个以上产生时,列分裂LMD 和 STM 指令,以减不中断延迟。
  • Read-Only Position Independent:为常量生成独立的代码空间 【设置编译器命令行:--apcs=/ropi】 比如:我们定义字库变量为常量,勾选该选项,会将这些字库变量放在独立的代码空间。 
  • One ELF Section per Function:优化每一个函数 ELF 段 【设置编译器命令行:--split_sections】 每个函数都会产生一个 ELF 段,勾选上,允许优化每一个 ELF 段。这个选项可以减少潜在的共享地址、数据和函数之间的字符串。直白的意思:可以减少代码量ROM的大小(内存RAM不会减小)。举一个例子,勾选之前和勾选之后,编译后存储大小对比: 勾选之前: Program Size: Code=2540 RO-data=336 RW-data=40 ZI-data=1024。勾选之后: Program Size: Code=908 RO-data=320 RW-data=40 ZI-data=1024Read-Write Position Independent:为可读写代码生成独立的代码空间 【设置编译器命令行:--apcs=/rwpi】
  • No Auto Includes:不自动添加头文件 不勾选该选项,编译器就会在Keil安装路径寻找你工程中.h文件。 举例:我们定义uint8_t是定义在stdint.h文件里面的,但是我们工程目录下一般是没有stdint.h文件。这时候,编译器就会在Keil路径下去寻找stdint.h文件。

在我们选择这些配置项时,下方的 Compiler control string 自动生成编译的配置选项,此选项对于我们之前的生成 .o 文件命令,只是加了一些我们勾选的参数。

armclang --target=arm-arm-none-eabi -mcpu=cortex-m3 -c .\start.c

2.2 链接选项

  • Use Memory Layout from Target Dialog​:勾选后,链接器直接使用“Target”选项卡中定义的内存地址(如Flash/RAM的起始和大小)。
  • 作用**​:避免手动维护分散加载文件(Scatter File),适合简单项目。
  • Make RW Sections Position Independent:使读写段(RAM中的数据)支持地址无关(适用于动态加载或固件升级)。
  • Make RO Sections Position Independent:使只读段(Flash中的代码/常量)支持地址无关(较少使用,可能增加代码大小)。
  • Don't Search Standard Libraries:禁止链接标准库(需手动指定所有依赖库),用于精简代码或自定义库。
  • Report 'might fail' Conditions as Errors:将潜在链接问题(如内存不足)视为错误(严格模式)。

同样的,在勾选配置时,下方的 Linker control string 也会变动,此对应我们之前的:

armlink start.o hello.o -o hello.axf

链接命令。 

2.3 预处理结果

考虑一个很简单的 Keil 工程,只有两个文件:

main.c:

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main() 
{
	  int max = MAX(3, 4);
    return 0;
}

void SystemInit(void) 
{
    // 空实现
}

 startup.s:

Stack_Size		EQU     0x1000

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size      EQU     0x1000

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                                         
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler          [WEAK]
                B       .
                ENDP
MemManage_Handler\
                PROC
                EXPORT  MemManage_Handler          [WEAK]
                B       .
                ENDP
BusFault_Handler\
                PROC
                EXPORT  BusFault_Handler           [WEAK]
                B       .
                ENDP
UsageFault_Handler\
                PROC
                EXPORT  UsageFault_Handler         [WEAK]
                B       .
                ENDP
SVC_Handler     PROC
                EXPORT  SVC_Handler                [WEAK]
                B       .
                ENDP
DebugMon_Handler\
                PROC
                EXPORT  DebugMon_Handler           [WEAK]
                B       .
                ENDP
PendSV_Handler  PROC
                EXPORT  PendSV_Handler             [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler            [WEAK]
                B       .
                ENDP

Default_Handler PROC

                B       .

                ENDP

                ALIGN

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB
                
                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit
                
                 ELSE
                
                 IMPORT  __use_two_region_memory
                 EXPORT  __user_initial_stackheap
                 
__user_initial_stackheap

                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR

                 ALIGN

                 ENDIF

                 END

 目录结构如下:

在 Keil 勾选上这个选项即可生成 .i 预编译后的文件:

在 目录下Listings 可以发现生成的 main.i 文件: 

这个文件内就是预编译后的文件,因为我们的程序比较简单,预编译器只做了宏替换。

# 1 "Source/main.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 363 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "Source/main.c" 2


int main()
{
   int max = ((3) > (4) ? (3) : (4));
    return 0;
}

void SystemInit(void)
{

}

让我们考虑一个比这个更复杂的 main.c 程序:

#include <stdio.h>
#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))

// test
#define VERSION 1.0

#ifndef VERSION
#error "VERSION is not defined!"
#endif

int main() {
    #ifdef DEBUG
        printf("Debug mode is on.\n");
    #endif
    printf("The value of PI is: %f\n", PI);
	  int max = MAX(3, 4);
    return 0;
}

 在这里我们使用了:

包含头文件(#include)

处理 #include 指令,将指定文件的内容(标准库头文件(如<stdio.h>)或自定义头文件(如"myheader.h"))插入到当前文件的当前位置中。

#include <stdio.h>
#include "myheader.h"

宏定义与替换(#define)

处理#define指令,将宏定义的常量、函数式宏或代码片段替换为相应的结果。

#define PI 3.14159
#define MAX(a, b) ((a) > (b) ? (a) : (b))

条件编译(#if, #ifdef, #ifndef, #else, #elif, #endif)

处理条件编译指令,用于根据条件决定是否编译某段代码。例如,对于下面的代码,如果开启了DEBUG,则包含 printf() 调用语句,否则就跳过该语句。

#ifdef DEBUG
    printf("Debug mode is on.\n");
#endif

某些特殊指令(#error)

预处理器支持某些特殊指令,如#error,用于在预处理阶段生成错误信息并停止编译,或者完成其它操作。

#ifndef VERSION
#error "VERSION is not defined!"
#endif

#Pragma等指令(#pragma)

保留#pragma等指令,从而向编译器传递特定实现相关的指令。

#pragma once  // 防止头文件重复包含(非标准但广泛支持)

此时 mian.i 文件就会把所有的预编译命令进行预处理后显示在出来: 

# 1 "Source/main.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 363 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "Source/main.c" 2
# 1 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 1 3
# 53 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
    typedef unsigned int size_t;
# 68 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
    typedef __builtin_va_list __va_list;
# 87 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
typedef struct __fpos_t_struct {
    unsigned long long int __pos;





    struct {
        unsigned int __state1, __state2;
    } __mbstate;
} fpos_t;
# 108 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
typedef struct __FILE FILE;
# 119 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
struct __FILE {
    union {
        long __FILE_alignment;



        char __FILE_size[84];

    } __FILE_opaque;
};
# 138 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern FILE __stdin, __stdout, __stderr;
extern FILE *__aeabi_stdin, *__aeabi_stdout, *__aeabi_stderr;
# 224 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int remove(const char * ) __attribute__((__nonnull__(1)));







extern __attribute__((__nothrow__)) int rename(const char * , const char * ) __attribute__((__nonnull__(1,2)));
# 243 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) FILE *tmpfile(void);






extern __attribute__((__nothrow__)) char *tmpnam(char * );
# 265 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fclose(FILE * ) __attribute__((__nonnull__(1)));
# 275 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fflush(FILE * );
# 285 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) FILE *fopen(const char * __restrict ,
                           const char * __restrict ) __attribute__((__nonnull__(1,2)));
# 329 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) FILE *freopen(const char * __restrict ,
                    const char * __restrict ,
                    FILE * __restrict ) __attribute__((__nonnull__(2,3)));
# 342 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) void setbuf(FILE * __restrict ,
                    char * __restrict ) __attribute__((__nonnull__(1)));






extern __attribute__((__nothrow__)) int setvbuf(FILE * __restrict ,
                   char * __restrict ,
                   int , size_t ) __attribute__((__nonnull__(1)));
# 370 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __printf_args
extern __attribute__((__nothrow__)) int fprintf(FILE * __restrict ,
                    const char * __restrict , ...) __attribute__((__nonnull__(1,2)));
# 393 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __printf_args
extern __attribute__((__nothrow__)) int _fprintf(FILE * __restrict ,
                     const char * __restrict , ...) __attribute__((__nonnull__(1,2)));





#pragma __printf_args
extern __attribute__((__nothrow__)) int printf(const char * __restrict , ...) __attribute__((__nonnull__(1)));






#pragma __printf_args
extern __attribute__((__nothrow__)) int _printf(const char * __restrict , ...) __attribute__((__nonnull__(1)));





#pragma __printf_args
extern __attribute__((__nothrow__)) int sprintf(char * __restrict , const char * __restrict , ...) __attribute__((__nonnull__(1,2)));








#pragma __printf_args
extern __attribute__((__nothrow__)) int _sprintf(char * __restrict , const char * __restrict , ...) __attribute__((__nonnull__(1,2)));






#pragma __printf_args
extern __attribute__((__nothrow__)) int __ARM_snprintf(char * __restrict , size_t ,
                     const char * __restrict , ...) __attribute__((__nonnull__(3)));


#pragma __printf_args
extern __attribute__((__nothrow__)) int snprintf(char * __restrict , size_t ,
                     const char * __restrict , ...) __attribute__((__nonnull__(3)));
# 460 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __printf_args
extern __attribute__((__nothrow__)) int _snprintf(char * __restrict , size_t ,
                      const char * __restrict , ...) __attribute__((__nonnull__(3)));





#pragma __scanf_args
extern __attribute__((__nothrow__)) int fscanf(FILE * __restrict ,
                    const char * __restrict , ...) __attribute__((__nonnull__(1,2)));
# 503 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __scanf_args
extern __attribute__((__nothrow__)) int _fscanf(FILE * __restrict ,
                     const char * __restrict , ...) __attribute__((__nonnull__(1,2)));





#pragma __scanf_args
extern __attribute__((__nothrow__)) int scanf(const char * __restrict , ...) __attribute__((__nonnull__(1)));








#pragma __scanf_args
extern __attribute__((__nothrow__)) int _scanf(const char * __restrict , ...) __attribute__((__nonnull__(1)));





#pragma __scanf_args
extern __attribute__((__nothrow__)) int sscanf(const char * __restrict ,
                    const char * __restrict , ...) __attribute__((__nonnull__(1,2)));
# 541 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __scanf_args
extern __attribute__((__nothrow__)) int _sscanf(const char * __restrict ,
                     const char * __restrict , ...) __attribute__((__nonnull__(1,2)));







extern __attribute__((__nothrow__)) int vfscanf(FILE * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));
extern __attribute__((__nothrow__)) int vscanf(const char * __restrict , __va_list) __attribute__((__nonnull__(1)));
extern __attribute__((__nothrow__)) int vsscanf(const char * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));

extern __attribute__((__nothrow__)) int _vfscanf(FILE * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));
extern __attribute__((__nothrow__)) int _vscanf(const char * __restrict , __va_list) __attribute__((__nonnull__(1)));
extern __attribute__((__nothrow__)) int _vsscanf(const char * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));
extern __attribute__((__nothrow__)) int __ARM_vsscanf(const char * __restrict , const char * __restrict , __va_list) __attribute__((__nonnull__(1,2)));

extern __attribute__((__nothrow__)) int vprintf(const char * __restrict , __va_list ) __attribute__((__nonnull__(1)));







extern __attribute__((__nothrow__)) int _vprintf(const char * __restrict , __va_list ) __attribute__((__nonnull__(1)));





extern __attribute__((__nothrow__)) int vfprintf(FILE * __restrict ,
                    const char * __restrict , __va_list ) __attribute__((__nonnull__(1,2)));
# 584 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int vsprintf(char * __restrict ,
                     const char * __restrict , __va_list ) __attribute__((__nonnull__(1,2)));
# 594 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int __ARM_vsnprintf(char * __restrict , size_t ,
                     const char * __restrict , __va_list ) __attribute__((__nonnull__(3)));

extern __attribute__((__nothrow__)) int vsnprintf(char * __restrict , size_t ,
                     const char * __restrict , __va_list ) __attribute__((__nonnull__(3)));
# 609 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int _vsprintf(char * __restrict ,
                      const char * __restrict , __va_list ) __attribute__((__nonnull__(1,2)));





extern __attribute__((__nothrow__)) int _vfprintf(FILE * __restrict ,
                     const char * __restrict , __va_list ) __attribute__((__nonnull__(1,2)));





extern __attribute__((__nothrow__)) int _vsnprintf(char * __restrict , size_t ,
                      const char * __restrict , __va_list ) __attribute__((__nonnull__(3)));
# 635 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
#pragma __printf_args
extern __attribute__((__nothrow__)) int __ARM_asprintf(char ** , const char * __restrict , ...) __attribute__((__nonnull__(2)));
extern __attribute__((__nothrow__)) int __ARM_vasprintf(char ** , const char * __restrict , __va_list ) __attribute__((__nonnull__(2)));
# 649 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fgetc(FILE * ) __attribute__((__nonnull__(1)));
# 659 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) char *fgets(char * __restrict , int ,
                    FILE * __restrict ) __attribute__((__nonnull__(1,3)));
# 673 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fputc(int , FILE * ) __attribute__((__nonnull__(2)));
# 683 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fputs(const char * __restrict , FILE * __restrict ) __attribute__((__nonnull__(1,2)));






extern __attribute__((__nothrow__)) int getc(FILE * ) __attribute__((__nonnull__(1)));
# 704 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
    extern __attribute__((__nothrow__)) int (getchar)(void);
# 713 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) char *gets(char * ) __attribute__((__nonnull__(1)));
# 725 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int putc(int , FILE * ) __attribute__((__nonnull__(2)));
# 737 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
    extern __attribute__((__nothrow__)) int (putchar)(int );






extern __attribute__((__nothrow__)) int puts(const char * ) __attribute__((__nonnull__(1)));







extern __attribute__((__nothrow__)) int ungetc(int , FILE * ) __attribute__((__nonnull__(2)));
# 778 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) size_t fread(void * __restrict ,
                    size_t , size_t , FILE * __restrict ) __attribute__((__nonnull__(1,4)));
# 794 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) size_t __fread_bytes_avail(void * __restrict ,
                    size_t , FILE * __restrict ) __attribute__((__nonnull__(1,3)));
# 810 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) size_t fwrite(const void * __restrict ,
                    size_t , size_t , FILE * __restrict ) __attribute__((__nonnull__(1,4)));
# 822 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fgetpos(FILE * __restrict , fpos_t * __restrict ) __attribute__((__nonnull__(1,2)));
# 833 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fseek(FILE * , long int , int ) __attribute__((__nonnull__(1)));
# 850 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int fsetpos(FILE * __restrict , const fpos_t * __restrict ) __attribute__((__nonnull__(1,2)));
# 863 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) long int ftell(FILE * ) __attribute__((__nonnull__(1)));
# 877 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) void rewind(FILE * ) __attribute__((__nonnull__(1)));
# 886 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) void clearerr(FILE * ) __attribute__((__nonnull__(1)));







extern __attribute__((__nothrow__)) int feof(FILE * ) __attribute__((__nonnull__(1)));




extern __attribute__((__nothrow__)) int ferror(FILE * ) __attribute__((__nonnull__(1)));




extern __attribute__((__nothrow__)) void perror(const char * );
# 917 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) int _fisatty(FILE * ) __attribute__((__nonnull__(1)));



extern __attribute__((__nothrow__)) void __use_no_semihosting_swi(void);
extern __attribute__((__nothrow__)) void __use_no_semihosting(void);
# 2 "Source/main.c" 2
# 12 "Source/main.c"
int main() {



    printf("The value of PI is: %f\n", 3.14159);
   int max = ((3) > (4) ? (3) : (4));
    return 0;
}

 其中大部分内容都是如:

extern __attribute__((__nothrow__)) int rename(const char * , const char * ) __attribute__((__nonnull__(1,2)));
# 243 "C:\\Keil_v5\\ARM\\ARMCLANG\\Bin\\..\\include\\stdio.h" 3
extern __attribute__((__nothrow__)) FILE *tmpfile(void);

 使用 __attribute__((__nothrow__)) 命令声明 stdio.h 文件内的函数和变量等。

__attribute__((__nothrow__)) ​是GCC/Clang编译器的扩展属性,表示被修饰的函数不会抛出异常(即使函数内部有异常也会被抑制)。

其中具体的语法会在之后章节描述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值