zoukankan      html  css  js  c++  java
  • 从变量的类型转换看C语言的思维模式

    怪异的语法

    C语言的类型转换语法对大多数人是非常奇怪的,尤其是对于接触的第一门语言不是C语言的人来说。在大多数“正常”的语言中,类型转换的语法应该是这样的(这里以Python为例):

    a = 1
    b = float(a)
    

    但C语言中的类型转换是这个样子的:

    unsigned int b = 0x1812;
    unsigned char a = (unsigned char)b;
    

    不同的语言模式

    造成这种差异的原因并不是因为语法设计的不同。而是语言设计的思路不一样。Python是面向对象的语言,int和float都是内置的类,表达式 float(a) 实际上是将int类型的a作为参数,生成一个float类的实例,这种类型转换实际上是生成了一个新的对象。

    而在C语言中,是没有对象的,而且不同的类型之间也似乎没有什么明确的界限,一个变量不过是一个字节序列。C语言中,有符号整型int和无符号整型unsigned都是4个字节大小。字节序列0xffffffff,如果看成int类型的话,是-1,而看成unsigned类型则是4294967295。C语言中的printf函数可以明显地看出这一特点,printf函数并不检查参数的类型,而是根据描述符说明的类型去解释变量,如果描述的类型和实际的类型不匹配,很可能出现一些奇怪的现象。

    例如如下代码

    #include <stdio.h>
    
    int main()
    {
    	int d = -1;
    	printf("%d
    ", d);
    	printf("%u
    ", d);
    	return 0;
    }
    

    输出结果为:

    -1
    4294967295
    

    变量d本来是int类型的,字节序列为0xffffffff,用%u描述符输出时,C编译器直接将0xffffffff作为无符号整型解释,得到4294967295. 因此我们可以理解到以下的原理:

    C语言类型转换的原理

    C语言的强制类型转换中,变量的字节序列不变,只是字节的解释方式发生了改变。

    为了进一步体会这种特性,我们将程序编译后,观察编译器生成的汇编代码,进一步进行验证。

    #include <stdio.h>
    
    int main()
    {
    	int a = -4;
    	unsigned b = (unsigned)a;
    	unsigned char c = (unsigned char)b;
    	printf("%d %u
    ", a, b);
    	printf("%x %x
    ", a, c);
    	return 0;
    }
    

    输出结果如下:

    -4 4294967292
    fffffffc fc
    

    对.o文件的反汇编结果如下:

    00000000 <main>:
       0:	55                   	push   %ebp
       1:	89 e5                	mov    %esp,%ebp
       3:	83 e4 f0             	and    $0xfffffff0,%esp
       6:	83 ec 20             	sub    $0x20,%esp
       9:	c7 44 24 1c fc ff ff 	movl   $0xfffffffc,0x1c(%esp)
      10:	ff 
      11:	8b 44 24 1c          	mov    0x1c(%esp),%eax
      15:	89 44 24 18          	mov    %eax,0x18(%esp)
      19:	8b 44 24 18          	mov    0x18(%esp),%eax
      1d:	88 44 24 17          	mov    %al,0x17(%esp)
      21:	8b 44 24 18          	mov    0x18(%esp),%eax
      25:	89 44 24 08          	mov    %eax,0x8(%esp)
      29:	8b 44 24 1c          	mov    0x1c(%esp),%eax
      2d:	89 44 24 04          	mov    %eax,0x4(%esp)
      31:	c7 04 24 00 00 00 00 	movl   $0x0,(%esp)
      38:	e8 fc ff ff ff       	call   39 <main+0x39>
      3d:	0f b6 44 24 17       	movzbl 0x17(%esp),%eax
      42:	89 44 24 08          	mov    %eax,0x8(%esp)
      46:	8b 44 24 1c          	mov    0x1c(%esp),%eax
      4a:	89 44 24 04          	mov    %eax,0x4(%esp)
      4e:	c7 04 24 07 00 00 00 	movl   $0x7,(%esp)
      55:	e8 fc ff ff ff       	call   56 <main+0x56>
      5a:	b8 00 00 00 00       	mov    $0x0,%eax
      5f:	c9                   	leave  
      60:	c3                   	ret    
    

    从汇编代码中可以看出,将unsigned类型的变量赋值给unsigned char类型,不过是将unsigned类型变量的最低字节取出;将int类型转换为unsigned类型,不过是原封不动的mov.

    总结

    C语言类型转换的语法设计成在变量前加带括号的类型名,这样的语法正体现了类型转换在C语言中实际发生的情况:用新的类型来“修饰”原先的变量,将变量按照新的类型来解释,并没有产生一个新的变量。

    附:所用软件版本

    gcc: gcc (Debian 4.7.2-5) 4.7.2
    objdump: GNU objdump (GNU Binutils for Debian) 2.22

  • 相关阅读:
    bzoj 1061 单纯形法,或转化网络流(待补)
    bzoj 1007 计算几何,单调栈
    bzoj 1015 并查集,离线
    bzoj 1013 高斯消元
    java类继承HttpServlet类实现Servlet程序出现405错误:HTTP method POST is not supported by this URL
    算法的特性和算法设计的要求
    Java实现自定义异常类
    怎么查看 MySQL 数据文件在当前电脑的存储位置
    数据结构的分类
    JS实现“全选”和"全不选"功能
  • 原文地址:https://www.cnblogs.com/nettee/p/4098364.html
Copyright © 2011-2022 走看看