C 内嵌汇编

← 返回 MOC

在 C 代码中直接嵌入汇编指令,用于极致性能优化、访问特殊寄存器或执行 C 无法表达的硬件操作。


基本语法(GCC 内联汇编)

GCC 使用 asm__asm__ 关键字,完整格式:

asm volatile (
    "汇编指令\n\t"
    : 输出操作数          // Output operands
    : 输入操作数          // Input operands
    : 破坏列表            // Clobber list
);
  • volatile:禁止编译器对该段汇编做优化/重排
  • \n\t:换行+缩进,保证生成的汇编文本格式正确
  • 操作数约束见下表

操作数约束

约束符含义
"r"任意通用寄存器
"m"内存操作数
"i"立即数(编译期常量)
"=r"输出到寄存器(只写)
"+r"读写同一寄存器
"0"与第 0 个操作数使用同一位置

占位符:%0 第一个操作数,%1 第二个,依此类推。


读写普通变量

#include <stdio.h>
 
int main(void) {
    int a = 5, b = 3, result;
 
    asm volatile (
        "add %1, %2, %0\n\t"   // result = a + b  (ARM 语法示例)
        : "=r"(result)          // 输出:result → %0
        : "r"(a), "r"(b)        // 输入:a → %1, b → %2
    );
 
    printf("result = %d\n", result);  // 8
    return 0;
}

x86 示例(AT&T 语法,src→dst 顺序):

asm volatile ("addl %1, %0" : "+r"(result) : "r"(b));

读写特殊寄存器(ARM Cortex-M 示例)

// 读 CONTROL 寄存器
static inline uint32_t get_control(void) {
    uint32_t val;
    asm volatile ("MRS %0, CONTROL" : "=r"(val));
    return val;
}
 
// 写 CONTROL 寄存器
static inline void set_control(uint32_t val) {
    asm volatile ("MSR CONTROL, %0" :: "r"(val));
    asm volatile ("ISB");   // 指令同步屏障,确保立即生效
}

内存屏障

// 编译器屏障:阻止编译器跨越此点重排内存访问
#define COMPILER_BARRIER() asm volatile ("" ::: "memory")
 
// ARM 数据内存屏障(DMB)
#define DMB() asm volatile ("DMB SY" ::: "memory")
 
// ARM 数据同步屏障(DSB)
#define DSB() asm volatile ("DSB SY" ::: "memory")

破坏列表中写 "memory" 告诉编译器:此处可能读写任意内存,不要缓存寄存器中的内存值。


开关全局中断(ARM Cortex-M)

static inline void disable_irq(void) {
    asm volatile ("CPSID I" ::: "memory");
}
 
static inline void enable_irq(void) {
    asm volatile ("CPSIE I" ::: "memory");
}

无操作延时(NOP)

static inline void nop(void) {
    asm volatile ("NOP");
}
 
// 循环延时(不精确,仅用于极短延时)
static inline void delay_nop(uint32_t n) {
    while (n--) {
        asm volatile ("NOP");
    }
}

KEIL / ARMCC 语法差异

KEIL 的 ARMCC/AC6 编译器使用 __asm 关键字,语法略有不同:

// ARMCC 内联汇编(不支持 GCC 约束语法)
__asm void disable_irq_keil(void) {
    CPSID I
    BX LR
}

现代 KEIL AC6 已支持 GCC 内联汇编语法(__asm volatile(...)),推荐统一用 GCC 风格。


常见坑

问题原因解决
编译器优化掉了汇编没加 volatileasm volatile
寄存器值被覆盖没在破坏列表声明在 clobber 里加寄存器名,如 "r0"
内存操作不一致编译器缓存了变量clobber 加 "memory"
ARM/x86 语法混用AT&T 与 Intel 语法不同明确目标平台,x86 默认 AT&T