您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业财务 > 《深入BREW开发》分章节版第2章软件基础
《深入BREW开发》——第二章软件基础版权所有,未经本作者同意不得转载,否则视为侵权行为,保留追究责任的权力。第二章软件基础我们正在向我们的软件王国进发,千万别急,在这条路上“枯燥”是我们最大的敌人,不知有多少人在它的面前臣服,但愿您不是其中之一。或许您觉得应该获得一些鼓励,写一些代码,能够看见一些诸如“Hello,World!”之类的信息。非常幸运,从这里开始您将能够看见它们了,我会将部分内容使用源程序的方式向您讲解。在这本书里,我将使用VisualStudio.Net2003的开发环境来执行这些测试程序。建立测试工程的步骤如下:1、首先打开VisualStudio.Net2003开发环境,选择新建项目。如图2.1,选择项目类型VisualC++项目/Win32控制台项目,选择路径,填写测试程序名称是Test1,点击确定按钮。2、在Win32应用程序向导中接受默认设置,点击完成按钮。如图2.2。图2.1建立测试程序这样就完成了一个测试用的应用程序,在以后的测试程序中我将不再重复这个步骤。由于我们主要是在C语言的基础上讲解,因此在这里创建的是Win32控制台应用程序,它使用Windows的命令窗口显示输出结果。不过请注意,虽然它使用Windows的命令窗口,但是它是一个Win32的应用程序,而不是DOS应用程序。在Windows环境下,应用程序分为控制台(Console)应用程序和窗口应用程序。控制台应用程序不显示窗口,其表现形式类似于DOS环境下的应用程序,但是由于它可以调用Windows的API来实现,所以它是一个Windows的应用程序而不是DOS应用程序。同时控制台应用程序可以使用标准C/C++的库,这样Windows下的控制台应用程序就和DOS环境下的C/C++十分的相似了。在这一部分我们将通过实例来演示一些C语言中较比令人“眩晕”的话题,期望能够通过这些实例让您弄明白这些问题的本质。由于本书是建立在您已经有一定的C语言基础之上的,因此主要是针对C语言中一些较难理解的概念进行讲解,毕竟我们不是一本专门讲解C语言的书籍。在本部分主要讲解的内容是指针、结构体、预处理和函数,因为在我们接下来的行程中必须要充分的理解它们才能继续前进。图2.2Win32应用程序向导2.1重温C语言的指针指针是一个精灵,以至于在我们刚刚接触它的时候有点不知所措,甚至有些人怀着敬畏的心情而决心远离它!不过,当我们掌握了它的时候,就会发现它能让我们随心所欲。尽管我们仍然面临着使用不当所带来的巨大风险,但是我还是会义无反顾的告诉您——一定要使用它。之所以我会在这里向您发出这样的号召,绝对不是因为我对指针的个人情感,我和指针也是非亲非故,只不过是因为透过它我们不但可以编写出灵活的程序,而且可以窥探到程序的真正世界——二进制世界的秘密。这就让我们开始拥抱它吧!2.1.1指针的本质指针的本质是存储它所指向存储空间地址的变量,下面将通过一个测试程序来开始征服它的旅程。打开测试工程Test1,在Test1.cpp中添加如下代码:#includestdafx.hint_tmain(intargc,_TCHAR*argv[]){int*pointer;intnNumber=100;pointer=&nNumber;printf(&pointer=0x%xpointer=0x%x*pointer=%d\n,&pointer,pointer,*pointer);return0;}在这段代码中,我们定义了一个int型指针pointer和一个int变量nNumber,然后让pointer指向nNumber。编译、运行生成可执行文件,可以看到输出的&pointer、pointer和*pointer的值如下:&pointer=0x12fed4pointer=0x12fec8*pointer=100*pointer=100是我们知道的,也就是指针指向的内存地址的内容,我们在程序中将pointer指针指向nNumber的地址,那么与nNumber的值相等就不足为奇了。pointer=1244872这个值就是指针指向的内存地址,是当前函数运行中存储nNumber的地址。&pointer就是这个指针变量的地址,由于我们定义了一个指针变量,因此,在函数运行时将会为这个变量分配一个存储空间,因此这个值是有意义的,而且它与pointer的值十分相近。我们可以这样理解指针,它是一个变量(因为指针也需要空间存储它所指向的地址),这个变量的值就是一个内存空间的地址。示意图如下:100??0x12fec80x12fec8??0x12fed4pointernNumber图2.3指针示意图在上面的代码中,&pointer是一个指向指针的指针,这样的称呼实在是过于繁琐了,我们就称它为“二重指针”吧。顺延的,如果是指向(指针的指针)的指针我们就叫它“三重指针”。在C语言中,二重指针是一个非常有用的东西,很多高阶的C语言应用都会使用到它。二重指针的主要作用是做为参数为一个指针变量赋值,在后面的章节中我们还会经常使用到它。对上面的图2.3作一个说明,pointer和nNumber分别位于两个不同的内存区域,nNumber中存储的值是100,这是程序中指定的;pointer内存储的值是0x12fec8,这个值正好是nNumber所在的内存地址;图左边的0x12fed4是存储pointer值的指针变量的地址,我们可以通过定义一个二重指针获得它的内容,在本程序中我们通过&pointer来获得它的内容。从本质上来说,指针、二重指针、三重指针等等,都是一样的,从代码的层次(我们将在“编译器基础”部分详细讲解软件的层次问题)来讲都是一个“地址”型的变量,只不过使用的时候会由编译器做一下合法性的检查,目的是为了防止程序出现错误;在二进制层次来说它们就更加没有分别了,都是一个存储内容的容器。打个比方,现在有两个盒子,一个规定放置篮球,另一个规定放置足球。体现在C语言中这个“规定”就是定义了两个变量,一个是int型的,另一个是指针型的;这两个盒子就是两块存储空间。在现实生活中“规定”是由人来制定的,在程序中就是由C语言定义的。那么我违反规定放置篮球的盒子我放置足球,放置足球的盒子我放置篮球。这是没什么问题的,不过会发生错误,因为会让一场足球比赛变成了踢篮球的比赛,让一场篮球比赛变成了足球投篮的比赛。同样的对于指针和变量的关系也是,它们的内容在二进制层次是可以互换的。但是我们在这里要考虑两件事情,一是这个错误发生的前提条件,这个错误要发生必须是在球的管理者不知道错的情况下。如果球的管理者知道哪个盒子放了篮球哪个盒子放了足球也不会出错。体现在程序上,就是增加强制类型转换来告诉编译器“我知道我要在int变量中放置一个指针的值”。否则编译器将检查到这个错误并报错。二是要考虑“盒子”容量的问题,因为足球比篮球小,所以互换的时候不能让篮球撑坏了足球盒子。在程序中也就是32位的指针变量不能放到char型变量中去存储,因为这样会丢掉地址信息。综上所述,虽然变量的二进制本质是一样的,但是在代码层次要精确控制变量的类型来避免错误的发生。指针也是一个变量,一个32位的指针变量也可以存储在一个4字节的无符号整型变量里,前提是我们要知道我们是采用这种方式来存储它们的。最后使用一个例子来做个演示,新建工程Test2,输入如下代码:#includestdafx.hint_tmain(intargc,_TCHAR*argv[]){intnNumber=100;int*pointer=&nNumber;unsignedintdwBox;unsignedshortwBox;//将指针的值分别赋给无符号整型变量dwBox=(unsignedint)pointer;wBox=(unsignedshort)pointer;//地址内容将被裁减!!!//输出Box变量的值,注意wBox与dwBox之间的不同printf(wBox=0x%xdwBox=0x%x\n,wBox,dwBox);//输出Box变量指向地址的值,其中*((int*)dwBox)相当于*pointer//此时不能够使用*((int*)wBox)输出值,因为它的地址是经过裁减的printf(*dwBox=%d\n,*((int*)dwBox));return0;}编译后运行,输出如下结果:wBox=0xfed4dwBox=0x12fed4*dwBox=100请仔细体会上面的代码,它真正揭示了变量与指针之间微妙的关系,还有类型转换时所发生的数据裁减。2.1.2指针的增减关于指针的增减是一个比较容易让人迷惑的问题,指针本身也是一个变量,它里面存储的是一个地址,那么这个变量的自增是将这个地址值加1吗?自增操作和直接加1的操作是一样的吗?为了弄清这个迷惑,这里我们在Test2工程中增加一个Test2-1的项目(请设置Test2-1为启动项目),然后输入如下代码:#includestdafx.htypedefstruct_Test2{charTest2[100];}Test2;int_tmain(intargc,_TCHAR*argv[]){intnValue=1;charcValue='A';Test2sValue;int*intPtr=&nValue;char*charPtr=&cValue;Test2*structPtr=&sValue;printf(Int%dChar%dStruct%d\n,(int)intPtr,(int)charPtr,(int)structPtr);intPtr++;charPtr++;structPtr++;printf(Int%dChar%dStruct%d\n,(int)intPtr,(int)charPtr,(int)structPtr);intPtr+=1;charPtr+=1;structPtr+=1;printf(Int%dChar%dStruct%d\n,(int)intPtr,(int)charPtr,(int)structPtr);return0;}编译运行输出的结果如下:Int1244884Char1244875Struct1244764Int1244888Char1244876Struct1244864Int1244892Char1244877Struct1244964从这个结果中,我们可以看出,指针的加减是与指针所指的变量类型有关的,它所增加的步数是所指变量的大小,而且指针的+1和++操作是一样的。那么void型的没有类型的指针怎么处理呢?答案是不处理,编译器会通知我们“未知大小”的错误而终止程序的生成。2.2重温C语言的结构结构是C语言中组织不同数据类型的一种方式,它将不同的数据类型组织到一个相邻的地址空间内。每个定义的结构体变量就是一个多种数据的存储空间。在这里我们主要讲述几个相对“高阶”问题,之所以在这里加上引号,是因为对于某些传说中的高手来说,这不过是小菜一碟。2.2.1结构体变量赋值我们已经习惯了为结构体变量中的每个成员赋值,那么我们可以在两个结构体变更之间直接使用“=”号赋值吗?答案是肯定的,因为编译器支持。例如定义一个表示矩形的结构体:typedefstruct_Rectangle{intx;//左上角x坐标inty;//左上角y坐标intdx;//矩形宽度intdy;//矩形高度}Rectangle;定义两个矩形结构体变量并赋值:RectangleRect1,Rect2;Rect1.x=100;Rect1.y=100;Rect1.dx=100;Rect1.dy=100;Rect2=Rect1;上面的赋值在C语言中是支持的,编译器会将Rect2=Rect1中的值转化成内存拷贝的CPU指令来实现赋值操作。可以想象,对于简单的变量赋值,CPU只需要执行一个MOV指令就可以完成了,因此对于包含多个简单变量的结构体来说,使用多个循环的MOV指令就在情理之中了(在早期16位CPU中,如果对一个32位的int变量执行赋值操作都需要两条
本文标题:《深入BREW开发》分章节版第2章软件基础
链接地址:https://www.777doc.com/doc-2842092 .html