扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

c语言的循环是如何用汇编代码来实现的?

03/13 10:31
240
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

本文通过一个基于arm裸机开发工程的简单实例给大家讲解,c语言的循环代码最终翻译成什么样的arm汇编代码。

测试环境参考下面文章:

《linux驱动、ARM学习环境搭建

《手把手教你分析C语言if架构代码最终如何用arm汇编实现》

1、c代码

废话不多说,直接上c代码,这个代码非常简单,就不多说了。

  1 /*                                                                          
  2  * main.c
  3  *
  4  *  Created on: 2025-11-26
  5  *  Author: pengdan
  6  */
  7 
  8 int main(void)
  9 {
 10     int i = 1;
 11     int sum = 0;
 12 
 13     for(i=0;i<=100;i++)
 14     {
 15         sum+=i;
 16     }
 17 
 18     return 0;
 19 }

2、编译代码

最终编译城的程序gcd.bin就可以烧录到arm板子上运行,

为方便理解,我们可以用arm-linux-gnueabihf-objdump去掉程序中符号信息,生成程序gcd.dis

gcd.dis

  1 
  2 gcd.elf:     file format elf32-littlearm               
  3 
  4 
  5 Disassembly of section .text:
  6 
  7 40008000 <_start>:
  8 40008000:   e3a0d207    mov sp, #1879048192 ; 0x70000000
  9 40008004:   ea00000d    b   40008040 <__main_from_arm>
 10 
 11 40008008 <main>:
 12 40008008:   b480        push    {r7}
 13 4000800a:   b083        sub sp, #12
 14 4000800c:   af00        add r7, sp, #0
 15 4000800e:   2300        movs    r3, #0
 16 40008010:   607b        str r3, [r7, #4]
 17 40008012:   2300        movs    r3, #0
 18 40008014:   603b        str r3, [r7, #0]
 19 40008016:   2300        movs    r3, #0
 20 40008018:   607b        str r3, [r7, #4]
 21 4000801a:   e006        b.n 4000802a <main+0x22>
 22 4000801c:   683a        ldr r2, [r7, #0]
 23 4000801e:   687b        ldr r3, [r7, #4]
 24 40008020:   4413        add r3, r2
 25 40008022:   603b        str r3, [r7, #0]
 26 40008024:   687b        ldr r3, [r7, #4]
 27 40008026:   3301        adds    r3, #1
 28 40008028:   607b        str r3, [r7, #4]
 29 4000802a:   687b        ldr r3, [r7, #4]
 30 4000802c:   2b64        cmp r3, #100    ; 0x64
 31 4000802e:   ddf5        ble.n   4000801c <main+0x14>
 32 40008030:   2300        movs    r3, #0
 33 40008032:   4618        mov r0, r3
 34 40008034:   370c        adds    r7, #12
 35 40008036:   46bd        mov sp, r7
 36 40008038:   f85d 7b04   ldr.w   r7, [sp], #4
 37 4000803c:   4770        bx  lr
 38     ...

3、 汇编代码分析

1)变量压栈

 11 40008008 <main>:
 12 40008008:   b480        push    {r7}
 13 4000800a:   b085        sub sp, #12
 14 4000800c:   af00        add r7, sp, #0

这几行代码,为我们在栈顶预留了12字节的空间,r7指向分配的栈顶。

并且为变量:i、sum分配了对应的栈空间,对应的栈地址如下:

分配过程参考下图:

2)for循环代码块

for循环代码,对应的汇编代码块,详见下图:

从上图可以得出,if代码块翻译成汇编代码的思路就是,

19-20行:初始化变量i值为0:将寄存器写入值0,然后将该值压入栈0x04位置,

22-23行:从栈中分别取出i,sum存入r2、r3

24行     :将i的值累加到sum:r2与r3相加结果存入r3

25行     :将sum写入栈中:将r3压栈

26-27行:i++  :从栈中i值到r3,然后将1累加到寄存器r3

28-29 行:将i的值写入到栈,然后再读出到r3

30行    :将i值和100比较,cmp指令会影响cpsr的状态位

31行   :通过b指令,判断cpsr中的状态位,如果小于等于(ble),则跳转到0x4000801c位置,否则往下执行

完整分析如下:

4、其他编译文件

gcd.s

.text
.global _start
_start:
  ldr  sp,=0x70000000         /*get stack top pointer*/
  b  main

Makefile

TARGET=gcd
TARGETC=main
all:
 arm-linux-gnueabihf-gcc   -lto -g -c -o $(TARGETC).o  $(TARGETC).c
 arm-linux-gnueabihf-gcc   -lto -g -c -o $(TARGET).o $(TARGET).s
 arm-linux-gnueabihf-gcc   -lto -g -S -o $(TARGETC).s  $(TARGETC).c
 arm-linux-gnueabihf-ld   $(TARGETC).o    $(TARGET).o -Tmap.lds  -o  $(TARGET).elf
 arm-linux-gnueabihf-objcopy -O binary -S $(TARGET).elf $(TARGET).bin
 arm-linux-gnueabihf-objdump -D $(TARGET).elf > $(TARGET).dis
clean:
 rm -rf *.o *.elf *.dis *.bin

map.lds文件

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
 . = 0x40008000;
 . = ALIGN(4);
 .text      :
 {
  gcd.o(.text)
  *(.text)
 }
 . = ALIGN(4);
    .rodata : 
 { *(.rodata) }
    . = ALIGN(4);
    .data : 
 { *(.data) }
    . = ALIGN(4);
    .bss :
     { *(.bss) }
}

更多嵌入式知识,请加彭老师好友:yikoupeng

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

公众号『一口Linux』号主彭老师,拥有15年嵌入式开发经验和培训经验。曾任职ZTE,某研究所,华清远见教学总监。拥有多篇网络协议相关专利和软件著作。精通计算机网络、Linux系统编程、ARM、Linux驱动、龙芯、物联网。原创内容基本从实际项目出发,保持原理+实践风格,适合Linux驱动新手入门和技术进阶。