当前位置:C++技术网 > 资讯 > 如何深入理解指针、地址、关系运算符?

如何深入理解指针、地址、关系运算符?

更新时间:2017-04-12 10:09:38浏览次数:1+次

1、问题描述:


#include <stdio.h>
int main (void) {
	int a = 1;
	printf("%#x\n", &a);//知道了a所表示的内存的地址为0x18ff44
	int  b = 0x18ff44;
	if (b == &a) {        //程序出错
		printf("yes\n");
	}
	return 0;
}



2、想弄清楚 b==&a 出错的原因?以及关于指针和地址更深入的理解?


3、我的自己的思考结果及疑问?

  -  关于关系运算符的问题? 考虑出错原因是数据类型不一致 ————思考结果如下:

      * 关系运算符最终得到的是逻辑值。判断是真是假比较的是关系运算符两边数据的二进制代码所表示的数据大小,所以我认          为关系运算符两边的数据类型是可以不一样的。 

  -  查找MSDN中的解释是The compiler is unable to convert type1 to type2. 难道关系运算符的判断是要先进行类型转          换? 因为 int * 不能转换为 int 所以出错了?


4、我的假设:是关系运算符编译时的解释过程中,先进行类型转换,然后再转换二进制,再进行比较?我想知道这样的理解是否正确,如果不正确,产生错误的原因又是什么呢?(是否需要学习编译原理、汇编语言弄清这个过程?)


感谢你抽时间为我解答,谢谢!


C++技术网会员解答:

    尊贵的会员您好,感谢您对C++技术网的支持与信任。
    C/C++语言是一种强类型语言,类型不同,不予后续的操作。这是一个基本原则。还有一个原则叫做变量使用前必须声明。这些原则是语言设计好的基本的公理,进而才有了各种各样的语法规则。而这些公理的存在,在学习C/C++时并不需要你去探究公理存在的依据。你只是学习语言的使用,并不需要去造一种语言。
    当然,你这种爱思考的习惯是值得肯定的。不过在学习一门语言时,不要“过思考”。这就好比太深入反而没法学习基本的使用语法了。你说关系运算符的运行机制,这可不是单单关系运算符这一个运算符的机制,这是一套语言整体设计的机制。然后,至于关系运算符是如何执行比较的,有点思考过度了。怎么说呢?
    我们语言是面对人类的语言,逻辑也是人类可理解的逻辑。比较自然也是基于人类的逻辑而设计的。所以,关系运算符自然也是基于人类的逻辑而设计。如果仅仅是考虑计算机的比较,那么什么数据类型都可以直接比较,毕竟在计算机内部全部是二进制的数据。但是这样的比较有什么意义呢?我们不要说类型转换再转换成二进制,计算机内部一直都是二进制的。
    关系运算符的比较基础就是C/C++语言设计原则的强数据类型的体现。使用所有东西之前,需要事先声明类型,比如类、函数、变量等等。没有类型,在C/C++之中是走不通的。同时,如果类型不一致,也是走不通的。如果叫我们人类来比较一只牛和一只羊的价值,我们如何比较?如果直接用牛和羊本身比较,我们无法得出结果。因为牛和羊是不一样的东西,类型不同,无法直接从量级来比较。但是如果比较一头羊和两头痒的价值,这个我们就能够比较了。这是数量级的比较,是允许的。
    那么羊和牛是如何比较的呢?货币价值是通用的价值交换等价物,其实就是统一的参考比较对象。所以我们在比较牛和羊的时候,会自动将羊和牛转换为货币价值,然后都是货币价值值了之后,就是同样的类型了。这样就可以比较了。或者我们会将牛羊转换到某一个实际的使用场景,比如耕田。那么也是一种类型,只是转化为某一个具体的部分类型,然后也是可以比较了。这就好像将浮点型转换为整型来比较大小。
    所以说,虽然我们好像直接比较了牛和羊,实际上在做隐含的类型转换。我们人类进化到现在,自然也变得高级,对于很多场景,都可以自动进行这种类型转换了,而不需要拿个纸币先将类型显式的转换过来,然后再比较。这样就显得有点低级了。
    是的,C/C++语言就是这样的基于原始的语言。而python这类语言已经进化为更高级的语言,当然C++也进化出了auto类型自动推导。但是不管是什么语言,内部实际上还是强类型的。只是对外使用来说,不要显式的声明类型了。甚至在使用类型比较时也会自动基于某一个原则来做类型转换。对于我们使用者来说,看似没有类型的说法,实际上只是语言自动处理了而已。
    而我们所说的语言的是否强类型,说的是使用语言的时候是否需要显式强调语言。至始至终,语言内部都是强类型的。
    我们再回到问题来具体解释一下。
    b==&a的比较,自然出错的原因在于类型不一致。上面已经解释了原因。编译器在比较类型之前,会检查数据类型是否兼容(包括一样),如果类型完全相同,直接比较,如果类型兼容,则按照类型兼容原则进行默认类型转换。如果转换成功,即类型兼容,那么一样会得出比较的结果。但是如果类型不兼容,那么就会报语法错误,也就是The compiler is unable to convert type1 to type2。而类型兼容的解释,在书上有很详细的解释,这个需要你详细的学习。我们推荐的书是C(C++) Primer Plus,这书上是有解释的,其他书不敢保证。举个例子,char、short、int、long、long long是同一种类型,int、double就是兼容类型。前者都是整数,后者都是数字。int和double的比较则会向上提升类型,让int转成double,再进行比较。这里的原则就是尽可能确保数据精度和准确度高。
    所以,如果默认的处理规则不满足你的要求,你就需要使用强制类型转换。比如将double转为int,但是这个就存在了风险,会丢失数据。
    而如果数据类型完全不一致,比如int和bool,是两种不同的类型,那是不兼容的。所以默认不会做任何处理,也就是默认尝试的类型转换会失败,然后提示The compiler is unable to convert type1 to type2。这表明编译器在卖力为你做自动转换,但是失败了。
    在C/C++语言中,地址是一类类型,和整型时不一样的。表面上看上去都是一个整数值,看上去一样。但是你却忽略了很重要的背景。地址类型是有绝对的范围的。地址是不能超过内存大小的。超出的数值是无效的,因为没有对应的内存。另外,内存地址是操作系统严格管控的,涉及到系统和程序的安全问题,内存越界、内存溢出都可能造成系统的崩溃。而整型类型只是做数据计算,随便折腾都行,不会威胁系统安全。你现在还觉得地址类型会和整型类型一样吗?这也是系统为什么一定要将地址作为一种类型的原因了。当然不完全完整,但至少这是其中的原因之一。
    我已经超出你的问题本身,讲了很多深入的背景和语言方面的问题,当然你这个问题自然也就迎刃而解了。我再给你一点建议,刚学习语言的时候,不要太深究语言机制的实现,你最多只能做逻辑上的设计原则的推测。比如上面解释的原则,你在学习具体的语法是可以大胆推测。你要知道,每一个语法都是精心设计出来的。其实每一个或一系列语法规则都蕴含了一种思想来里面。通过大量的学习,完整的学习,慢慢来体会这个思想。所以语法规则的规定都是一种思想的体现。至于计算机内部的具体实现,没必要在学习语言时去假设,意义不大。如果你确实对语言的实现有兴趣,等你把语言完整的学完了之后,再深入学习编译原理。如果只是学习语言的使用,也就是我们现在说的学习编程语言,学好语法就够了。
    你的爱思考的习惯是很好的,请继续保持,这是程序员必备的职业素养。但是也得记得进退有度,以免陷入死胡同。充分认真的学习语法,其实也是对语言设计大师的一种尊重,最这些思想的尊重,不能随意去践踏语法规则的设计。当你把语法规则学好了,用灵活了,你才会越来越深刻的体会语法设计的思想,而不是说看一两本书去介绍这个思想。思想都在语法规则里了。用业内语句名言来讲,“代码之前了无秘密”,那么对于设计思想来讲,“语法规则之前设计思想也是了无秘密的”,只要你用心去体会就知道,不需要别人的推测。