通过实例理解:C语言 volatile 关键字
来源:互联网 发布:java线程池下载多任务 编辑:程序博客网 时间:2024/05/18 01:03
先来看一个简单的程序,试着回答一下面这段程序的输出是什么?
#include <stdio.h>int main(){ int i = 5; int j = 6; int p = 0; int q = 0; p = (i++)+(i++)+(i++); q = (++j)+(++j)+(++j); printf("i=%d,j=%d,p=%d,q=%d \n",i,j,p,q); return 0;}
根据C 语言的规则:
i++是先赋值再计算
++i是先计算再赋值
p = (i++)+(i++)+(i++); i++是先赋值再计算,原来的式子
等价于 p = 5+6+7 即18;i++ 重复了三次 i的最终值为 8;
q = (++j)+(++j)+(++j); ++j是先赋值再计算,原来的式子
等价于 p = 7+8+9 即24;++j 重复了三次 j的最终值为 9;
于是最终的结果是:
“ i=8,j=9,p=18,q=24 ”
但是在环境上实际测试的值是:
“ i=8,j=9,p=18, q=25 ”
为什么 q 是 25?
我们深入程序的内部看一看,linux 下执行,gcc -S volatitle.c -o vo.s ,注意”-S”大写。重点看注释的部分。
.file "volatitle.c" .section .rodata.LC0: .string "i=%d,j=%d,p=%d,q=%d \n" .text .globl main .type main, @functionmain:.LFB0: .cfi_startproc leal 4(%esp), %ecx .cfi_def_cfa 1, 0 andl $-16, %esp pushl -4(%ecx) pushl %ebp .cfi_escape 0x10,0x5,0x2,0x75,0 movl %esp, %ebp pushl %ecx .cfi_escape 0xf,0x3,0x75,0x7c,0x6 subl $20, %esp movl $5, -24(%ebp) # i = 5 movl $6, -20(%ebp) # j = 6 movl $0, -16(%ebp) movl $0, -12(%ebp) movl -24(%ebp), %edx leal 1(%edx), %eax movl %eax, -24(%ebp) movl -24(%ebp), %eax leal 1(%eax), %ecx movl %ecx, -24(%ebp) leal (%edx,%eax), %ecx movl -24(%ebp), %eax leal 1(%eax), %edx movl %edx, -24(%ebp) addl %ecx, %eax movl %eax, -16(%ebp) addl $1, -20(%ebp) # ++j 即7 addl $1, -20(%ebp) # ++j 即8 movl -20(%ebp), %eax # 将j的值 存在 %eax leal (%eax,%eax), %edx #把eax+eax的值装入edx中。此时就是把8+8的值16 存在了 %edx addl $1, -20(%ebp) #++j 即9 movl -20(%ebp), %eax #将9的值存在了 %eax中 addl %edx, %eax #即 16+9=25 movl %eax, -12(%ebp) subl $12, %esp pushl -12(%ebp) pushl -16(%ebp) pushl -20(%ebp) pushl -24(%ebp) pushl $.LC0 call printf addl $32, %esp movl $0, %eax movl -4(%ebp), %ecx .cfi_def_cfa 1, 0 leave .cfi_restore 5 leal -4(%ecx), %esp .cfi_def_cfa 4, 4 ret .cfi_endproc.LFE0: .size main, .-main .ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609" .section .note.GNU-stack,"",@progbits
上面的汇编不必全部看懂,但是有些运算部分已经注释,根据注释的内容,可以看出,在计算q = (++j)1+(++j)2+(++j)3;
- 第一个(++j):计算完成之后并没有存储其值7,(优化了movl -20(%ebp), %ebx),只是使用了其在栈中的偏移地址。
- 第二个(++j):根据第一个(++j)在栈中的偏移地址:-20(%ebp) ,继续运算第二个(++j),第二次运算之后的值是8,运算完成之后,存储了栈中的值到 %eax中,这个时候才存储了j的值(movl -20(%ebp), %eax )
- 再对前面两个数进行求和运算的时候,变成了对两个%eax的运算,即为8+8=16
最后一个(++j) :对于最后一个(++j)运算的过程中,没有编译器优化
addl $1, -20(%ebp) #++j 即9
movl -20(%ebp), %eax #将9的值存在了 %eax中.最后把前两个数只和(存在%edx)和最后一个 数(存在%eax) 相加。addl %edx, %eax # 16+9=25;
在计算前两个 (++j)1+(++j)2)的时候,编译器做了一个常量合并的优化,如何才能不优化呢?
定义变量的时候,加上volatile 关键字。
#include <stdio.h>int main(){ int i = 5; volatile int j = 6; int p = 0; int q = 0; p = (i++)+(i++)+(i++); q = (++j)+(++j)+(++j); printf("i=%d,j=%d,p=%d,q=%d \n",i,j,p,q); return 0;}
加上一个 volatitle关键字后,执行的结果是
“ i=8,j=9,p=18,q=24 ”
linux 下执行,gcc -S volatitle.c -o voo.s ,注意”-S”大写,重点看注释的部分。
.file "volatitle.c" .section .rodata.LC0: .string "i=%d,j=%d,p=%d,q=%d \n" .text .globl main .type main, @functionmain:.LFB0: .cfi_startproc leal 4(%esp), %ecx .cfi_def_cfa 1, 0 andl $-16, %esp pushl -4(%ecx) pushl %ebp .cfi_escape 0x10,0x5,0x2,0x75,0 movl %esp, %ebp pushl %ecx .cfi_escape 0xf,0x3,0x75,0x7c,0x6 subl $20, %esp movl $5, -20(%ebp) # i = 5 movl $6, -24(%ebp) # j = 5 movl $0, -16(%ebp) movl $0, -12(%ebp) movl -20(%ebp), %edx leal 1(%edx), %eax movl %eax, -20(%ebp) movl -20(%ebp), %eax leal 1(%eax), %ecx movl %ecx, -20(%ebp) leal (%edx,%eax), %ecx movl -20(%ebp), %eax leal 1(%eax), %edx movl %edx, -20(%ebp) addl %ecx, %eax movl %eax, -16(%ebp) movl -24(%ebp), %eax #存j leal 1(%eax), %edx #++j,存入%edx movl %edx, -24(%ebp) #取出到 栈中 movl -24(%ebp), %eax # addl $1, %eax #++j存入%eax movl %eax, -24(%ebp) # addl %eax, %edx #计算前两个(++j)的结果,存入 %edx中。 movl -24(%ebp), %eax #存栈 addl $1, %eax #++j 存入%eax movl %eax, -24(%ebp) # addl %edx, %eax movl %eax, -12(%ebp) movl -24(%ebp), %eax subl $12, %esp pushl -12(%ebp) pushl -16(%ebp) pushl %eax pushl -20(%ebp) pushl $.LC0 call printf addl $32, %esp movl $0, %eax movl -4(%ebp), %ecx .cfi_def_cfa 1, 0 leave .cfi_restore 5 leal -4(%ecx), %esp .cfi_def_cfa 4, 4 ret .cfi_endproc.LFE0: .size main, .-main .ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609" .section .note.GNU-stack,"",@progbits
汇编中j的每一次运算都去做了存值的操作。
这是一篇实例,想看更多关于volatile的理论部分,请参考详解C中volatile关键字
- 通过实例理解:C语言 volatile 关键字
- C语言关键字volatile
- C语言volatile关键字
- C语言volatile关键字
- C语言关键字volatile
- C语言volatile关键字
- C语言关键字volatile
- C语言volatile关键字
- C语言关键字volatile
- C语言关键字Volatile
- C语言volatile关键字
- C语言volatile关键字
- C语言 volatile 关键字
- C语言关键字volatile
- C语言中对volatile关键字的理解
- 关键字volatile在C语言下的理解
- C语言 volatile 关键字讲解:
- c语言中volatile关键字
- 测试系列-CMMI 知识扫盲篇
- 算法练习(3):Median of Two Sorted Arrays
- 多线程同步机制2-深入理解synchronized关键字
- 京东笔试题 神奇数
- springboot mail简单实例
- 通过实例理解:C语言 volatile 关键字
- 网页退出弹出新网页或者收藏夹
- 9.9 7日知识实践和总结(关于JavaScript的数组操作)
- Django 开发学习笔记(4)- 关于数据 ORM
- 自己写的顺序表的基本操作,插入、删除、修改,给出完整代码,其中运用到goto语句
- IE9- 图片|遮挡|绝对|定位|元素的|解决|办法
- jQuery事件对象的作用(利用冒泡事件优化)
- C#--基于三层架构的应用实战
- 浅析互联网广告