通过反汇编分析C语言中volatile关键字的含义

本文通过使用VC2010和GCC4.4.7编译器,分析了volatile关键字如何影响C语言代码的汇编生成,特别关注了其在优化和内存读取上的区别。同时,解释了volatile变量的读写顺序如何被编译器保留,并讨论了其与memory barrier的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

根据C语言标准,volatile关键字的作用是禁止编译器对相关变量的存取进行优化。本文利用VC 2010和GCC 4.4.7,分析volatile关键字对生成的汇编代码的影响,以验证volatile的具体含义。

VC 2010

以下是基础C代码

int gMark = 1;

int _tmain(int argc, _TCHAR* argv[])  
{  
	while(gMark){
	}
	return 0;
}  
以下是VC 2010 Releae编译后的汇编代码,可以看到循环直接被被优化掉了。

00A51000  jmp         wmain (0A51000h) 

如果把gMark声明为volatile int,再次VC 2010 Releae编译,生成的汇编代码如下,可以看到每次循环都要重新读取一次gMark所在的内存。

01071000 mov         eax,dword ptr [gMark(1073018h)]

01071005 test        eax,eax 

01071007  jne         wmain (1071000h)

GCC 4.4.7

以下是基础C代码

int gMark = 1;
int main()
{
  while(gMark){
  }
  return 0;
}
以下是g++ -O3编译后的反汇编代码,可以看到循环直接被被优化掉了,与VC完全一样。

Dump of assembler code forfunction main:

   0x0000000000400680 <+0>:  jmp   0x400680 <main>

Endof assembler dump.

如果把gMark声明为volatile int,g++ -O3编译后的汇编代码如下。可以看到每次循环都要重新读取一次gMark所在的内存,这点与VC完全一致。

Dumpof assembler code for function main:

   0x0000000000400680 <+0>:  movl  $0x1,-0x4(%rsp)

   0x0000000000400688 <+8>:  nopl  0x0(%rax,%rax,1)

   0x0000000000400690 <+16>: mov   -0x4(%rsp),%eax

   0x0000000000400694 <+20>: test  %eax,%eax

   0x0000000000400696 <+22>: jne   0x400690 <main+16>

   0x0000000000400698 <+24>: repz retq

Endof assembler dump.

结论

如果不加volatile关键字,编译器对C代码中的多次读取该变量的操作,有可能只生成一次加载内存到寄存器的指令。如果加上volatile,则每次C源码中用到该变量,都需要从内存中重新加载。

其它

以上基本说明了volatile的含义。另外,如果C代码的函数中有多处对volatile变量的读写,编译器还必须在生成的汇编代码中保留这些volatile变量的读写顺序,见wiki(http://en.wikipedia.org/wiki/Memory_barrier)。

还有一个经常混淆的问题是,volatile是否还定义了原子语义?用volatile修饰变量,能不能保证对一个变量的读写操作不会被其他线程打断?我们说根据C标准,volatile是没有原子语义的。另外,刚才生成的汇编代码也说明了volatile变量的读写是没有原子性的。

最后是volatile和memory barrier到底是什么关系。我的理解是,volatile是对编译器优化进行干预,memory barrier是对CPU的乱序执行进行干预,因此两者没有关系。但是有些编译器对volatile变量赋予了acquire和release语义,从而使得volatile能有一定的memory barrier的作用。详细见http://msdn.microsoft.com/zh-cn/library/ms686355。因为这和编译器以及编译器的版本是密切相关的,对于开发应用程序的程序员,就当它不存在好了。


<think>好的,我现在需要处理用户关于C语言与ARM汇编结合使用的示例或教程的请求。用户提到了四个引用,其中涉及C调用汇编、内嵌汇编、字符串拷贝示例以及反汇编分析。用户希望得到主程序的示例或教程,因此我需要整理这些引用中的信息,并提供一个清晰的步骤说明。 首先,引用[1]提到在ARM平台下,C程序和汇编程序可以通过遵守参数传递规则相互调用,例如在C中调用汇编子程序。引用[3]和[4]讨论了内嵌汇编的方法和反汇编分析,而引用[2]提供了一个字符串拷贝的示例。用户的请求需要结合这些内容,给出具体的示例。 我应该先介绍如何在C程序中调用ARM汇编函数,需要说明参数传递规则(如使用r0-r3寄存器传递前四个参数),然后给出一个简单的示例,比如加法函数。接着,可能需要展示如何在内嵌汇编中实现同样的功能,如使用__asm__关键字。然后,引用[2]中的字符串拷贝示例可以作为另一个例子,展示更复杂的应用。 需要注意代码的正确性,比如在汇编代码中保存和恢复寄存器,使用正确的指令(如bx lr返回)。此外,要确保示例中的语法符合ARM汇编的规范,比如使用.global声明导出符号,正确的函数调用约定。 用户可能需要了解编译和链接的方法,所以需要提到使用arm-linux-gcc等交叉编译工具进行编译。同时,内嵌汇编部分需要解释如何将C变量与汇编操作数关联,使用输入输出约束,如%0、%1等。 最后,生成相关问题时要覆盖用户可能感兴趣的扩展内容,比如参数传递规则、内嵌汇编的优势、混合编程的应用场景,以及调试方法。确保问题与用户提供的引用内容相关,并引导进一步学习。</think>### C语言与ARM汇编混合编程示例教程 #### 一、C语言调用ARM汇编子程序 在ARM架构中,C语言与汇编的交互遵循AAPCS标准(ARM架构过程调用标准)。以下是一个C主程序调用汇编函数的完整示例: 1. **C语言主程序 (main.c)** ```c #include <stdio.h> // 声明汇编函数 extern int add_asm(int a, int b); int main() { int result = add_asm(5, 3); printf("5 + 3 = %d\n", result); // 输出计算结果[^2] return 0; } ``` 2. **ARM汇编函数 (add.s)** ```armasm .global add_asm @ 声明全局符号 .text add_asm: add r0, r0, r1 @ 参数通过r0/r1传递,结果存入r0返回 bx lr @ 函数返回 ``` *参数传递规则*:前4个参数通过`r0-r3`传递,返回值存储在`r0`中[^1]。 3. **编译命令** ```bash arm-linux-gcc -o program main.c add.s ``` #### 二、内嵌汇编实现相同功能 使用GCC内嵌汇编语法实现加法运算: ```c int add_inline(int a, int b) { int result; __asm__ volatile ( "add %0, %1, %2" : "=r"(result) // 输出操作数 : "r"(a), "r"(b) // 输入操作数 ); return result; } ``` *特性*:编译器自动处理寄存器分配,支持C变量直接操作[^3]。 #### 三、字符串拷贝示例(汇编函数) 引用[2]中的字符串操作扩展实现: 1. **汇编实现 (strcopy.s)** ```armasm .global strcopy_asm strcopy_asm: ldrb r2, [r1], #1 @ 从源地址加载字节 strb r2, [r0], #1 @ 存储到目标地址 cmp r2, #0 @ 检查字符串结束符 bne strcopy_asm bx lr ``` 2. **C语言调用** ```c extern void strcopy_asm(char *dst, char *src); int main() { char src[] = "Hello ARM!"; char dst[20]; strcopy_asm(dst, src); printf("Copied: %s\n", dst); return 0; } ``` #### 四、混合编程调试技巧 1. 使用`objdump -d`查看反汇编代码 2. 通过`__attribute__((noinline))`防止函数被优化 3. 使用`-g`编译选项保留调试信息[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值