很多文章上说int类型是最高效的类型,cpu处理这种类型要比其它类型快,比如要比处理char类型快,原因是什么,原因就是他们学过一本国人的教科书,教科书上就这么写的,所以人们就记住了,然而事实为何如此呢?事实上,所谓int比char高效会涉及到很多硬件知识,比如数据总线或者地址总线,比如内存的对齐访问等等。如果一个有心人(比如我)将下面的代码进行了反汇编,那么一步一步的做,最后就会得到一个不一定的答案,或者说是相反的答案,首先看一对C代码,前者是用int类型和0比较,后者是用char和0比较:

int comparetozero_int(int a)

{

if (a == 0)

{

return 0;

}

return 1;

}

char comparetozero_nonint(char c)

{

if ( c == 0 )

{

return 0;

}

return 1;

}

下面用objdump将其反汇编,在windows下用vc调试的话直接进入调试状态然后转到反汇编就可以了,下面看linux下的objdump的结果:

:

push %ebp

mov %esp,%ebp

sub $0x4,%esp

cmpl $0x0,0x8(%ebp)

jne 8048399

movl $0x0,0xfffffffc(%ebp)

jmp 80483a0

movl $0x1,0xfffffffc(%ebp)

mov 0xfffffffc(%ebp),%eax

leave

ret

上面的int类型的比较就不多说了,由于是4个字节的,因此很简单,真的很简单,但是comparetozero_nonint呢,看来就不是那么假单了,先看看再说:

:

push %ebp

mov %esp,%ebp

sub $0x8,%esp

mov 0x8(%ebp),%eax

mov %al,0xfffffffc(%ebp)

cmpb $0x0,0xfffffffc(%ebp)

jne 80483c0

movl $0x0,0xfffffff8(%ebp)

jmp 80483c7

movl $0x1,0xfffffff8(%ebp)

mov 0xfffffff8(%ebp),%eax

leave

ret

从反汇编中可以看到,并没有像比较int类型时那样在栈中直接比较函数的参数和0,而是先将这个char类型的数据搬运到了局部变量中,然后再比较,可是这是为什么呢?既然有cmpb指令,那么为何不能直接定位到栈中的参数,然后直接比较呢?带着怀疑我写下了下面的汇编函数,这些汇编函数就是直接比较栈中数据的版本:

int comparetozero_nonint(char c)

{

asm volatile("cmpb $0x0,0x8(%%ebp)/n/t"

"jne 1f/n"

"movl $47,%%eax/n"

"jmp 2f/n"

"1:/t"

"movl $32,%%eax/n"

"2:/t"::);

}

经过试验证实这种方式完全可行,那么为何gcc或者vc编译器不将代码直接编译成这样呢?原来这是c语言规范在起着作用,c语言规定了一个类型转换体系,这里就不说了,该体系中有强制转换和自动转换,像char这种类型就是强制转换,就是说两个char参与运算,编译器都要将之转换为int来计算,这主要是在多个处理器中取个交集,就是说该规范照顾了所有的处理器,为何要照顾?很多cpu不能正常处理没有4字节对齐的数据,或者说它们的地址总线的低两位不是总是有效,对于这类处理器,强制转换是为了最少化运行时异常,c语言的规范当然要在标准规范集合中照顾到这种情况,然而这仅仅保证了编译后程序的正确性和稳定性,那么高效性从何而来呢?这就是优化的作用,刚才提到规范是一种共性上的折中,这里的优化当然就是个性的尽情发挥了,比如在x86处理器上没有地址总线的限制,这类处理器上可以说没有什么限制,甚至不要求数据必须对齐,因此在x86机器上编译程序时设置了O1优化,那么代码就会成为下面的样子:

:

push %ebp

mov %esp,%ebp

cmpl $0x0,0x8(%ebp)

setne %al

movzbl %al,%eax

pop %ebp

ret

:

push %ebp

mov %esp,%ebp

cmpb $0x0,0x8(%ebp)

setne %al

movzbl %al,%eax

pop %ebp

ret

由此可见,c语言的规范仅仅是共性的东西,这些规范仅仅是一种设计原则,而不是什么必须的东西,其实本来就没有什么东西是必须的,只有最适合的,而优化后的代码就是最适合的,当然前提是正确性,在x86上,不一定int的效率是最高的,由于x86是cisc的架构,因此可以直接支持很多长度的数据类型的操作,单纯从代码考虑,char不比int差。最后重申一遍,c语言的类型转换仅仅是约定,而优化可以违背这种约定,看看x86机器上的又一个例子,看一个函数:

char abc()

{

char a = 'a',b = 'b';

char c = a+b;

return c;

}

下面两种编译都是正确的:

:

push %ebp

mov %esp,%ebp

sub $0x10,%esp

movb $0x61,0xfffffffd(%ebp)

movb $0x62,0xfffffffe(%ebp)

movzbl 0xfffffffd(%ebp),%edx

movzbl 0xfffffffe(%ebp),%eax

add %edx,%eax

mov %al,0xffffffff(%ebp)

movsbl 0xffffffff(%ebp),%eax

leave

ret

:

push %ebp

mov %esp,%ebp

sub $0x10,%esp

movb $0x61,0xfffffffd(%ebp)

movb $0x62,0xfffffffe(%ebp)

movzbl 0xfffffffd(%ebp),%edx

movzbl 0xfffffffe(%ebp),%eax

lea (%edx,%eax,1),%eax

mov %al,0xffffffff(%ebp)

movsbl 0xffffffff(%ebp),%eax

leave

ret

转载于:https://blog.51cto.com/dog250/1274100

x86的cpu处理int类型并不是处理char高效多少相关推荐

  1. 4.1 int类型介绍

    C语言学习栏目目录 目录 4.1 int类型介绍 4.2 char类型介绍 4.3 float.double类型介绍 4.4 小结及其他数据类型简单介绍 4.5 类型大小 C语言提供了许多整数类型,为 ...

  2. 赠书 | 读懂 x86 架构 CPU 虚拟化,看这文就够了

    作者 | 王柏生.谢广军 导读:本文摘自于王柏生.谢广军撰写的<深度探索Linux系统虚拟化:原理与实现>一书,介绍了CPU虚拟化的基本概念,探讨了x86架构在虚拟化时面临的障碍,以及为支 ...

  3. 读懂 x86 架构 CPU 虚拟化,看这文就够了 | 赠书

    作者 | 王柏生.谢广军 导读:本文摘自于王柏生.谢广军撰写的<深度探索Linux系统虚拟化:原理与实现>一书,介绍了CPU虚拟化的基本概念,探讨了x86架构在虚拟化时面临的障碍,以及为支 ...

  4. int类型以及指针的类型所占字节的大小,到底是由什么决定的?

    int类型的大小仅仅是由 机器 的字长决定的,还是与机器的字长以及编译器都有关?     sizeof(int)=??         指针的类型是由什么决定的呢?     sizeof(Type   ...

  5. Scala语言学习-02-实现十进制转换为二进制(Int类型数据)

    一.测试环境 名称 版本 操作系统 Red Hat Enterprise Linux Server release 7.9 (Maipo) CPU 12th Gen Intel® Core™ i7-1 ...

  6. X86系列CPU标准寄存器

    title: X86系列CPU标准寄存器 tags: 计算机组成原理   版权声明:本文章参考了唐朔飞的< 计算机组成原理>未经作者允许,严禁用于商业出版,否则追究法律责任.网络转载请注明 ...

  7. 为何 java 中 int 类型的取值范围是 [-2147483648, 2147483647]

    在 jdk 源代码时, 在 Integer 包装类中,可以看到定义了两个静态变量 /*** A constant holding the minimum value an {@code int} ca ...

  8. python 把int类型转bytes以及把bytes 转int 类型(使用方法to_bytes ,from_byte, struct)

    把int类型转bytes 方法1 使用方法to_bytes to_bytes 方法里面有3个参数 , 第一个数是指定要转换的bytes占多少个字节 第二个是byteorder 是指定大端或者是小端 的 ...

  9. getchar返回int类型

    #include <stdio.h> /* copy input to output; 2nd version */ main() { int c; c = getchar(); whil ...

最新文章

  1. Esper 20章 优化
  2. 为什么对象字面量没有名字?
  3. Apache commons lang3包ArrayUtils工具使用
  4. 电脑看书软件_能全平台阅读的图书软件,是kindle? No!大公司低调出品
  5. vector删除数据时有什么需要注意的吗 ?
  6. 机械秒表的使用方法_让console.log()不再是你的唯一选项js日志输出6种方法
  7. 用python生成云词汇_用python生成词云wordcloud
  8. 安装redis并开启_如何安装Redis,以及对Redis配置文件的更改和测试
  9. ue4小白人骨骼定义_【Blender】用SkinModifier+骨骼顶点“灵活”快速创建雕刻需要用的基本人物模型...
  10. iotop iostat_适用于SQL Server DBA的有用的Linux命令– iotop和iostat
  11. 单链表的结构体定义与声明
  12. Linux中启动Steam报错libGL error的解决办法
  13. 用数学语言说我爱你怎么说_你会说我的语言吗
  14. 奇妙的数学:蓝眼睛岛和强弱共识
  15. 自建 KMS 激活服务器
  16. 【金钱开道】直捣黄龙!
  17. 扬帆志远教育:对跨境电商商业模式解读
  18. Android中的自定义View(一)
  19. 区块链支付和第三方支付区别
  20. 游戏推广免费的cps模式和游戏加盟平台选择的一个误区。

热门文章

  1. Day20 Ajax
  2. 小蚂蚁学习数据结构(4)——线性结构——线性表的链式表示和实现(下)
  3. Unit24 What's on TV tonight?
  4. Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效
  5. 从Varchar转换为 datetime
  6. 区块链教程Fabric1.0源代码分析Tx(Transaction 交易)二
  7. springboot 日志问题记录
  8. ASP.NET Core 中文文档 第二章 指南(4.4)添加 Model
  9. echop红包发放规则添加
  10. NMSE考试常见问题