﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-嵌入式-文章分类-C/C++</title><link>http://www.cnitblog.com/zouzheng/category/4500.html</link><description>要像阿甘一直向前奔跑！</description><language>zh-cn</language><lastBuildDate>Mon, 26 Sep 2011 10:58:46 GMT</lastBuildDate><pubDate>Mon, 26 Sep 2011 10:58:46 GMT</pubDate><ttl>60</ttl><item><title>Linux下Makefile的automake生成全攻略</title><link>http://www.cnitblog.com/zouzheng/articles/74393.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 15 Jun 2011 02:23:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/74393.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/74393.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/74393.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/74393.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/74393.html</trackback:ping><description><![CDATA[<div><p style="text-indent: 2em;">Linux下Makefile的automake生成全攻略</p> <p style="text-indent: 2em;">2004-10-19 09:59 作者： 余涛 出处： 天极网 责任编辑：<a title="向本编辑提问" href="http://comments.yesky.com/t/%B7%BD%D6%DB/6,324/1865620.shtml" target="_blank">方舟</a> </p> <p style="text-indent: 2em;">&nbsp;&nbsp;&nbsp; 相关专题： <a href="http://soft.yesky.com/lesson/1/2648001.shtml">Linux设备驱动程序开发入门</a> </p> <p style="text-indent: 2em;">作为Linux下的程序开发人员，大家一定都遇到过Makefile，用make命令来编译自己写的程序确实是很方便。一般情况下，大家都是手工写一个简单Makefile，如果要想写出一个符合自由软件惯例的Makefile就不那么容易了。 <br />　 　在本文中，将给大家介绍如何使用autoconf和automake两个工具来帮助我们自动地生成符合自由软件惯例的Makefile，这样就可以象常 见的GNU程序一样，只要使用&#8220;./configure&#8221;，&#8220;make&#8221;，&#8220;make  instal&#8221;就可以把程序安装到Linux系统中去了。这将特别适合想做开放源代码软件的程序开发人员，又或如果你只是自己写些小的Toy程序，那么这 个文章对你也会有很大的帮助。<br />一、Makefile介绍<br />　　Makefile是用于自动编译和链接的，一个工程有很多文件组成，每一个文件的改变都会导致工程的重新链接，但是不是所有的文件都需要重新编译，Makefile中纪录有文件的信息，在make时会决定在链接的时候需要重新编译哪些文件。<br />　　Makefile的宗旨就是：让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变，编译器会自动的发现最终的生成文件已经过时，而重新编译相应的模块。<br />　 　Makefile的基本结构不是很复杂，但当一个程序开发人员开始写Makefile时，经常会怀疑自己写的是否符合惯例，而且自己写的 Makefile经常和自己的开发环境相关联，当系统环境变量或路径发生了变化后，Makefile可能还要跟着修改。这样就造成了手工书写 Makefile的诸多问题，automake恰好能很好地帮助我们解决这些问题。<br />　　使用automake，程序开发人员只需要写一些简单的含 有预定义宏的文件，由autoconf根据一个宏文件生成configure，由automake根据另一个宏文件生成Makefile.in，再使用 configure依据Makefile.in来生成一个符合惯例的Makefile。下面我们将详细介绍Makefile的automake生成方法。<br />二、使用的环境<br />　　本文所提到的程序是基于Linux发行版本：Fedora Core release 1，它包含了我们要用到的autoconf，automake。</p> <p style="text-indent: 2em;">三、从helloworld入手<br />　　我们从大家最常使用的例子程序helloworld开始。<br />　　下面的过程如果简单地说来就是：<br />　　新建三个文件：<br />　　　helloworld.c<br />　　　configure.in<br />　　　Makefile.am<br />　　然后执行：</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">aclocal; autoconf; automake --add-missing; ./configure; make; ./helloworld</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">就可以看到Makefile被产生出来，而且可以将helloworld.c编译通过。<br />　　很简单吧，几条命令就可以做出一个符合惯例的Makefile，感觉如何呀。<br />　　现在开始介绍详细的过程：<br />1、建目录<br />　　在你的工作目录下建一个helloworld目录，我们用它来存放helloworld程序及相关文件，如在/home/my/build下：</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">$ mkdir helloword<br />$ cd helloworld</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">2、 helloworld.c<br />　　然后用你自己最喜欢的编辑器写一个hellowrold.c文件，如命令：vi helloworld.c。使用下面的代码作为helloworld.c的内容。</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">int main(int argc, char** argv)<br />{<br />printf("Hello, Linux World!\n");<br />return 0;<br />}</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">完成后保存退出。<br />　　现在在helloworld目录下就应该有一个你自己写的helloworld.c了。<br />　　3、生成configure<br />　　我们使用autoscan命令来帮助我们根据目录下的源代码生成一个configure.in的模板文件。<br />　　命令：</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">$ autoscan<br />$ ls<br />configure.scan helloworld.c</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">执行后在hellowrold目录下会生成一个文件：configure.scan，我们可以拿它作为configure.in的蓝本。<br />　　现在将configure.scan改名为configure.in，并且编辑它，按下面的内容修改，去掉无关的语句：</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">============================configure.in内容开始=========================================<br /># -*- Autoconf -*-<br /># Process this file with autoconf to produce a configure script.<br />AC_INIT(helloworld.c)<br />AM_INIT_AUTOMAKE(helloworld, 1.0)<br /># Checks for programs.<br />AC_PROG_CC<br /># Checks for libraries.<br /># Checks for header files.<br /># Checks for typedefs, structures, and compiler characteristics.<br /># Checks for library functions.<br />AC_OUTPUT(Makefile)<br />============================configure.in内容结束=========================================</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;"><br />　　然后执行命令aclocal和autoconf，分别会产生aclocal.m4及configure两个文件：</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">$ aclocal <br />$ls <br />aclocal.m4 configure.in helloworld.c <br />$ autoconf <br />$ ls <br />aclocal.m4 autom4te.cache configure configure.in helloworld.c</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">大家可以看到configure.in内容是一些宏定义，这些宏经autoconf处理后会变成检查系统特性、环境变量、软件必须的参数的shell脚本。<br />　　autoconf 是用来生成自动配置软件源代码脚本（configure）的工具。configure脚本能独立于autoconf运行，且在运行的过程中，不需要用户的干预。<br />　　要生成configure文件，你必须告诉autoconf如何找到你所用的宏。方式是使用aclocal程序来生成你的aclocal.m4。<br />　　aclocal根据configure.in文件的内容，自动生成aclocal.m4文件。aclocal是一个perl 脚本程序，它的定义是：&#8220;aclocal - create aclocal.m4 by scanning configure.ac&#8221;。<br />　　autoconf从configure.in这个列举编译软件时所需要各种参数的模板文件中创建configure。<br />　　autoconf需要GNU m4宏处理器来处理aclocal.m4，生成configure脚本。<br />　　m4是一个宏处理器。将输入拷贝到输出，同时将宏展开。宏可以是内嵌的，也可以是用户定义的。除了可以展开宏，m4还有一些内建的函数，用来引用文件，执行命令，整数运算，文本操作，循环等。m4既可以作为编译器的前端，也可以单独作为一个宏处理器。</p> <p style="text-indent: 2em;">Linux下Makefile的automake生成全攻略</p> <p style="text-indent: 2em;">4、新建Makefile.am<br />　　新建Makefile.am文件，命令：</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">$ vi Makefile.am</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">内容如下:</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">AUTOMAKE_OPTIONS=foreign<br />bin_PROGRAMS=helloworld<br />helloworld_SOURCES=helloworld.c</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">automake会根据你写的Makefile.am来自动生成Makefile.in。<br />　　Makefile.am中定义的宏和目标,会指导automake生成指定的代码。例如，宏bin_PROGRAMS将导致编译和连接的目标被生成。<br />5、运行automake<br />　　命令：</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">$ automake --add-missing<br />configure.in: installing `./install-sh'<br />configure.in: installing `./mkinstalldirs'<br />configure.in: installing `./missing'<br />Makefile.am: installing `./depcomp'</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">automake会根据Makefile.am文件产生一些文件，包含最重要的Makefile.in。<br />　　6、执行configure生成Makefile</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">$ ./configure <br />checking for a BSD-compatible install... /usr/bin/install -c<br />checking whether build environment is sane... yes<br />checking for gawk... gawk<br />checking whether make sets $(MAKE)... yes<br />checking for gcc... gcc<br />checking for C compiler default output... a.out<br />checking whether the C compiler works... yes<br />checking whether we are cross compiling... no<br />checking for suffix of executables... <br />checking for suffix of object files... o<br />checking whether we are using the GNU C compiler... yes<br />checking whether gcc accepts -g... yes<br />checking for gcc option to accept ANSI C... none needed<br />checking for style of include used by make... GNU<br />checking dependency style of gcc... gcc3<br />configure: creating ./config.status<br />config.status: creating Makefile<br />config.status: executing depfiles commands<br />$ ls -l Makefile<br />-rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Makefile</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;"><br />　　你可以看到，此时Makefile已经产生出来了。<br />　　7、使用Makefile编译代码</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">$ make<br />if gcc -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -<br />DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE=\"helloworld\" -DVERSION=\"1.0\" <br />-I. -I. -g -O2 -MT helloworld.o -MD -MP -MF ".deps/helloworld.Tpo" \<br />-c -o helloworld.o `test -f 'helloworld.c' || echo './'`helloworld.c; \<br />then mv -f ".deps/helloworld.Tpo" ".deps/helloworld.Po"; \<br />else rm -f ".deps/helloworld.Tpo"; exit 1; \<br />fi<br />gcc -g -O2 -o helloworld helloworld.o </p></td></tr></tbody></table><br /> <p style="text-indent: 2em;"><br />　　运行helloworld</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">$ ./helloworld <br />Hello, Linux World!</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;"><br />　　这样helloworld就编译出来了，你如果按上面的步骤来做的话，应该也会 很容易地编译出正确的helloworld文件。你还可以试着使用一些其他的make命令，如make clean，make install，make  dist，看看它们会给你什么样的效果。感觉如何？自己也能写出这么专业的Makefile，老板一定会对你刮目相看。</p> <p style="text-indent: 2em;">Linux下Makefile的automake生成全攻略</p> <p style="text-indent: 2em;">四、深入浅出</p> <p style="text-indent: 2em;">　　针对上面提到的各个命令，我们再做些详细的介绍。<br />1、 autoscan<br />　 　autoscan是用来扫描源代码目录生成configure.scan文件的。autoscan可以用目录名做为参数，但如果你不使用参数的话，那么 autoscan将认为使用的是当前目录。autoscan将扫描你所指定目录中的源文件，并创建configure.scan文件。<br />2、 configure.scan<br />　　configure.scan包含了系统配置的基本选项，里面都是一些宏定义。我们需要将它改名为configure.in<br />3、 aclocal<br />　 　aclocal是一个perl  脚本程序。aclocal根据configure.in文件的内容，自动生成aclocal.m4文件。aclocal的定义是：&#8220;aclocal -  create aclocal.m4 by scanning configure.ac&#8221;。<br />4、 autoconf<br />　　autoconf是用来产生configure文件的。configure是一个脚本，它能设置源程序来适应各种不同的操作系统平台，并且根据不同的系统来产生合适的Makefile，从而可以使你的源代码能在不同的操作系统平台上被编译出来。<br />　 　configure.in文件的内容是一些宏，这些宏经过autoconf  处理后会变成检查系统特性、环境变量、软件必须的参数的shell脚本。configure.in文件中的宏的顺序并没有规定，但是你必须在所有宏的最前 面和最后面分别加上AC_INIT宏和AC_OUTPUT宏。<br />　　在configure.ini中：<br />　　#号表示注释，这个宏后面的内容将被忽略。<br />　　AC_INIT(FILE) <br />　　这个宏用来检查源代码所在的路径。</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">AM_INIT_AUTOMAKE(PACKAGE, VERSION) </p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">这个宏是必须的，它描述了我们将要生成的软件包的名字及其版本号：PACKAGE是软件包的 名字，VERSION是版本号。当你使用make  dist命令时，它会给你生成一个类似helloworld-1.0.tar.gz的软件发行包，其中就有对应的软件包的名字和版本号。<br />AC_PROG_CC<br />　　这个宏将检查系统所用的C编译器。 <br />AC_OUTPUT(FILE)<br />　　这个宏是我们要输出的Makefile的名字。<br />　　我们在使用automake时，实际上还需要用到其他的一些宏，但我们可以用aclocal 来帮我们自动产生。执行aclocal后我们会得到aclocal.m4文件。<br />　　产生了configure.in和aclocal.m4 两个宏文件后，我们就可以使用autoconf来产生configure文件了。<br />5、 Makefile.am<br />　　Makefile.am是用来生成Makefile.in的，需要你手工书写。Makefile.am中定义了一些内容：<br />AUTOMAKE_OPTIONS <br />　　这个是automake的选项。在执行automake时，它会检查目录下是否存在标准GNU软件包中应具备的各种文件，例如AUTHORS、ChangeLog、NEWS等文件。我们将其设置成foreign时，automake会改用一般软件包的标准来检查。<br />bin_PROGRAMS<br />　　这个是指定我们所要产生的可执行文件的文件名。如果你要产生多个可执行文件，那么在各个名字间用空格隔开。 <br />helloworld_SOURCES <br />　 　这个是指定产生&#8220;helloworld&#8221;时所需要的源代码。如果它用到了多个源文件，那么请使用空格符号将它们隔开。比如需要 helloworld.h，helloworld.c那么请写成helloworld_SOURCES= helloworld.h  helloworld.c。<br />　　如果你在bin_PROGRAMS定义了多个可执行文件，则对应每个可执行文件都要定义相对的filename_SOURCES。<br />6、 automake<br />　　我们使用automake --add-missing来产生Makefile.in。<br />　　选项--add-missing的定义是&#8220;add missing standard files to package&#8221;，它会让automake加入一个标准的软件包所必须的一些文件。<br />　　我们用automake产生出来的Makefile.in文件是符合GNU Makefile惯例的，接下来我们只要执行configure这个shell 脚本就可以产生合适的 Makefile 文件了。<br />7、 Makefile<br />　　在符合GNU Makefiel惯例的Makefile中，包含了一些基本的预先定义的操作：<br />make<br />　　根据Makefile编译源代码，连接，生成目标文件，可执行文件。<br />make clean<br />　　清除上次的make命令所产生的object文件（后缀为&#8220;.o&#8221;的文件）及可执行文件。<br />make install<br />　　将编译成功的可执行文件安装到系统目录中，一般为/usr/local/bin目录。<br />make dist<br />　　产生发布软件包文件（即distribution package）。这个命令将会将可执行文件及相关文件打包成一个tar.gz压缩的文件用来作为发布软件的软件包。</p> <p style="text-indent: 2em;">它会在当前目录下生成一个名字类似&#8220;PACKAGE-VERSION.tar.gz&#8221;的文件。PACKAGE和VERSION，是我们在configure.in中定义的AM_INIT_AUTOMAKE(PACKAGE, VERSION)。<br />make distcheck<br />　　生成发布软件包并对其进行测试检查，以确定发布包的正确性。这个操作将自动把压缩包文件解开，然后执行configure命令，并且执行make，来确认编译不出现错误，最后提示你软件包已经准备好，可以发布了。</p><br /> <table width="90%" border="1" cellpadding="0"> <tbody> <tr> <td> <p style="text-indent: 2em;">===============================================<br />helloworld-1.0.tar.gz is ready for distribution<br />===============================================<br />make distclean</p></td></tr></tbody></table><br /> <p style="text-indent: 2em;">类似make clean，但同时也将configure生成的文件全部删除掉，包括Makefile。<br />五、结束语<br />　　通过上面的介绍，你应该可以很容易地生成一个你自己的符合GNU惯例的Makefile文件及对应的项目文件。<br />　　如果你想写出更复杂的且符合惯例的Makefile，你可以参考一些开放代码的项目中的configure.in和Makefile.am文件，比如：嵌入式数据库sqlite，单元测试cppunit。</p></div><img src ="http://www.cnitblog.com/zouzheng/aggbug/74393.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2011-06-15 10:23 <a href="http://www.cnitblog.com/zouzheng/articles/74393.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>static关键字</title><link>http://www.cnitblog.com/zouzheng/articles/40309.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Fri, 29 Feb 2008 12:49:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/40309.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/40309.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/40309.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/40309.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/40309.html</trackback:ping><description><![CDATA[<p><strong><font color=#02368d size=5>static关键字</font></strong><br><span><strong>1．作用于变量：</strong></span><br><span style="FONT-FAMILY: 宋体">&nbsp;&nbsp; 用</span><span lang=EN-US>static</span><span style="FONT-FAMILY: 宋体">声明局部变量-------<span style="FONT-FAMILY: 宋体">局部变量指在代码块</span><span lang=EN-US>{}</span><span style="FONT-FAMILY: 宋体">内部定义的变量，只在代码块内部有效（作用域），其缺省的存储方式是自动变量或说是动态存储的，即指令执行到变量定义处时才给变量分配存储单元，跳出代码块时释放内存单元（生命期）。用</span><span lang=EN-US>static</span><span style="FONT-FAMILY: 宋体">声明局部变量时，则改变变量的存储方式（生命期），使变量成为静态的局部变量，即编译时就为变量分配内存，直到程序退出才释放存储单元。这样，使得该局部变量有记忆功能，可以记忆上次的数据，不过由于仍是局部变量，因而只能在代码块内部使用（作用域不变）。<br></span></span><span style="FONT-FAMILY: 宋体"><br></span><span style="FONT-FAMILY: 宋体">&nbsp;&nbsp; 用</span><span lang=EN-US>static</span><span style="FONT-FAMILY: 宋体">声明外部变量-------<span style="FONT-FAMILY: 宋体">外部变量指在所有代码块</span><span lang=EN-US>{}</span><span style="FONT-FAMILY: 宋体">之外定义的变量，它缺省为静态变量，编译时分配内存，程序结束时释放内存单元。同时其作用域很广，整个文件都有效甚至别的文件也能引用它。为了限制某些外部变量的作用域，使其只在本文件中有效，而不能被其他文件引用，可以用</span><span lang=EN-US>static</span><span style="FONT-FAMILY: 宋体">关键字对其作出声明。</span></span><br><br><span style="FONT-FAMILY: 宋体">总结：用</span><span lang=EN-US>static</span><span style="FONT-FAMILY: 宋体">声明局部变量，使其变为静态存储方式，作用域不变；用</span><span lang=EN-US>static</span><span style="FONT-FAMILY: 宋体">声明外部变量，其本身就是静态变量，这只会改变其连接方式，使其只在本文件内部有效，而其他文件不可连接或引用该变量。</span></p>
<p><span><strong>2．作用于函数：</strong></span><br><span style="FONT-FAMILY: 宋体">使用</span><span lang=EN-US>static</span><span style="FONT-FAMILY: 宋体">用于函数定义时，对函数的连接方式产生影响，使得函数只在本文件内部有效，对其他文件是不可见的。这样的函数又叫作静态函数。使用静态函数的好处是，不用担心与其他文件的同名函数产生干扰，另外也是对函数本身的一种保护机制。</span></p>
<p><span style="FONT-FAMILY: 宋体">如果想要其他文件可以引用本地函数，则要在函数定义时使用关键字</span><span lang=EN-US>extern</span><span style="FONT-FAMILY: 宋体">，表示该函数是外部函数，可供其他文件调用。另外在要引用别的文件中定义的外部函数的文件中，使用</span><span lang=EN-US>extern</span><span style="FONT-FAMILY: 宋体">声明要用的外部函数即可。</span><br></p>
<p class=MsoNormal><span style="FONT-FAMILY: 宋体">参考资料：</span></p>
<p class=MsoNormal><span style="FONT-FAMILY: 宋体">①《</span><span lang=EN-US> C</span><span style="FONT-FAMILY: 宋体">程序设计（第二版）</span> <span style="FONT-FAMILY: 宋体">》，谭浩强</span></p>
<p class=MsoNormal><span style="FONT-FAMILY: 宋体">②《</span><span lang=EN-US> Pointers on C </span><span style="FONT-FAMILY: 宋体">》，</span><span lang=EN-US>Kenneth A.Reek</span></p>
<p><strong><font color=#02368d size=5>void和void指针</font></strong></p>
<p><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">void的含义</span><br>&nbsp;&nbsp;&nbsp; void即&#8220;无类型&#8221;，void *则为&#8220;无类型指针&#8221;，可以指向任何数据类型。<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">void指针使用规范</span><br>&nbsp;&nbsp;&nbsp; <span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">①void指针可以指向任意类型的数据，亦即可用任意数据类型的指针对void指针赋值。例如：<br>&nbsp;&nbsp;&nbsp; int *pint;<br>&nbsp;&nbsp;&nbsp; void *pvoid;<br>&nbsp;&nbsp;&nbsp; pvoid = pint;&nbsp;&nbsp; /* 不过不能 pint = pvoid; */<br>&nbsp;&nbsp;&nbsp; 如果要将pvoid赋给其他类型指针，则需要强制类型转换如：pint = (int *)pvoid;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; ②在ANSI C标准中，不允许对void指针进行算术运算如pvoid++或pvoid+=1等，而在GNU中则允许，因为在缺省情况下，GNU认为void *与char *一样。sizeof( *pvoid )== sizeof( char ).<br>&nbsp;&nbsp; </span></span><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">void的作用<br>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">①对函数返回的限定。<br>&nbsp;&nbsp;&nbsp; ②对函数参数的限定。<br>&nbsp;&nbsp;&nbsp; 当函数不需要返回值时，必须使用void限定。例如： void func(int, int);<br>&nbsp;&nbsp;&nbsp; 当函数不允许接受参数时，必须使用void限定。例如： int func(void)。<br><br>&nbsp;&nbsp;&nbsp; 由于void指针可以指向任意类型的数据，亦即可用任意数据类型的指针对void指针赋值，因此还可以用void指针来作为函数形参，这样函数就可以接受任意数据类型的指针作为参数。例如：<br>&nbsp;&nbsp;&nbsp; void * memcpy( void *dest, const void *src, size_t len );<br>&nbsp;&nbsp;&nbsp; void * memset( void * buffer, int c, size_t num );<br></span></span><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)"></span></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<p>&nbsp; &nbsp; <br>参考资料：《 C/C++语言void及void指针深层探索 》，宋宝华<br><strong><font color=#02368d size=5><br></font></strong><strong><font color=#02368d size=5>函数指针</font></strong></p>
<p><strong><font color=#0001ff>函数指针是什么？<br></font></strong>&nbsp;&nbsp;&nbsp; 先来看函数调用是怎么回事。一个函数占用一段连续内存。当调用一个函数时，实际上是跳转到函数入口地址，执行函数体的代码，完成后返回。如何找到对应的入口地址？这是由函数名来标记的，实际上，函数名就是函数的入口地址。<br>&nbsp;&nbsp;&nbsp; 函数指针是一种特殊类型的指针，它指向一个函数的入口地址。<br><br>&nbsp;&nbsp;&nbsp; <span style="COLOR: rgb(153,1,2)"><span style="FONT-WEIGHT: bold">注意：</span><span style="COLOR: rgb(0,1,2)">除了void类型指针是无类型的指针外，其他所有指针都是有对应类型的，例如int *pint、struct studentdata *psdata等，只有指明了指针所指的数据类型，编译器才能为指针分配或预计分配相应大小的存储空间，指针的算术运算如pint++等才是有意义的。因此，定义了某种类型的指针之后，除非使用强制类型转换，那么它只能指向相应数据类型的变量或常量，不同类型的指针或数据之间不可混用。所以指针的类型实际上是一种身份标志的作用。<br><br>&nbsp;&nbsp; 函数指针如何表明自己的身份呢？为了避免混乱，必须也要作出相应规定，不同函数的函数指针不能混用。例如，int func1(int arg11, char arg12)与int func2(char arg)的函数指针就不能混用，要定义可以指向func1的函数指针应该这样：<br>&nbsp;&nbsp;&nbsp; int (*pfunc1)(int, char) = func1；<br>定义可以指向func2的函数指针则该如下：<br>&nbsp;&nbsp;&nbsp; int (*pfunc2)(char) = func2;<br>&nbsp;&nbsp;&nbsp; <span style="FONT-WEIGHT: bold">从函数指针的定义可以看出，函数指针的类型实际上是由</span><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,102)">函数签名</span><span style="FONT-WEIGHT: bold">决定的。</span>函数签名就象是函数的身份证，一个函数的函数签名是独一无二的，具有相同函数签名的函数实际上就是同一函数。<span style="TEXT-DECORATION: underline">函数签名包括函数名、函数形参类型的有序列表和函数返回值类型。</span><br><br>&nbsp;&nbsp;&nbsp; 一个函数指针的定义规定了它只能指向特定类型的函数。如果两个函数的形参列表和返回值类型相同，只有函数名和函数体不同，则可以使用相同类型的函数指针。例如，如果还有一个函数int func3(char arg)，则上面定义的可以指向函数func2的函数指针也可以用于指向func3，即：<br>&nbsp;&nbsp;&nbsp; pfunc2 = func3;<br>&nbsp;&nbsp;&nbsp; 再使用pfunc2(char ARG)就可以调用函数func3，这时指令计数器（PC）指向函数入口，从此开始执行函数体代码。<br>&nbsp;&nbsp;&nbsp; 注意：对函数指针进行算术运算也是没有意义的。<br><br style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)"><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">如何使用函数指针？</span><br>&nbsp;&nbsp;&nbsp; ①定义合适类型的函数指针变量；<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; int (*pfunc)(int, int);<br>&nbsp;&nbsp;&nbsp; ②给函数指针变量赋值，使它指向某个函数入口；<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; int example(int, int);<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; pfunc = example;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; /*将函数入口地址赋给函数指针变量*/<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 或者：pfunc = &amp;example;&nbsp;&nbsp;&nbsp; /*函数名总是被编译器转换为函数指针（入口地址）来使用，因此与上面一句等价 */<br>&nbsp;&nbsp;&nbsp; ③使用函数指针来调用相应的函数；<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; retval = pfunc(10, 16);<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 或者：retval = (*pfunc)(10, 16);<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 上面两句都与retval = example(10, 16);等价。<br>&nbsp;&nbsp;&nbsp; 理解：一个指针变量p实际上也和普通的变量一样，要占存储空间（通常与平台的虚拟地址一样宽），也有其自身的存储地址&amp;p；不同的是，在指针变量p的值有特殊的意义，它是另外一个变量或常量的地址值，也就是说，在地址为&amp;p的存储单元上存放着另外一个数据的地址。因此，*p实际上是将p看作它指向的数据的地址来使用，*操作符是引用相应地址中的数据，也就是对地址为p的存储单元中存放的数据进行操作。<br>&nbsp;&nbsp;&nbsp; 一个函数指针变量则更为特殊。比如上面的例子，pfunc变量本身的值是函数example()的入口地址。因此pfunc可以代替其所指函数的函数名来使用。至于*pfunc，如果按照上面的理解，它实际上是地址pfunc的内容，也即函数example()的入口地址的内容，就有点含糊了。不过，从另一方面来理解，如果使用pfunc = &amp;example来初始化pfunc，则*pfunc == *(&amp;example) == example，又与pfunc等价。因此，就有了两种使用函数指针来调用相应函数的形式。<br>&nbsp;&nbsp;&nbsp; 值得注意的是，不可用*pfunc来对pfunc的值初始化。即*pfunc = example的写法是错误的。<br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">为什么要使用函数指针？<br>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">前面介绍了函数指针的基本知识和使用规范。下面介绍函数指针的实际用途。不过首先要对前面的知识再做一个补充，因为下面的应用很可能用到这一特性。前面指出，除函数名之外的函数签名内容（函数返回值类型和形参列表）决定了函数指针的类型。实际上还有一种特殊的或说通用的函数指针，在定义这类函数指针时，只需要指定函数返回值类型，而留空形参列表，这样就可以指向返回值类型相同的所有函数。例如：<br>&nbsp;&nbsp;&nbsp; int (*pfunc)();<br>&nbsp;&nbsp;&nbsp; 这样定义的pfunc就可以指向前面提到的func1和func2，因为他们都返回整型值。<br>&nbsp;&nbsp;&nbsp; <span style="FONT-WEIGHT: bold; COLOR: rgb(102,1,2)">注意：</span> int (*pfunc)()与int (*pfunc)(void)不是一回事，后者不允许接受任何参数。<br><br></span></span>&nbsp;&nbsp;&nbsp; <span style="FONT-WEIGHT: bold">函数指针最常见的三个用途是：</span><br>&nbsp;&nbsp;&nbsp; ①作为参数传递给其他函数。<br>&nbsp;&nbsp;&nbsp; 这样可以把多个函数用一个函数体封装起来，得到一个具有多个函数功能的新函数，根据传递的函数指针变量值的不同，执行不同的函数功能。这是函数嵌套调用难以实现的。参数的传递可以由程序员设定，也可以由用户输入读取，因此具有较大的灵活性和交互性。<br>&nbsp;&nbsp;&nbsp; 另外还可以用于回调函数。使用void配合，还可以将对不同数据类型的数据进行相同处理的多个函数封装为一个函数，增强函数的生命力。<br><br>&nbsp;&nbsp;&nbsp; ②用于散转程序。<br>&nbsp;&nbsp;&nbsp; 这种程序首先建立一个函数表（实际上是一个函数指针数组），表中存放了各个函数的入口地址（或函数名），根据条件的设定来查表选择执行相应的函数。这样也可以将多个函数封装为一个函数或者程序，散转分支条件可以由程序员设定，也可以由用户输入读取，甚至是外设的某种特定状态（这种状态可以是不受人为控制的）。<br><br>&nbsp;&nbsp;&nbsp; ③实现C的面向对象的类的封装。<br>&nbsp;&nbsp;&nbsp; C语言中的struct与C++中的class有很大不同，除了缺省的成员属性外（struct的成员缺省为public的，可随意使用，而class成员缺省为private的），struct还很难实现类成员函数的封装。struct的成员一般都是数据成员，而非函数成员。因此，为了在C语言中，为某个struct定义一套自己的函数对结构数据成员进行操作，可以在struct结构体中增加函数指针变量成员，在初始化时使它指向特定函数即可。<br><br>&nbsp;&nbsp;&nbsp; 应用举例：<br>&nbsp;&nbsp;&nbsp; ①假设定义了四个函数：add(int, int)、sub(int, int)、mul(int, int)、div(int, int)，可以将其封装为一个四则运算计算器函数:<br>&nbsp;&nbsp;&nbsp; double calculator(int x, int y, int (*pfunc)(int, int)) {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; double result;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; result = pfunc(x, y);<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return result;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; 又例如，在一个链表查询程序中，要通过比较节点的特征值来查询节点，不同类型的数据的比较方式不一样，整型等可以直接比较，字符串却要用专门的字符串操作函数，为了使代码可重用性更高，可以使用一个比较函数来代替各种不同数据类型的直接比较代码，同时，比较函数也必然是数据类型相关的，因此要使用void指针和函数指针来转换为类型无关的比较函数，根据相应的数据类型，调用相应的函数（传递相应的函数指针）。一个实例是：<br>&nbsp;&nbsp;&nbsp; int (*compare)(void const *, void const *);<br>&nbsp;&nbsp;&nbsp; 这个函数指针可以接受任意类型的数据的指针参数，同时返回int值作为比较结果标志。一个比较整型数据的比较函数是：<br>&nbsp;&nbsp;&nbsp; int compare_ints(void const *a, void const *b) {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; if( *(int *)a == *(int *)b ) <br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; return 0;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; return 1;<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; ②散转程序。通过一个转移表（函数指针数组）来实现。还是上面定义的四个四则运算函数，可以建立这样一个转移表（注意初始化该转移表的语句前面应有add等相应函数原型声明或定义）：<br>&nbsp;&nbsp;&nbsp; double (*calculator[])(int, int) = {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; add, sub, mul, div<br>&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp; 这样，calculator[0] == add, calculator[1] == sub, ...<br>&nbsp;&nbsp;&nbsp; 使用result = calculator[oper](x, y);就可以代替下面整个switch语句：<br>&nbsp;&nbsp;&nbsp; switch( oper ) {<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; case 0: result = add(x, y); break;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; case 1: result = sub(x, y); break;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; ③C的面向对象化。一个对象包括数据和对数据的操作。C语言中的struct只有数据成员，因此要增加一些&#8220;伪数据成员&#8221;即函数指针来实现对数据的操作。例如：<br>&nbsp;&nbsp;&nbsp; #ifndef C_Class<br>&nbsp;&nbsp;&nbsp; #define </span></span><span style="COLOR: rgb(153,1,2)"><span style="COLOR: rgb(0,1,2)">C_Class struct<br>&nbsp;&nbsp;&nbsp; #endif<br>&nbsp;&nbsp;&nbsp; </span></span><span style="COLOR: rgb(153,1,2)"><span style="COLOR: rgb(0,1,2)">C_Class student{<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; C_Class student *student_this<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; char name;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; int height;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; int gender;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; int classnum;<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; void (*Oper)( C_Class student *student_this );<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; ...<br>&nbsp;&nbsp;&nbsp; }<br></span></span></p>
<hr style="WIDTH: 100%; HEIGHT: 2px">
<p>参考资料：<br>①《 Pointers on C 》，Kenneth A.Reek<br>②《 C程序设计（第二版）》，谭浩强<br>③《 C语言嵌入式系统编程修炼之道 》<br></p>
<p><strong><font color=#02368d size=5>errno与错误处理</font></strong><br><br><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)">errno是什么?<br></span><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">在/usr/include/errno.h中,include了&lt;sys/errno.h&gt;,在该文件中定义了不同的errno的值(错误类型编号)所对应的宏以及错误类型.<br></span></span><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)"><br>基本使用:<br><span style="FONT-WEIGHT: bold"><span style="COLOR: rgb(0,1,2)"></span></span></span><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)"></span></span><span style="FONT-WEIGHT: bold; COLOR: rgb(0,1,255)"><span style="FONT-WEIGHT: bold"><span style="COLOR: rgb(0,1,2)"></span></span></span><span style="COLOR: rgb(0,1,255)"><span style="COLOR: rgb(0,1,2)">#include &lt;errno.h&gt;<br>extern int errno;<br><br>1.使用perror( const char *msg )函数来将错误类型所对应的错误信息以字符串形式打印到终端.<br>&nbsp; 首先输出用户自定义的字符串msg(可以为空,即""),然后打印错误信息.<br><br>2.使用stderr( int errnum )将错误信息转换为字符串.<br><br>3.注意,必须在函数表明操作失败后立刻对errno的值进行检查以找出对应错误.在使用它之前必须总是先将其值copy到另外一个变量保存起来,因为很多函数(象fprintf之类)自身就可能会改变errno的值.<br>&nbsp; func( );<br>&nbsp; errortype = errno;<br>&nbsp; printf( "%d\n", errortype );<br>或者：<br>&nbsp; if( errortype == ... ) {<br>&nbsp; &nbsp;&nbsp; do ...<br>&nbsp; }<br>&nbsp; else {<br>&nbsp; &nbsp;&nbsp; do ....<br>&nbsp; }<br></span></span></p>
<p><br>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/40309.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2008-02-29 20:49 <a href="http://www.cnitblog.com/zouzheng/articles/40309.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 声明与函数、函数指针</title><link>http://www.cnitblog.com/zouzheng/articles/21921.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Tue, 16 Jan 2007 07:18:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21921.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21921.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21921.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21921.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21921.html</trackback:ping><description><![CDATA[
		<p>
				<strong>
						<span lang="ZH-CN" style="FONT-WEIGHT: normal; FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">概述 </span>
				</strong>
				<strong>
						<span style="FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">
								<br />
								<br />
								<span lang="ZH-CN">　　</span>
						</span>
				</strong>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">在很多情况下，尤其是读别人所写代码的时候，对</span>
				<span style="FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">C<span lang="ZH-CN">语言声明的理解能力变得非常重要，而</span>C<span lang="ZH-CN">语言本身的凝练简约也使得</span>C<span lang="ZH-CN">语言的声明常常会令人感到非常困惑，因此，在这里我用一篇的内容来集中阐述一下这个问题。</span></span>
				<span style="FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">
						<br />
						<br />
						<span lang="ZH-CN">　　<strong>问题：声明与函数 </strong></span>
						<strong>
								<br />
								<br />
						</strong>
						<span lang="ZH-CN">　　有一段程序存储在起始地址为</span>
						<span>0<span lang="ZH-CN">的一段内存上，如果我们想要调用这段程序，请问该如何去做？ </span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　答案</span>
						<br />
						<br />
						<span lang="ZH-CN">　　答案是</span>
						<span>(*(void (*)( ) )0)( )<span lang="ZH-CN">。看起来确实令人头大，那好，让我们知难而上，从两个不同的途径来详细分析这个问题。 </span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　答案分析：从尾到头</span>
						<br />
						<br />
						<span lang="ZH-CN">　　首先，最基本的函数声明：</span>
						<span>void function (paramList);</span>
						<br />
						<br />
						<span lang="ZH-CN">　　最基本的函数调用：</span>
						<span>function(paramList);</span>
						<br />
						<br />
						<span lang="ZH-CN">　　鉴于问题中的函数没有参数，函数调用可简化为</span>
						<span>function();</span>
						<br />
						<br />
						<strong>
								<span lang="ZH-CN">　　</span>
						</strong>
						<span lang="ZH-CN">其次，根据问题描述，可以知道</span>
						<span>0<span lang="ZH-CN">是这个函数的入口地址，也就是说，</span>0<span lang="ZH-CN">是一个函数的指针。使用函数指针的函数声明形式是：</span>void (*pFunction)()<span lang="ZH-CN">，相应的调用形式是：</span> (*pFunction)()<span lang="ZH-CN">，则问题中的函数调用可以写作：</span>(*0)( )<span lang="ZH-CN">。</span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　第三，大家知道，函数指针变量不能是一个常数，因此上式中的</span>
						<span>0<span lang="ZH-CN">必须要被转化为函数指针。</span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　我们先来研究一下，对于使用函数指针的函数：比如</span>
						<span>void (*pFunction)( )<span lang="ZH-CN">，函数指针变量的原型是什么？这个问题很简单，</span>pFunction<span lang="ZH-CN">函数指针原型是</span>( void (*)( ) )<span lang="ZH-CN">，即去掉变量名，清晰起见，整个加上（）号。</span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　所以将</span>
						<span>0<span lang="ZH-CN">强制转换为一个返回值为</span>void<span lang="ZH-CN">，参数为空的函数指针如下：</span>( void (*)( ) )<span lang="ZH-CN">。</span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　</span>
						<span>OK<span lang="ZH-CN">，结合</span>2)<span lang="ZH-CN">和</span>3)<span lang="ZH-CN">的分析，结果出来了，那就是：</span>(*(void (*)( ) )0)( ) <span lang="ZH-CN">。 </span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　答案分析：从头到尾理解答案 </span>
						<br />
						<br />
						<span lang="ZH-CN">　　</span>
						<span>(void (*)( )) <span lang="ZH-CN">，是一个返回值为</span>void<span lang="ZH-CN">，参数为空的函数指针原型。</span></span>
						<br />
						<span lang="ZH-CN">　　</span>
						<span>(void (*)( ))0<span lang="ZH-CN">，把</span>0<span lang="ZH-CN">转变成一个返回值为</span>void<span lang="ZH-CN">，参数为空的函数指针，指针指向的地址为</span>0.</span>
						<br />
						<span lang="ZH-CN">　　</span>
						<span>*(void (*)( ))0<span lang="ZH-CN">，前面加上</span>*<span lang="ZH-CN">表示整个是一个返回值为</span>void<span lang="ZH-CN">的函数的名字</span></span>
						<br />
						<span lang="ZH-CN">　　</span>
						<span>(*(void (*)( ))0)( )<span lang="ZH-CN">，这当然就是一个函数了。</span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　我们可以使用</span>
						<span>typedef<span lang="ZH-CN">清晰声明如下：</span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　</span>
						<span>typedef void (*pFun)( );</span>
						<br />
						<br />
						<span lang="ZH-CN">　　这样函数变为</span>
						<span>(*(pFun)0 )( );</span>
						<br />
						<br />
						<strong>
								<span lang="ZH-CN">　　问题：三个声明的分析 </span>
						</strong>
						<strong>
								<br />
						</strong>
						<br />
						<span lang="ZH-CN">　　对声明进行分析，最根本的方法还是类比替换法，从那些最基本的声明上进行类比，简化，从而进行理解，下面通过分析三个例子，来具体阐述如何使用这种方法。</span>
						<br />
						<br />
						<span lang="ZH-CN">＃</span>
						<span>1<span lang="ZH-CN">：</span>int* (*a[5])(int, char*);</span>
						<br />
						<br />
						<strong>
								<span lang="ZH-CN">　　</span>
						</strong>
						<span lang="ZH-CN">首先看到标识符名</span>
						<span>a<span lang="ZH-CN">，</span>"[]"<span lang="ZH-CN">优先级大于</span>"*"<span lang="ZH-CN">，</span>a<span lang="ZH-CN">与</span>"[5]"<span lang="ZH-CN">先结合。所以</span>a<span lang="ZH-CN">是一个数组，这个数组有</span>5<span lang="ZH-CN">个元素，每一个元素都是一个指针，指针指向</span>"(int, char*)"<span lang="ZH-CN">，很明显，指向的是一个函数，这个函数参数是</span>"int, char*"<span lang="ZH-CN">，返回值是</span>"int*"<span lang="ZH-CN">。</span>OK<span lang="ZH-CN">，结束了一个。：）</span></span>
						<br />
						<br />
						<span lang="ZH-CN">＃</span>
						<span>2<span lang="ZH-CN">：</span>void (*b[10]) (void (*)());</span>
						<br />
						<br />
						<span lang="ZH-CN">　　</span>
						<span>b<span lang="ZH-CN">是一个数组，这个数组有</span>10<span lang="ZH-CN">个元素，每一个元素都是一个指针，指针指向一个函数，函数参数是</span>"void (*)()"<span lang="ZH-CN">【注</span>10<span lang="ZH-CN">】，返回值是</span>"void"<span lang="ZH-CN">。完毕！</span></span>
						<br />
						<br />
						<span lang="ZH-CN">　　注意：这个参数又是一个指针，指向一个函数，函数参数为空，返回值是</span>
						<span>"void"<span lang="ZH-CN">。</span></span>
						<br />
						<br />
						<span lang="ZH-CN">＃</span>
						<span>3. doube(*)() (*pa)[9];</span>
						<br />
						<br />
						<span lang="ZH-CN">　　</span>
						<span>pa<span lang="ZH-CN">是一个指针，指针指向一个数组，这个数组有</span>9<span lang="ZH-CN">个元素，每一个元素都是</span>"doube(*)()"<span lang="ZH-CN">（也即一个函数指针，指向一个函数，这个函数的参数为空，返回值是</span>"double"<span lang="ZH-CN">）。</span><?xml:namespace prefix = o /?><o:p></o:p></span>
				</span>
		</p>
		<p>
				<strong>
						<span style="FONT-FAMILY: PMingLiU">
								<a href="http://dugujian.blogchina.com/422469.html">
										<span style="COLOR: windowtext; TEXT-DECORATION: none">C<span lang="ZH-CN">语言中的函数指针</span></span>
								</a>
								<o:p>
								</o:p>
						</span>
				</strong>
		</p>
		<p>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">函数在内存中有一个物理位置，而这个位置是可以赋给一个指针的。一零点函数的地址就是该函数的入口点。因此，函数指针可被用来调用一个函数。函数的地址是用不带任何括号或参数的函数名来得到的。（这很类似于数组地址的得到方法，即，在只有数组名而无下标是就得到数组地址。）</span>
				<span style="FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">
						<o:p>
						</o:p>
				</span>
		</p>
		<p>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">怎样说明一个函数指针变量呢</span>
				<span style="FONT-SIZE: 10pt; FONT-FAMILY: PMingLiU">? <br /><span lang="ZH-CN">为了说明一个变量</span> fn_pointer <span lang="ZH-CN">的类型是</span>"<span lang="ZH-CN">返回值为</span> int <span lang="ZH-CN">的函数指针</span>", <span lang="ZH-CN">你可以使用下面的说明语句</span>:<br /><span style="COLOR: navy; FONT-FAMILY: PMingLiU"><font color="#000000">int (*fn_pointer) ();</font></span><br /><span lang="ZH-CN">为了让编译器能正确地解释这句语句</span>, *fn_pointer <span lang="ZH-CN">必须用括号围起来。若漏了这对括号</span>, <span lang="ZH-CN">则</span>:<br />int *fn_pointer ();<br /><span lang="ZH-CN">的意思完全不同了。</span>fn_pointer <span lang="ZH-CN">将是一个函数名</span>, <span lang="ZH-CN">其返回值为</span> int <span lang="ZH-CN">类型的指针。</span><o:p></o:p></span>
		</p>
		<p style="LINE-HEIGHT: 150%">
				<strong>
						<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数指针变量</span>
				</strong>
				<strong>
						<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  </span>
				</strong>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">
						<br />
						<br />
				</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　　在Ｃ语言中规定，一个函数总是占用一段连续的内存区，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">而函数名就是该函数所占内存区的首地址。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">我们可以把函数的这个首地址</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">(</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">或称入口地址</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">赋予一个指针变量，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">我们把这种指向函数的指针变量称为</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数指针变量</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数指针变量定义的一般形式为：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">类型说明符</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> (*</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针变量名</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)();   <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">其中</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">类型说明符</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示被指函数的返回值的类型。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"(* </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针变量名</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"*"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">后面的变量是定义的指针变量。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">最后的空括号表示指针变量所指的是一个函数。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">例如：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> int (*pf)();  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">pf</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是一个指向函数入口的指针变量，该函数的返回值</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">(</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数值</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是整型。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">下面通过例子来说明用指针形式实现对函数调用的方法。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br />int max(int a,int b){  <br />if(a&gt;b)return a;  <br />else return b;  <br />}  <br />main(){  <br />int max(int a,int b);  <br />int(*pmax)();  <br />int x,y,z;  <br />pmax=max;  <br />printf("input two numbers:\n");  <br />scanf("%d%d",&amp;x,&amp;y);  <br />z=(*pmax)(x,y);  <br />printf("maxmum=%d",z);  <br />}  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　　从上述程序可以看出用，函数指针变量形式调用函数的步骤如下：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">
						<o:p>
						</o:p>
				</span>
		</p>
		<p style="LINE-HEIGHT: 150%">
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">1. </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">先定义函数指针变量，如后一程序中第</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">9</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> int (*pmax)();</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">定义</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">pmax</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">为函数指针变量。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /><br />2. </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">把被调函数的入口地址</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">(</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数名</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">赋予该函数指针变量，如程序中第</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">11</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> pmax=max;  <br /><br />3. </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">用函数指针变量形式调用函数，如程序第</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">14</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> z=(*pmax)(x,y);</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　调用函数的一般形式为：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> (*</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针变量名</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">) (</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">实参表</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">使用函数指针变量还应注意以下两点：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /><br />a. </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数指针变量不能进行算术运算，这是与数组指针变量不同的。数组指针变量加减一个整数可使指针移动指向后面或前面的数组元素，而函数指针的移动是毫无意义的。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /><br />b. </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数调用中</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"(*</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针变量名</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">的两边的括号不可少，其中的</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">不应该理解为求值运算，在此处它只是一种表示符号。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /><br /></span>
				<strong>
						<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针型函数</span>
				</strong>
				<strong>
						<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  </span>
				</strong>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">
						<br />
						<br />
				</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">前面我们介绍过，所谓函数类型是指函数返回值的类型。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">在Ｃ语言中允许一个函数的返回值是一个指针</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">(</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">即地址</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">这种返回指针值的函数称为指针型函数。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">定义指针型函数的一般形式为：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">   <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">类型说明符</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> *</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数名</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">(</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">形参表</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">)   <br />{   <br />...... /*</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数体</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*/  <br />}   <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">其中函数名之前加了</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"*"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">号表明这是一个指针型函数，即返回值是一个指针。类型说明符表示了返回的指针值所指向的数据类型。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">如：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br />int *ap(int x,int y)  <br />{  <br />...... /*</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数体</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*/  <br />}  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　　表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">ap</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是一个返回指针值的指针型函数，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">它返回的指针指向一个整型变量。下例中定义了一个指针型函数</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> day_name</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">，它的返回值指向一个字符串。该函数中定义了一个静态指针数组</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">name</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">name </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">数组初始化赋值为八个字符串，分别表示各个星期名及出错提示。形参</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">n</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示与星期名所对应的整数。在主函数中，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">把输入的整数</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">i</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">作为实参，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">在</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">printf</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">语句中调用</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">day_name</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数并把</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">i</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">值传送给形参</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> n</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">day_name</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">函数中的</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">return</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">语句包含一个条件表达式，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> n </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">值若大于</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">7</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">或小于</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">1</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">则把</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">name[0] </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针返回主函数输出出错提示字符串</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">"Illegal day"</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">。否则返回主函数输出对应的星期名。主函数中的第</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">7</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行是个条件语句，其语义是，如输入为负数</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">(i&lt;0)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">则中止程序运行退出程序。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">exit</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是一个库函数，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">exit(1)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示发生错误后退出程序，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> exit(0)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示正常退出。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /><br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　　应该特别注意的是函数指针变量和指针型函数这两者在写法和意义上的区别。如</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">int(*p)()</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">和</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">int *p()</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是两个完全不同的量。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">int(*p)()</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是一个变量说明，说明</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">p </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是一个指向函数入口的指针变量，该函数的返回值是整型量，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">(*p)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">的两边的括号不能少。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">int *p() </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">则不是变量说明而是函数说明，说明</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">p</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是一个指针型函数，其返回值是一个指向整型量的指针，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*p</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">两边没有括号。作为函数说明，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">在括号内最好写入形式参数，这样便于与变量说明区别。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">对于指针型函数定义，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">int *p()</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">只是函数头部分，一般还应该有函数体部分。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br />main(){  <br />int i;  <br />char *day_name(int n);   <br />printf("input Day No:\n");  <br />scanf("%d",&amp;i);  <br />if(i&lt;0) exit(1);  <br />printf("Day No:%2d--&gt;%s\n",i,day_name(i));  <br />}  <br />char *day_n<o:p></o:p></span>
		</p>
		<p style="LINE-HEIGHT: 150%">
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">ame(int n){  <br />static char *name[]={ "Illegal day",  <br />"Monday",  <br />"Tuesday",  <br />"Wednesday",  <br />"Thursday",  <br />"Friday",  <br />"Saturday",  <br />"Sunday"};  <br />return((n&lt;1||n&gt;7) ? name[0] : name[n]);  <br />}  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　　本程序是通过指针函数，输入一个</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">1</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">～</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">7</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">之间的整数，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">输出对应的星期名。指针数组的说明与使用一个数组的元素值为指针则是指针数组。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针数组是一组有序的指针的集合。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　　指针数组说明的一般形式为：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">类型说明符</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">数组名</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">[</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">数组长度</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">]   <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　　其中类型说明符为指针值所指向的变量的类型。例如：</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> int *pa[3] </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">pa</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是一个指针数组，它有三个数组元素，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">每个元素值都是一个指针，指向整型变量。通常可用一个指针数组来指向一个二维数组。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">指针数组中的每个元素被赋予二维数组每一行的首地址，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">因此也可理解为指向一个一维数组。图</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">6—6</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示了这种关系。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">  <br />int a[3][3]={1,2,3,4,5,6,7,8,9};  <br />int *pa[3]={a[0],a[1],a[2]};  <br />int *p=a[0];  <br />main(){  <br />int i;  <br />for(i=0;i&lt;3;i++)  <br />printf("%d,%d,%d\n",a[i][2-i],*a[i],*(*(a+i)+i));  <br />for(i=0;i&lt;3;i++)  <br />printf("%d,%d,%d\n",*pa[i],p[i],*(p+i));  <br />}  <br /></span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">　　本例程序中，</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">pa</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">是一个指针数组，三个元素分别指向二维数组</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">a</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">的各行。然后用循环语句输出指定的数组元素。其中</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*a[i]</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">i</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">0</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">列元素值；</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*(*(a+i)+i)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">i</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">i</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">列的元素值；</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*pa[i]</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">i</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">0</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">列元素值；由于</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">p</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">与</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">a[0]</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">相同，故</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">p[i]</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">0</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">i</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">列的值；</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">*(p+i)</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">表示</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">0</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">行</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana">i</span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">列的值。读者可仔细领会元素值的各种不同的表示方法。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">应该注意指针数组和二维数组指针变量的区别。</span>
				<span style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: Verdana"> </span>
				<span lang="ZH-CN" style="FONT-SIZE: 10pt; COLOR: black; LINE-HEIGHT: 150%; FONT-FAMILY: PMingLiU">这两者虽然都可用来表示二维数组，但是其表示方法和意义是不同的</span>
		</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21921.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-16 15:18 <a href="http://www.cnitblog.com/zouzheng/articles/21921.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>可变参数学习笔记</title><link>http://www.cnitblog.com/zouzheng/articles/21888.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Mon, 15 Jan 2007 13:54:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21888.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21888.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21888.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21888.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21888.html</trackback:ping><description><![CDATA[[转帖]可变参数学习笔记<br />前言：本文在很大程度上改编自网友kevintz的"C语言中可变参数的用法"一文，在行文之前先向这位前辈表示真诚的敬意和感谢。<br />一、什么是可变参数<br />我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为: <br />int printf( const char* format, ...); <br />它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的（用三个点"…"做参数占位符）,实际调用时可以有以下的形式: <br />    printf("%d",i); <br />    printf("%s",s); <br />    printf("the number is %d ,string is:%s", i, s);    <br />以上这些东西已为大家所熟悉。但是究竟如何写可变参数的C函数以及这些可变参数的函数编译器是如何实现，这个问题却一直困扰了我好久。本文就这个问题进行一些探讨,希望能对大家有些帮助.<br /><br />二、写一个简单的可变参数的C函数 <br />先看例子程序。该函数至少有一个整数参数,其后占位符…，表示后面参数的个数不定. 在这个例子里，所有的输入参数必须都是整数，函数的功能只是打印所有参数的值.<br />函数代码如下：<br />//示例代码1：可变参数函数的使用<br />#include "stdio.h"<br />#include "stdarg.h"<br />void simple_va_fun(int start, ...) <br />{ <br />    va_list arg_ptr; <br />    int nArgValue =start;<br />    int nArgCout=0;     //可变参数的数目<br />    va_start(arg_ptr,start); //以固定参数的地址为起点确定变参的内存起始地址。<br />    do {<br />       ++nArgCout;<br />        printf("the %d th arg: %d\n",nArgCout,nArgValue);     //输出各参数的值<br />        nArgValue = va_arg(arg_ptr,int);                    //得到下一个可变参数的值<br />    } while(nArgValue != -1);                <br />    return; <br />}<br />int main(int argc, char* argv[])<br />{<br />    simple_va_fun(100,-1); <br />    simple_va_fun(100,200,-1); <br />    return 0;<br />}<br /><br />下面解释一下这些代码<br /><br />从这个函数的实现可以看到,我们使用可变参数应该有以下步骤: <br /><br />⑴由于在程序中将用到以下这些宏: <br />    void va_start( va_list arg_ptr, prev_param ); <br />    type va_arg( va_list arg_ptr, type ); <br />    void va_end( va_list arg_ptr ); <br />va在这里是variable-argument(可变参数)的意思. <br />这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件.<br /><br />⑵函数里首先定义一个va_list型的变量,这里是arg_ptr,这个变量是存储参数地址的指针.因为得到参数的地址之后，再结合参数的类型，才能得到参数的值。<br /><br />⑶然后用va_start宏初始化⑵中定义的变量arg_ptr,这个宏的第二个参数是可变参数列表的前一个参数,即最后一个固定参数. <br /><br />⑷然后依次用va_arg宏使arg_ptr返回可变参数的地址,得到这个地址之后，结合参数的类型，就可以得到参数的值。<br /><br />⑸设定结束条件，这里的条件就是判断参数值是否为-1。注意被调的函数在调用时是不知道可变参数的正确数目的，程序员必须自己在代码中指明结束条件。至于为什么它不会知道参数的数目，读者在看完这几个宏的内部实现机制后，自然就会明白。<br /><br />(二)可变参数在编译器中的处理 <br />我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的, 由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下面看一下VC++6.0中stdarg.h里的代码（文件的路径为VC安装目录下的\vc98\include\stdarg.h）<br />    typedef char *  va_list;<br />    #define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) &amp; ~(sizeof(int) - 1) )<br />    #define va_start(ap,v)  ( ap = (va_list)&amp;v + _INTSIZEOF(v) )<br />    #define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )<br />    #define va_end(ap)      ( ap = (va_list)0 )<br /><br />下面我们解释这些代码的含义：<br /><br />1、首先把va_list被定义成char*，这是因为在我们目前所用的PC机上，字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void*的<br /><br />2、定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.这个宏的目的是为了得到最后一个固定参数的实际内存大小。在我的机器上直接用sizeof运算符来代替，对程序的运行结构也没有影响。（后文将看到我自己的实现）。<br /><br />3、va_start的定义为 &amp;v+_INTSIZEOF(v) ,这里&amp;v是最后一个固定参数的起始地址，再加上其实际占用大小后，就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在的内存地址,有了这个地址，以后的事情就简单了。 <br /><br />这里要知道两个事情：<br />    ⑴在intel+windows的机器上，函数栈的方向是向下的，栈顶指针的内存地址低于栈底指针，所以先进栈的数据是存放在内存的高地址处。<br />    (2)在VC等绝大多数C编译器中，默认情况下，参数进栈的顺序是由右向左的，因此，参数进栈以后的内存模型如下图所示：最后一个固定参数的地址位于第一个可变参数之下，并且是连续存储的。<br />|--------------------------|<br />|  最后一个可变参数             |   -&gt;高内存地址处<br />|--------------------------|<br />|--------------------------|<br />|  第N个可变参数              |     -&gt;va_arg(arg_ptr,int)后arg_ptr所指的地方,<br />|                               |     即第N个可变参数的地址。<br />|--------------- |     <br />|--------------------------|<br />|  第一个可变参数               |     -&gt;va_start(arg_ptr,start)后arg_ptr所指的地方<br />|                               |     即第一个可变参数的地址<br />|--------------- |     <br />|------------------------ --|<br />|                               |<br />|  最后一个固定参数             |    -&gt; start的起始地址<br />|-------------- -|       .................<br />|-------------------------- |<br />|                               |  <br />|--------------- |  -&gt; 低内存地址处<br /><br />(4) va_arg():有了va_start的良好基础，我们取得了第一个可变参数的地址，在va_arg()里的任务就是根据指定的参数类型取得本参数的值，并且把指针调到下一个参数的起始地址。<br />因此，现在再来看va_arg()的实现就应该心中有数了：<br />    #define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )<br />这个宏做了两个事情，<br />   ①用用户输入的类型名对参数地址进行强制类型转换，得到用户所需要的值<br />   ②计算出本参数的实际大小，将指针调到本参数的结尾，也就是下一个参数的首地址，以便后续处理。<br /><br />(5)va_end宏的解释：x86平台定义为ap=(char*)0;使ap不再 指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的. 在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型. 关于va_start, va_arg, va_end的描述就是这些了,我们要注意的 是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.<br /><br />(三)可变参数在编程中要注意的问题 <br />因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢, 可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能 地识别不同参数的个数和类型. 有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数 printf是从固定参数format字符串来分析出参数的类型,再调用va_arg 的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的. 例如，在C的经典教材《the c programming language》的7.3节中就给出了一个printf的可能实现方式，由于篇幅原因这里不再叙述。<br /><br />（四）小结: <br />1、标准C库的中的三个宏的作用只是用来确定可变参数列表中每个参数的内存地址，编译器是不知道参数的实际数目的。<br />2、在实际应用的代码中，程序员必须自己考虑确定参数数目的办法，如<br />⑴在固定参数中设标志-- printf函数就是用这个办法。后面也有例子。<br />⑵在预先设定一个特殊的结束标记，就是说多输入一个可变参数，调用时要将最后一个可变参数的值设置成这个特殊的值，在函数体中根据这个值判断是否达到参数的结尾。本文前面的代码就是采用这个办法.<br />无论采用哪种办法，程序员都应该在文档中告诉调用者自己的约定。<br />3、实现可变参数的要点就是想办法取得每个参数的地址，取得地址的办法由以下几个因素决定：<br />①函数栈的生长方向<br />②参数的入栈顺序<br />③CPU的对齐方式<br />④内存地址的表达方式<br />结合源代码，我们可以看出va_list的实现是由④决定的，_INTSIZEOF(n)的引入则是由③决定的，他和①②又一起决定了va_start的实现，最后va_end的存在则是良好编程风格的体现，将不再使用的指针设为NULL,这样可以防止以后的误操作。<br />4、取得地址后，再结合参数的类型，程序员就可以正确的处理参数了。理解了以上要点，相信稍有经验的读者就可以写出适合于自己机器的实现来。下面就是一个例子<br /><br />（五）扩展--自己实现简单的可变参数的函数。<br />下面是一个简单的printf函数的实现，参考了&lt;The C Programming Language&gt;中的156页的例子，读者可以结合书上的代码与本文参照。<br />#include "stdio.h"<br />#include "stdlib.h"<br />void myprintf(char* fmt, ...)        //一个简单的类似于printf的实现，//参数必须都是int 类型<br />{ <br />    char* pArg=NULL;               //等价于原来的va_list <br />    char c;<br />    pArg = (char*) &amp;fmt;    //注意不要写成p = fmt !!因为这里要对//参数取址，而不是取值<br />    pArg += sizeof(fmt);         //等价于原来的va_start          <br />   do<br />    {<br />        c =*fmt;<br />        if (c != '%')<br />        {<br />            putchar(c);            //照原样输出字符<br />        }<br />        else<br />{<br />//按格式字符输出数据<br />            switch(*++fmt) <br />{<br />            case 'd':<br />                printf("%d",*((int*)pArg));           <br />                break;<br />            case 'x':<br />                printf("%#x",*((int*)pArg));<br />                break;<br />            default:<br />                break;<br />            } <br />            pArg += sizeof(int);               //等价于原来的va_arg<br />        }<br />        ++fmt;<br />    }while (*fmt != '\0'); <br />    pArg = NULL;                               //等价于va_end<br />    return; }<br />int main(int argc, char* argv[])<br />{<br />    int i = 1234;<br />    int j = 5678;<br />    myprintf("the first test:i=%d\n",i,j); <br />    myprintf("the secend test:i=%d; %x;j=%d;\n",i,0xabcd,j); <br />    system("pause");<br />    return 0;<br />}<br />在intel+win2k+vc6的机器执行结果如下：<br />the first test:i=1234<br />the secend test:i=1234; 0xabcd;j=5678;<br /><img src ="http://www.cnitblog.com/zouzheng/aggbug/21888.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-15 21:54 <a href="http://www.cnitblog.com/zouzheng/articles/21888.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>雅虎笔试题</title><link>http://www.cnitblog.com/zouzheng/articles/21759.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 11 Jan 2007 03:16:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21759.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21759.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21759.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21759.html</trackback:ping><description><![CDATA[辛辛苦苦把题做完，系统提示说“由于长时间未操作，系统失去联系”，艾...<font style="FONT-SIZE: 0px; COLOR: #ffffff">1C&#x1;E(` ? `&#x1B;M</font><br />可能每个人的题是随机的吧<font style="FONT-SIZE: 0px; COLOR: #ffffff">*j&#x7;_ w1U*z)J</font><p>Question 1. (单选)<br />在顺序表(3,6,8,10,12,15,16,18,21,25,30)中,用二分法查找关键码值11,所需的关键码比<br />较次数为(3)</p><p> 1. 2<br />2. 3<br />3. 4<br />4. 5</p><p>Question 2. (单选)<br />设散列表的存储空间大小为19,所用散列函数为h(key)=key mod 19,用开地址线性探查法解<br />决碰撞。散列表的当前</p><p>状态如下:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 190 194 768 559 582 39<br />3 208.现要将关键码</p><p>值75插入到该散列表中,其地址应为 (1)</p><p> 1. 1<br />2. 11<br />3. 5<br />4. 15</p><p>Question 3. (单选)<br />作业管理的主要任务包括作业输入、作业处理和作业输出。其中作业处理的工作是Ⅰ.作业<br />调度 Ⅱ.作业控制 Ⅲ.</p><p>作业后备 (3)</p><p> 1. 只有Ⅰ<br />2. 只有Ⅱ<br />3. Ⅰ和Ⅱ<br />4. 都是</p><p>Question 4. (单选)<br />系统为了管理文件,设置了专门的数据结构----文件控制块(FC。FCB是在执行下列哪一个系<br />统调用时建立的? (1)</p><p> 1. create<br />2. open<br />3. read<br />4. write</p><p>Question 5. (单选)<br />下面关于通道的叙述中,正确的是Ⅰ.通道相当于一个功能简单的处理机Ⅱ.通道完成数据输<br />入输出工作Ⅲ.通道与</p><p>CPU共用一个内存 (4)</p><p> 1. Ⅰ和Ⅱ<br />2. Ⅰ和Ⅲ<br />3. Ⅱ和Ⅲ<br />4. 都是</p><p>Question 6. (单选)<br />互操作性是指在不同环境下的应用程序可以相互操作,交换信息。要使采用不同数据格式的<br />各种计算机之间能够相</p><p>互理解,这一功能是由下面哪一层来实现的? (2)</p><p> 1. 应用层<br />2. 表示层<br />3. 会话层<br />4. 传输层</p><p>Question 7. (单选)<br />在UNIX的Shell程序中,可以使用位置变量。若要指明Shell引用的最近后台进程的号码,可<br />以使用位置变量 (2)</p><p> 1. $$<br />2. $!<br />3. $#<br />4. $-</p><p>Question 8. (单选)<br />设二叉树根结点的层次为0，一棵深度(高度)为k的满二叉树和同样深度的完全二叉树各有<br />f个结点和c个结点，下列</p><p>关系式不正确的是： (2)</p><p> 1. f&gt;＝c<br />2. c&gt;f<br />3. f=2k+1-1<br />4. C&gt;2k-1</p><p>Question 9. (单选)<br />单链表的每个结点中包括一个指针link，它指向该结点的后继结点。现要将指针q指向的新<br />结点插入到指针p指向的</p><p>单链表结点之后，下面的操作序列中哪一个是正确的？ (3)</p><p> 1. q:=p^.link; p^.link:=q^.link<br />2. p^.link:=q^.link; q:=P^.link<br />3. q^.link:=p^.link; p^.link:=q;<br />4. p^.link:=q; q^.link:=p^,link</p><p>Question 10. (单选)<br />某二叉树结点的对称序序列为A、B、C、D、E、F、G，后序序列为B、D、C、A、F、G、E。<br />该二叉树结点的前序序</p><p>列为  (2)</p><p> 1. E、G、F、A、C、D、B<br />2. E、A、C、B、D、G、F<br />3. E、A、G、C、F、B、D<br />4. E、G、A、C、D、F、B</p><p>Question 11. (单选)<br />某二叉树结点的对称序序列为A、B、C、D、E、F、G，后序序列为B、D、C、A、F、G、E。<br />该二叉树对应的树林包</p><p>括多少棵树？ (2)</p><p> 1. 1<br />2. 2<br />3. 3<br />4. 4</p><p>Question 12. (单选)<br />某二叉树结点的对称序序列为A、B、C、D、E、F、G，后序序列为B、D、C、A、F、G、E。<br />该二叉树对应的树林结</p><p>点的层次次序序列为  (1)</p><p> 1. E、G、F、A、C、D、B<br />2. E、A、C、B、D、G、F<br />3. E、A、G、C、F、B、D<br />4. E、G、A、C、D、F、B</p><p>Question 13. (单选)<br />假设就绪队列中有10个进程，系统将时间片设为200ms， CPU进行进程切换要花费10ms。则<br />系统开销所占的比率约</p><p>为  (2)</p><p> 1. 1%<br />2. 5%<br />3. 10%<br />4. 20%</p><p>Question 14. (单选)<br />长度相同但格式不同的2种浮点数，假设前者阶码长、尾数短，后者阶码短、尾数长，其它<br />规定均相同，则它们可</p><p>表示的数的范围和精度为： (2)</p><p> 1. 两者可表示的数的范围和精度相同<br />2. 前者可表示的数的范围大但精度低<br />3. 后者可表示的数的范围大且精度高<br />4. 前者可表示的数的范围大且精度高</p><p>Question 15. (单选)<br />所谓“变号操作”是指将一个整数变成绝对值相同但符号相反的另一个整数。假设使用补<br />码表示的8位整数</p><p>X=10010101，则经过变号操作后结果为：(4)</p><p> 1. 1101010<br />2. 10101<br />3. 11101010<br />4. 1101011</p><p>Question 16. (单选)<br />设有一个用数组Q[1..m」表示的环形队列，约定f为当前队头元素在数组中的位置，r为队<br />尾元素的后一位置(按顺</p><p>时针方向)，若队列非空，则计算队列中元素个数的公式应为：(2)</p><p> 1. r-f<br />2. (m+r-f) mod m<br />3. (m-r+f)mod m<br />4. (m-r-f) mod m</p><p>Question 17. (单选)<br />计算机最主要的工作特点是(2)</p><p> 1. 存储程序与自动控制<br />2. 高速度与高精度<br />3. 可靠性与可用性<br />4. 有记忆能力</p><p>Question 18. (单选)<br />计算机中数据的表示形式是(3)</p><p> 1. 八进制<br />2. 十进制<br />3. 二进制<br />4. 十六进制</p><p>Question 19. (单选)<br />下面列出的四种存储器中，易失性存储器是(1)</p><p> 1. RAM<br />2. ROM<br />3. PROM<br />4. CD-ROM</p><p>Question 20. (单选)<br />I/O接口位于 (2)</p><p> 1. 总线和设备之间<br />2. CPU和I/O设备之间<br />3. 主机和总线之间<br />4. CPU和主存储器之间</p><p>Question 21. (单选)<br />计算机硬件能直接识别和执行的只有(4)</p><p> 1. 高级语言<br />2. 符号语言<br />3. 汇编语言<br />4. 机器语言</p><p>Question 22. (单选)<br />具有多媒体功能的微型计算机系统中，常用的CD-ROM是(2)</p><p> 1. 只读型大容量软盘<br />2. 只读型光盘<br />3. 只读型硬盘<br />4. 半导体只读存储器</p><p>Question 23. (单选)<br />微机中1K字节表示的二进制位数是(4)</p><p> 1. 1000<br />2. 8x1000<br />3. 1024<br />4. 8x1024</p><p>Question 24. (单选)<br />下列字符中，ASCII码值最小的是(2)</p><p> 1. a<br />2. A<br />3. x<br />4. Y</p><p>Question 25. (单选)<br />OSI(开放系统互联)参考模型的最低层是(3)</p><p> 1. 传输层<br />2. 网络层<br />3. 物理层<br />4. 应用层</p><p>Question 26. (单选)<br />在面向对象的系统中，系统责任的良好分配原则是(3)</p><p> 1. 在类之间均匀分配<br />2. 集中分配在少数控制类中<br />3. 根据交互图的消息进行分配<br />4. 根据个人喜好进行分配</p><p>Question 27. (单选)<br />在CMM模型中，对软件成熟度有以下5个级别，请从低到高排序<br />a 初始级:<br />软件过程的特点是无秩序的，偶尔甚至是混乱的。几乎没有什么过程是经过定义的,成功依<br />赖于个人的努力。</p><p>b 优化级:<br />利用来自过程和来自新思想、新技术的先导性试验的定量反馈信息，使持续过程改进成为<br />可能。</p><p>c 已定义级:<br />管理活动和工程活动两方面的软件过程均已文档化、标准化、并集成到组织的标准软件过<br />程。</p><p>d 已管理级:<br />已采集详细的有关软件过程和产品质量的度量,无论软件过程还是产品均得到定量了解和控<br />制。</p><p>e 可重复级:<br />已建立基本的项目管理过程去跟踪成本、进度和功能性。必要的过程纪律已经就位，使具<br />有类似应用的项目。能重</p><p>复以前的成功。<br />(3)</p><p> 1. acdbe<br />2. adceb<br />3. aecdb<br />4. abcde</p><p>Question 28. (单选)<br />在下面所列举的逻辑测试覆盖中，测试覆盖最强的是(3)</p><p> 1. 条件覆盖<br />2. 条件组合覆盖<br />3. 语句覆盖<br />4. 条件及判定覆盖</p><p>Question 29. (单选)<br />一般来说，在软件维护过程中，大部分工作是由什么引起的(3)</p><p> 1. 适应新的软件环境<br />2. 适应新的硬件环境<br />3. 用户的需求改变<br />4. 程序的可靠性</p><p>Question 30. (单选)(2)<br />PSP是？</p><p> 1. 团队软件过程<br />2. 个体软件过程<br />3. 过程改进过程<br />4. 质量管理过程</p><p>Question 31. (单选)<br />假定a和b为int型变量,则执行以下语句后b的值为   (4)<br />a=1; b=10;<br />do { b-=a; a++; } while (b--&lt;0);</p><p> 1. 9<br />2. -2<br />3. -1<br />4. 8</p><p>Question 32. (单选)<br />设有以下宏定义:<br />#define N 3<br />#define Y(n) ( (N+1)*n)<br />则执行语句:z=2 * (N+Y(5+1));后,z的值为 (3)</p><p> 1. 出错<br />2. 42<br />3. 48<br />4. 54</p><p>Question 33. (单选)<br />执行以下程序段后,m的值为  (1)<br />int a[2][3]={ {1,2,3},{4,5,6} };<br />int m,*p;<br />p=&amp;a[0][0];<br />m=(*p)*(*(p+2))*(*(p+4));</p><p> 1. 15<br />2. 14<br />3. 13<br />4. 12</p><p>Question 34. (单选)<br />有以下程序<br />main()<br />{ char a[]="programming", b[]="language";<br />char *p1,*p2;<br />int i;<br />p1=a; p2=b;<br />for(i=0;i&lt;7;i++)<br />if(*(p1+i)==*(p2+i)) printf("％c",*(p1+i));<br />}<br />输出结果是 (4)</p><p> 1. gm<br />2. rg<br />3. or<br />4. ga</p><p>Question 35. (单选)<br />有以下程序<br />int fun(int x,int y,int *cp,int *dp)<br />{ *cp=x+y; *dp=x-y; }<br />main()<br />{ int a, b, c, d;<br />a=30; b=50;<br />fun(a,b,&amp;c,&amp;d);<br />printf("%d,%d<br />", c, d);<br />}<br />输出结果是 (3)</p><p> 1. 50,30<br />2. 30,50<br />3. 80,-20<br />4. 80,20</p><p>Question 36. (单选)<br />下述标识符中, 合法的用户标识符是(4)</p><p> 1. A#C<br />2. getch<br />3. void<br />4. sizeOf</p><p>Question 37. (单选)<br />以下选项中合法的字符常量是(3)</p><p> 1. A<br />2. '10'<br />3. 68<br />4. D</p><p>Question 38. (单选)<br />以下叙述正确的是 (3)</p><p> 1. 在C程序中，main函数必须位于程序的最前面<br />2. C程序的每行中只能写一条语句<br />3. C语言本身没有输入输出语句<br />4. 在对一个C程序进行编译的过程中，可发现注释中的拼写错误</p><p>Question 39. (单选)<br />以下叙述中不正确的是 (4)</p><p> 1. 在不同的函数中可以使用相同名字的变量<br />2. 函数中的形式参数是局部变量<br />3. 在一个函数内定义的变量只在本函数范围内有效<br />4. 在一个函数内的复合语句中定义的变量在本函数范围内有效</p><p>Question 40. (单选)<br />设int类型的数据长度为2个字节，则 unsigned int 类型数据的取值范围是 (2)</p><p> 1. 0~255<br />2. 0~65535<br />3. -32768~32767<br />4. -256~255</p><p>Question 41. (单选)<br />某文件中定义的静态全局变量(或称静态外部变量)其作用域是 (2)</p><p> 1. 只限某个函数<br />2. 本文件<br />3. 跨文件<br />4. 不限制作用域</p><p>Question 42. (单选)<br />语句:printf("%d<br />"，12 &amp; 012)；的输出结果是 (3)</p><p> 1. 12<br />2. 012<br />3. 8<br />4. 6</p><p>Question 43. (单选)<br />设int x=4; 则执行以下语句: x+=x-=x-x;后,x的值为 (3)</p><p> 1. 0<br />2. 4<br />3. 8<br />4. 12</p><p>Question 44. (单选)<br />while(!x)中的(!x)与下面条件等价。 (4)</p><p> 1. x==1<br />2. x!=1<br />3. x!=0<br />4. x==0</p><p>Question 45. (单选)<br />已知int i,a;执行语句: i=(a=2*3,a*5),a+6;后,变量i的值是 (3)</p><p> 1. 6<br />2. 12<br />3. 30<br />4. 36</p><p>Question 46. (单选)<br />整型变量x和y的值相等、且为非0值，则以下选项中，结果为零的表达式是 (4)</p><p> 1. x || y<br />2. x | y<br />3. x &amp; y<br />4. x ^ y</p><p>Question 47. (单选)<br />x、y、z被定义为int型变量，若从键盘给x、y、z输入数据，正确的输入语句是  (2)</p><p> 1. INPUT x、y、z;<br />2. scanf("%d%d%d",&amp;x,&amp;y,&amp;z);<br />3. scanf("%d%d%d",x,y,z);<br />4. read("%d%d%d",x,y,z);</p><p>Question 48. (单选)<br />以下各选项企图说明一种新的类型名，其中正确的是 (2)</p><p> 1. typedef v1 int;<br />2. typedef int v3;<br />3. typedef v4: int;<br />4. typedef v2=int;</p><p>Question 49. (单选)<br />char x=3,y=6,z;<br />z=x^y&lt;&lt;2;<br />则z的二进制值是 (2)</p><p> 1. 10100<br />2. 11011<br />3. 11100<br />4. 11000</p><p>Question 50. (单选)<br />若有说明：int i, j=2，*p=&amp;i;，则能完成i=j赋值功能的语句是 (2)</p><p> 1. i=*p;<br />2. *p=*&amp;j;<br />3. i=&amp;j;<br />4. i=**p;</p><p>Question 51. (问答)<br />给定一个整型变量a，写两段代码，第一个设置a的bit 3，第二个清除a的bit 3。在以上两<br />个操作中，要保持其它</p><p>位不变。</p><p> a|=0x4;</p><p>int b=a&amp;0x7;<br />a&gt;&gt;=4;<br />a=(a&lt;&lt;4)|b;</p><p>Question 52. (问答)<br />有双向循环链表结点定义为：<br />struct node<br />{ int data;<br />struct node *front,*next;<br />};<br />有两个双向循环链表A，B，知道其头指针为：pHeadA,pHeadB，请写一函数将两链表中dat<br />a值相同的结点删除</p><p> struct node<br />{ int data;<br />struct node *front,*next;<br />};<br />node* del(node *pHead){<br />    if(pHead==pHead-&gt;next){<br />                  free(pHead);<br />        return NULL;<br />         }<br />         node *p=pHead;<br />    pHead-&gt;next-&gt;front=pHead-&gt;front;<br />    pHead-&gt;front-&gt;next=pHead-&gt;next;<br />    pHead=pHead-&gt;next;<br />         free(p);<br />    return pHead;<br />}<br />void delequ(node*pHeadA,node *pHeadB){<br />    node *pA=pHeadA;<br />    node *pB;<br />    int flag;<br />    do{<br />        flag=0;<br />        pB=pHeadB;<br />        do{<br />            if(pB-&gt;data==pA-&gt;data){<br />                if(pA==pHeadA){<br />                    pHeadA=pA=del(pHeadA);<br />                    flag=1;<br />                }<br />                else{<br />                    pA=del(pA);<br />                }<br />                pB=del(pB);<br />                break;<br />            }<br />            pB=pB-&gt;next;<br />        }while(pB!=pHeadB);<br />        if(flag==0){<br />            pA=pA-&gt;next;<br />        }<br />    }while(!pA &amp;&amp; !pB &amp;&amp; (flag||(pA!=pHeadA)));<br />}</p><p>Question 53. (问答)<br />编程实现：找出两个字符串中最大公共子字符串,如"abccade","dgcadde"的最大子串为"c<br />ad"</p><p> char* constr(char *A,char *B){<br />    int lenA=strlen(A),lenB=strlen(B);<br />    int itmp,conlen=0;<br />    char *pequ,*pH=NULL;<br />    char *pA,*pB,*pA2,*pB2;<br />    for(pA=A;pA-A<lena-conlen;pa++){><br />        for(pB=B;pB-B<lenb-conlen;){><br />            if(*pB==*pA){<br />                pA2=pA+1;<br />                pB2=pB+1;<br />                for(itmp=1;*pA2==*pB2 &amp;&amp; pA2++-A<lena &&="" pb2++-b<lenb;itmp++);<br="" />                if(itmp&gt;conlen){<br />                    conlen=itmp;<br />                    pH=pA;<br />                }<br />            }<br />            pB++;<br />        }<br />    }<br />    pequ=(char*)malloc(conlen+1);<br />    memcpy(pequ,pH,conlen);<br />    *(pequ+conlen)='\0';<br />    return pequ;<br />}</lenb-conlen;){></lena-conlen;pa++){></p><p>Question 54. (问答)<br />说明关键字static和volatile的含义</p><p> static用于外部变量或者函数时，限制了它们的作用域为文件内部；static也可以用于内<br />部变量，这样，内部变</p><p>量在每次函数调用时都为同一存储，值具有连续性。</p><p>volatile说明了一个变量的值是会随机变化的，即使程序没有对它进行任何赋值操作。它<br />告诉编译器的优化处理器</p><p>，这些变量是实实在在存在的，在优化过程中不能无故消除，这样就保证了编译后的代码<br />在每次操作是从变量地址</p><p>处取数。</p><p>Question 55. (问答)<br />编程实现：把十进制数(long型)分别以二进制和十六进制形式输出，不能使用printf系列<br />库函数</p><p> void put(long data){<br />    long mask=0x1&lt;&lt;(8*sizeof(long)-1);<br />    int i;<br />    char c;<br />    if(data&amp;mask)<br />    putchar('1');<br />    else<br />    putchar('0');<br />    mask=0x1&lt;&lt;(8*sizeof(long)-2);<br />    for(i=1;i&lt;8*sizeof(long);i++){<br />        if(data&amp;mask)<br />            putchar('1');<br />        else<br />            putchar('0');<br />        mask&gt;&gt;=1;<br />    }<br />    putchar('\n');<br />    mask=0xf&lt;&lt;(8*sizeof(long)-4);<br />    c=(data&amp;mask)&gt;&gt;(8*sizeof(long)-4);<br />    if(c&lt;10)<br />    putchar(c+'0');<br />    else<br />    putchar(c+'a');<br />    mask=0xf&lt;&lt;(8*sizeof(long)-8);<br />    for(i=1;i&lt;2*sizeof(long);i++){<br />        c=(data&amp;mask)&gt;&gt;(8*sizeof(long)-4*i-4);<br />        if(c&lt;10)<br />            putchar(c+'0');<br />        else<br />            putchar(c+'a');<br />        mask&gt;&gt;=4;<br />    }<br />}</p><p> </p><p>////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////<br />在这里公布选择题的答案没有意义的，选择题是随机从题库中提取的，每个人只有很少几<br />个是一样的，大部分都不一样。</p><p>最后的5个问答题是一样的。这里提供第54，55题的答案，仅供参考！<br />＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝<br />54题：<br />在C中，static变量的含义。<br />A:static 声明的变量在C语言中有两方面的特征：<br />　　1)、变量会被放在程序的全局存储区中，这样可以在下一次调用的时候还可以保持原<br />来的赋值。这一点是它与堆栈变量和堆变量的区别。<br />　　2)、变量用static告知编译器，自己仅仅在变量的作用范围内可见。这一点是它与全<br />局变量的区别。<br />Tips:<br />　　A.若全局变量仅在单个C文件中访问，则可以将这个变量修改为静态全局变量，以降低<br />模块间的耦合度；<br />　　B.若全局变量仅由单个函数访问，则可以将这个变量改为该函数的静态局部变量，以<br />降低模块间的耦合度；<br />　　C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时，需要考虑<br />重入问题；<br />    D.如果我们需要一个可重入的函数，那么，我们一定要避免函数中使用static变量(这<br />样的函数被称为：带“内部存储器”功能的的函数)<br />     E.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时，则必须<br />是static的局部变量的地址作为返回值，若为auto类型，则返回为错指针。</p><p>=====================================</p><p>volatile总是与优化有关，编译器有一种技术叫做数据流分析，分析程序中的变量在哪里<br />赋值、在哪里使用、在哪里失效，分析结果可以用于常量合并，常量传播等优化，进一步<br />可以死代码消除。但有时这些优化不是程序所需要的，这时可以用volatile关键字禁止做<br />这些优化，volatile的字面含义是易变的，它有下面的作用：</p><p>1 不会在两个操作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环<br />境下，变量可能被其他的程序改变，编译器 自己无法知道，volatile就是告诉编译器这种<br />情况。</p><p>2 不做常量合并、常量传播等优化，所以像下面的代码：<br />volatile int i = 1;<br />if (i &gt; 0) ...</p><p>if的条件不会当作无条件真。</p><p>3 对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到，编译器常<br />常可以省略那个赋值操作，然而对Memory Mapped IO的处理是不能这样优化的。</p><p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p><p>55题，递归算法：<br />/*长整型转变成二进制*/<br />void binary(long n)<br />{<br />    char c;<br />    if(n/2!=0)<br />        binary(n/2);<br />        c=n%2+48;<br />        putch(c);<br />}</p><p><br />/*长整型转变成十六进制*/</p><p>void hex(long n)<br />{<br />    if(n/16!=0)<br />        hex(n/16);<br />        if(n%16&gt;=0&amp;&amp;n%16&lt;=9)<br />            {c=n%16+48;<br />             putch(c);<br />            }<br />        else<br />            {c=n%16-10+97;<br />            putch(c);<br />            }</p><p>}  </p><img src ="http://www.cnitblog.com/zouzheng/aggbug/21759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-11 11:16 <a href="http://www.cnitblog.com/zouzheng/articles/21759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言题库</title><link>http://www.cnitblog.com/zouzheng/articles/21758.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Thu, 11 Jan 2007 02:07:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21758.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21758.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21758.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21758.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21758.html</trackback:ping><description><![CDATA[*1 <br />A <br />一个C程序的执行是从_____。 <br /> A)本程序的main函数开始,到main函数结束 <br /> B)本程序文件的第一个函数开始,到本程序文件的最后一个函数结束 <br /> C)本程序的main函数开始,到本程序文件的最后一个函数结束 <br /> D)本程序文件的第一个函数开始,到本程序main函数结束 <br />*2 <br />C <br />以下叙述正确的是: <br />A)在C程序中,main函数必须位于程序的最前面 <br />B)C程序的每行中只能写一条语句 <br />C)C语言本身没有输入输出语句 <br />D)在对一个C程序进行编译的过程中,可发现注释中的拼写错误 <br />*3 <br />D <br />以下叙述不正确的是。 <br />A)一个C源程序可由一个或多个函数组成 <br />B)一个C源程序必须包含一个main函数 <br />C)C程序的基本组成单位是函数 <br />D)在C程序中,注释说明只能位于一条语句的后面 <br />*4 <br />C <br />C语言规定:在一个源程序中,main函数的位置 。 <br />A)必须在最开始 <br />B)必须在系统调用的库函数的后面 <br />C)可以任意 <br />D)必须在最后 <br />*5 <br />B <br />一个C语言程序是由 <br />A)一个主程序和若干子程序组成    B)函数组成 <br />C)若干过程组成                  D)若干子程序组成 <br />*6 <br />A <br />在C语言中(以16位PC机为例),5种基本数据类型的存储空间长度的排列顺序为 <br />A)char<int&nbsp;&nbsp;><1ong&nbsp;&nbsp;int>&lt;=FLOAT<double><br />B)char=int&lt;1ong  int&lt;=float<double><br />C)char&lt;  int  &lt;1ong  int=float=double <br />D)char=int  =1ong  int&lt;=float<double><br />*7 <br />C <br />若x,i,j和k都是int型变量,则执行下面表达式后x的值为 <br /> x=(i=4,j=16,k=32) <br /> A)4   B)16  C)32     D)52 <br />*8 <br />B <br />假设所有变量均为整型,则表达式(a=2,b=5,b++,a+b)的值是: <br /> A)7    B)8   C)6   D)2 <br />*9 <br />A <br />下列四组选项中,均不是C语言关键字的选项是 <br />A)define    B)getc    C)include   D)while <br /> IF           char       scanf        go <br />type          printf       case        pow <br />*10 <br />B <br />下面四个选项中,均是C语言关键字的选项是 <br />A)auto      B)switch    C)signed    D)if <br /> enum       typedef       union      struct <br /> include     continue      scanf      type <br />*11 <br />C <br />下面四个选项中,均是不合法的用户标识符的选项是。 <br /> A)  A            B)float       C)b-a       D)一123 <br />    P_0            1a0          goto          temp <br />    do             一A          int           INT <br />*12 <br />C <br />C语言中的标识符只能由字母、数字和下划线三种字符组成,且第一个字符 <br /> A)必须为字母            B)必须为下划线 <br /> C)必须为字母或下划线   D)可以是字母,数字和下划线中任一种字符 <br />*13 <br />A <br />下面四个选项中,均是合法整型常量的选项是: <br />A)160      B)一0xcdf    C)一01    D)一0x48a <br /> 一0xffff      01a       986, 012      2e5 <br />     011        0xe        0668         0x <br />*14 <br />D <br />下面四个选项中,均是不合法的整型常量的选项是: <br />A)一一0f1    B)一0Xcdf  C)一018    D)一0x48eg <br />一0xffff          017        999        一068 <br />   <br />      0011        12,456      5e2         03f <br />*15 <br />B <br />下面四个选项中,均是不合法的浮点数的选项是 <br />A)  160.    B)  123     C)一.18    D)一e3 <br />   0.12        2e4.2     123e4          .234 <br />     e3        .e5        0.0           1e3       <br />*16  <br />b <br />下面四个选项中,均是合法浮点数的选项是 <br />  A)  +le+1    B)-.60    C)  123e      D)一e3 <br />      5e一9.4    12e-4     1.2e-.4     .8e4 <br />      03e2     一8e5        +2e一1     5.e一0 <br />*17 <br />A <br />下面四个选项中,均是合法转义字符的选项是 <br /> A)'\''        B)'\'    C)'\018'   D)'\\0' <br />    '\\'     '\017'    '\f'       '101' <br />    '\n'     '\"'       'xab'       'xlf' <br />*18 <br />B <br />下面四个选项中,均是不合法的转义字符的选项是 <br />  A)'\''     B)'\1011'    C)'\011'    D)'\abc' <br />    '\\'         '\'        '\f'         '\101' <br />    '\xf'       '\a'         '\}'        'xlf' <br />*19 <br />C <br />下面正确的字符常量是: <br />A)"a"    B)'\\''    C)'W'    D)'' <br />*20 <br />d <br />   <br />下面四个选项中,均是不正确的八进制数或十六进制数的选项是。 <br />    A)  016      B)oabc       C)010      D)  0a12 <br />0x8f  017 -0x11   7ff <br />018   0x8  0x16   -123 <br />*21 <br />C <br />下面四个选项中,均是正确的八进制数或十六进制数的选项是。 <br /> A)一10 B)0abc      C)0010   0)  0al2 <br />   0x8f    一017       一0x11-0xl23 <br />  一011     0xc 0xf1      一0x8 <br />*22 <br />d <br />下面四个选项中、均是正确的数值常量或字符常量的选项是: <br />   A)0.0       日)"a"     C)'3'D)十001 <br />      0f  3.9E-2.5       011     0xabcd <br />     8.9e   lel0xFFOO  2e2 <br />      '&amp;' '\'''       0a       50. <br />*23 <br />A <br />下面不正确的字符串常量是。 <br />  A)'abc'  B)"12'12"     C)"0"     D)"" <br />*24 <br />c <br />若有代数式3ae/bc,则不正确的C语言表达式是: <br /> A) a/b/c*e*3      B)3*a*e/b/c <br /> C)3*a*e/b*c       D)a*e/c/b*3 <br />*25 <br />A <br />已知各变量的类型说明如下: <br />   int   k,a,b; <br />   unsigned  long  w= 5; <br />  double    x=1.42; <br />则以下不符合C语言语法的表达式是: <br />A)  x%(一3)   B)  w+=-2 <br />C)k=(a=2,b=3,a十b)      D)a十= a一=(b=4)*(a=3) <br />*26 <br />A <br />已知各变量的类型说明如下: <br /> int i=8,k,a,b; <br />unsigned  long  w=5; <br /> double  x=1.42,y=5.2; <br />则以下符合C语言语法的表达式是: <br />A)  a十=a一=(b= 4)*(a=3)  B)a=a*3=2 <br />C)  x%(一3)  D)y=f1oat(i) <br />*27 <br />D <br />以下不正确的叙述是 <br />A)在C程序中,逗号运算符的优先级最低 <br />B)在C程序中,APH和aph是两个不同的变量 <br />C)若a和b类型相同,在执行了赋值表达式a=b后b中的值将放人a中,而b中的值不变。 <br />D)当从键盘输入数据时,对于整型变量只能输入整型数值,对于实型变量只能输入实型数值。 <br />*28 <br />D <br />以下正确的叙述是: <br />A)在C程序中,每行中只能写一条语句 <br />B)若a是实型变量,C程序中允许赋值a=10,因此实型变量中允许存放整型数 <br />C)在C程序中,无论是整数还是实数,都能被准确无误地表示 <br />D)在C程序中,%是只能用于整数运算的运算符 <br />*29 <br />B <br />以下符合C语言语法的赋值表达式是 <br />A)d=9+e+f=d+9B)  d=9+e,f=d+9 <br />C)d=9十e,e++,d十9     D)d=9十e++=d十7 <br />*30 <br />A <br />已知字母A的ASCII码为十进制数65,且c2为字符型,则执行语句c2='A'十'6'一'3';后,c2中的值为 <br />A)D     B)68   C)不确定的值     D)C <br />*31 <br />D <br />在C语言中,要求运算数必须是整型的运算符是。 <br />A) /   B) ++  c) !=   D)% <br />*32 <br />B <br />若以下变量均是整型,且num=sum=7;则执行表达式sUM=num++,sUM++,++num后sum的值为 <br /> A)  7    B)  8  C)  9  D)  10 <br />*33 <br />D <br />在C语言中,int、char和short种类型数据在内存中所占用的字节数 <br />A)由用户自己定义     B)均为2个字节 <br />C)是任意的   D)由所用机器的机器字长决定 <br />*34 <br />A <br />若有说明语句:char c='\72'则变量c <br />A)包含1个字符   B)包含2个字符 <br />C)包含3个字符   D)说明不合法,c的值不确定 <br />*35 <br />A <br />若有定义:int a=7;float x=2.5,y=4.7;则表达式x+a%3*(int)(x+y)%2/4的值是 <br />A)2•500000  B)2.7500OO  c)  3.500000  D)  0.000000。 <br />*36 <br />B <br />sizeof(float)是: <br />A)一个双精度型表达式  B)一个整型表达式 <br />C)一种函数调用D)一个不合法的表达式 <br />*37 <br />C <br />设变量a是整型,f是实型,i是双精度型,则表达式10+'a'+i*f值的数据类型为 <br /> A)int     B)  float  C)  double  D)不确定 <br />*38 <br />A <br />下面四个选项中,均是非法常量的选项是。 <br />A)'as'    B)'\\'  c)-0xl8    D)0xabc <br />  -0fff '\01'    01177     '\0' <br />'\0xa'     12,456     0xf"a" <br />*39 <br />D <br />在C语言中,  char型数据在内存中的存储形式是: <br />A)补码 B)反码 C)原码     D)ASCII码 <br />*40 <br />B <br />设变量n为f1oat类型,m为int类型,则以下能实现将n中的数值保留小数点后两位,第三位进行四舍五人运算的表达式是一一一。 <br />A) n=(n*100+0.5)/100.0     B)m=n*100+0.5 ,n= m/100.0 <br />C) n=n*100+0.5/100.0   D)n=(n/100+0.5)*100.0 <br />*41 <br />C <br />表达式18/4*sqrt(4.0)/8值的数据类型为: <br />A)  int    B)float   C)double  D)不确定 <br />*42 <br />C <br />设C语言中,一个int型数据在内存中占2个字节,则unsigned  int型数据的取值范围为: <br />   <br />A)  0～255   B) 0～32767     C) 0～65535  D)  0～2147483647 <br />*43 <br />D <br />设有说明:  char w; int  x;  f1oat  y;  double  z;则表达式w* x+z一y值的数据类型为: <br />A)float    B)char   C)int     D)double <br />*44 <br />D <br />若有以下定义,则能使值为3的表达式是: <br />int  k= 7,x =12; <br />A)  x%=(k%= 5)B)  x%=(k一k%5) <br />C)  x%=k- k%5   D)  (x%=k)一(k%=5) <br />*45 <br />C <br />设以下变量均为int类型,则值不等于7的表达式是 <br />A)  (x= y= 6,  x+y,x+1)   B)(x= y= 6,x+y,y+1) <br />C)  (x= 6,x+1,y= 6,x+y)   D)  (y=6,y+l,x = y,x+1) <br />*46 <br />D <br />putchar函数可以向终端输出一个: <br />A)整型变量表达式值 <br />B)实型变量值 <br />C)字符串 <br />D)字符或字符型变量值 <br />*47 <br />B <br />printf函数中用到格式符%5s ,其中数字5表示输出的字符串占用5列。如果字符串长度大于5,则输出按方式 <br />A)从左起输出该字串,右补空格 <br />B)按原字符长从左向右全部输出 <br />C)右对齐输出该字串,左补空格 <br />D)输出错误信息 <br />*48 <br />C <br />printf函数中用到格式符%5s ,其中数字5表示输出的字符串占用5列。如果字符串长度小于5,则输出按方式。 <br />A)从左起输出该字串,右补空格 <br />B)按原字符长从左向右全部输出 <br />C)右对齐输出该字串,左补空格 <br />D)输出错误信息 <br />*49 <br />D <br />已有定义 int a=-2;和输出语句:printf("%8lx",a);以下正确的叙述是: <br />A)整型变量的输出格式符只有%d一种 <br />B)%x 是格式符的一种,它可以适用于任何一种类型的数据 <br />C)%x 是格式符的一种,其变量的值按十六进制输出,但%8lx 是错误的 <br />D)%8lx 不是错误的格式符,其中数字8规定了输出字段的宽度 <br />*50 <br />D <br />若x ,y均定义为int型,z定义为double型,以下不合法的scanf函数调用语句是: <br />A)  scanf(" %d%lx,%le",&amp;x,&amp;y,&amp;z); <br />B)  scanf("%2d * %d%lf"&amp;x,&amp;y,&amp;z );   <br />C)  scanf("%x %* d%o",&amp;x,&amp;y); <br />D)  scanf("%x%o%6.2f",&amp;x,&amp;y,&amp;z);    <br />*51 <br />A <br />已有如下定义和输入语句 ,若要求a1,a2,c1,c2值分别为10,20,A和B,当从第一列开始输入数据时,正确的数据输入方式是 <br />int  a1,a2;  char  c1,c2; <br />scanf("%d%c%d%c",&amp;a1,&amp;c1,&amp;a2,&amp;c2); <br />A)10A 20B&lt; CR&gt;     B)10 A 20 B<cr /><br />C)10A20<cr />   D)10A20 B<cr />。 <br />*52 <br />B <br />已有定义int  x;  f1oat  y;且执行scanf("%3d%f",&amp;x,&amp;y);语句,若从第一列开始输入数据12345  678(回车),则x 的值为 <br />A)12345  B)123   。  C)  45       D)  345 <br />*53 <br />B <br />已有定义int  x;  f1oat  y;且执行scanf("%3d%f",&amp;x,&amp;y);语句,若从第一列开始输入数据12345  678(回车),则y 的值为: <br />A)无定值B)45.0000  C)  678.000000  D)  123.00000 <br />*54 <br />D <br />已有如下定义和输入语句,若要求a1,a2,c1,c2的值分别为10,20,A,B,当从第一列开始输入数据时,正确的数据输入方式是: <br />int  a1,a2;  char  c1,c2; <br />scanf("%d%d",&amp;a1,&amp;a2); <br />scanf("%c%c",&amp;c1,&amp;c2); <br /> A)  1020AB<cr />    B)10  20<cr />AB<cr /><br /> C)10  20   AB<cr />   D)10 20AB<cr /><br />*55 <br />D <br />已有程序段和输入数据的形式如下,程序中输入语句的正确形式应当为 <br /> main() <br />{int  a;float f; <br />    printf("\nInput number:"); <br />  输入语句 <br />    printf("\nf=%f,a= %d\n ,f,a); <br />} <br />Input  number:  4.5   2<cr /><br />A )scanf("%d,%f",&amp;a,&amp;f);  B)scanf("%f,%d",&amp;f,&amp;a) <br />C)scanf("%d%f",&amp;a,&amp;f);   D)scanf("%f%d",&amp;f,&amp;a); <br />*56 <br />B <br />根据定义和数据的输入方式,输入语句的正确形式为: <br />已有定义:  float  fl,f2; <br />数据的输入方式:4.52 <br />3. 5 <br />A)scanf("%f,%f",&amp;f1,&amp;f2);  B)scanf("%f%f",&amp;f1,&amp;f2 ); <br />C)scanf("%3.2f%2.1f",&amp;f1,&amp;f); D)scanf("%3.2f%2.1f",&amp;f1,&amp;f2); <br />*57 <br />D <br />阅读以下程序,当输入数据的形式为25,13,10<cr />正确的输出结果为: <br /> main() <br />{int  x,y,z <br /> scanf("%d%d%d",&amp;x,&amp;y,&amp;z ); <br /> printf("x+y+z=%d\n ,x+y+z);。 <br /> } <br /> A)x +y+z=48      B)x +y+z=35 <br /> C)x +z=35D)不确定值 <br />*58 <br />B <br />阅读 下程序,若运行结果为如下形式  ,输入输出语句的正确内容是: <br />main() <br />{int  x;  float  y;printf("enter  x,y  :") <br />输入语句    输出语句 <br />} <br />输入形式    enter  x,y:  2  3.4 <br />输出形式    x+y=5.40 <br />A)scanf("%d,%f",&amp;x,&amp;y);printf("\nx+y= %4.2f",x+y); <br />B)scanf("%d%f",&amp;x,&amp;y );printf("\nx+y=%4.2f",x+y); <br />C)scanf("%d%f",&amp;x,&amp;y); printf("\nx+y=%6.lf",x+y); <br />D)scanf("%d%3.1f",&amp;x,&amp;y );printf("\nx十y=%4.2f",x+y); <br />*59 <br />D <br />以下说法正确的是: <br />A)输入项可以为一实型常量,如scanf("%f",3.5); <br />B)只有格式控制,没有输入项,也能进行正确输入,如scanf("a=%d,b=%d"); <br />C)当输入一个实型数据时,格式控制部分应规定小数点后的位数, <br />如scanf("%4.2f",&amp;f); <br />D)当输入数据时,必须指明变量的地址,如scanf("%f",&amp;f); <br />*60 <br />A <br />根据下面的程序及数据的输入方式和输出形式,程序中输入语句的正确形式应该为: <br /> main() <br />{char  ch1  ,ch2  ,ch3; <br /> 输入语句 <br />printf("%c%c%c",ch1,ch2,ch3);} <br /> 输入形式:  A B C <br /> 输出形式:  A B <br /> A)  scanf("%c%c%c",&amp;ch1,&amp;ch2,&amp;ch3); <br /> B)  scanf("%c,%c,%c", &amp;ch1,&amp;ch2, &amp;ch3); <br /> C)  scanf("%c %c %c",&amp;ch1,&amp;ch2,&amp;ch3); <br /> D)  scanf("%c%c",&amp;ch1,&amp;ch2,&amp;ch3); <br />*61 <br />D <br />有输入语句:  scanf("a= %d,b= %d,c=%d",&amp;a,&amp;b,&amp;c);为使变量a的值为1,b为3,c为2,从键盘输入数据的正确形式应当是 <br />A)132(回车) <br />B)1,3,2(回车) <br />C)a=1b=3c=2(回车) <br />D)a=1,b =3,c =2(回车) <br />*62 <br />A <br />以下能正确地定义整型变量a,b和c 并为其赋初值5的语句是 <br /> A)int a=b= c= 5;       B) int a,b, c= 5; <br /> C)a= 5,b=5,c=5;     D)  a= b= c=5; <br />*63 <br />A <br />已知ch是字符型变量,下面不正确的赋值语句是 <br />A)ch='a+b';  B)ch='\0';  C)ch='7'+'9';  D)  ch=5+9; <br />*64 <br />B <br />已知ch是字符型变量,下面正确的赋值语句是  <br />A)  ch= '123';  B)  ch= '\xff';  C)  ch= '\08';  D)  ch="\"; <br />*65 <br />B <br />若有以下定义,则正确的赋值语句是: <br /> int  a,b;float  x; <br />  A)  a=1,b=2,    B)b++;  C)  a= b= 5     D)  b= int(x); <br />*66 <br />B <br />设x 、y均为f1oat型变量,则以下不合法的赋值语句是 <br />A)++x;   B)y=(x%2)/10; C)  x*=y+8;  D)x=y=0; <br />*67 <br />A <br />x,y,z均为int型变量,则执行语句x=(y=(z=10)+5)一5;后,x 、y 和之的值是: <br />  A)  x=10    B)  x=10  C)  x=10  D)  x=10 <br />       y=15 y=10       y=10       y=5 <br />       z=10 z=10       z=15       z=10 <br />       <br />*68   <br />D   <br />逻辑运算符两侧运算对象的数据类型一一一。 <br />  A)只能是0或1 <br />  B)只能是0或非0正数 <br />  C)只能是整型或字符型数据 <br />  D)可以是任何类型的数据 <br />*69 <br />C <br />下关于运算符优先顺序的描述中正确的是一一一。 <br />A)关系运算符&lt; 算术运算符&lt; 赋值运算符&lt; 逻辑与运算符 <br />B)逻辑 运算符&lt; 关系运算符&lt; 算术运算符&lt; 赋值运算符 <br />C)赋值运算符&lt; 逻辑与运算符&lt; 关系运算符&lt; 算术运算符 <br />D)算术运算符&lt; 关系运算符&lt; 赋值运算符&lt; 逻辑与运算符 <br />*70 <br />B <br />下列运算符中优先级最高的是一一一。 <br />  A)&lt;   B)十   C)&amp;&amp;     D)!= <br />*71 <br />C <br />能正确表示"当x 的取值在[1,10]和[200,210]范围内为真,否则为假"的表达式是一一一。 <br />  A)  (x&gt;=1)  &amp;&amp;(x&lt;=10)  &amp;&amp;(x&gt; = 200)  &amp;&amp;(x&lt;=210) <br />  B)  (x&gt;=1)  || (x&lt;=10)  ||(x&gt;=200)  ||(x&lt;=210) <br />  c)  (x&gt;=1)  &amp;&amp;(x&lt;=10)||(x&gt;= 200)  &amp;&amp;(x&lt;=210) <br />  D) (x &gt; =1)||(x&lt; =10)  &amp;&amp;  (x&gt; = 200)||(x&lt;=210) <br />*72 <br />C <br />表示图中坐标轴上阴影部分的正确表达式是: <br />      /////||//////| <br />      /////||//////| <br />   -----------------------------&gt;   X <br />  a  b      c      <br />  A)(X&lt;=A)&amp;&amp;(X&gt;=B)&amp;&amp;(X&lt;=C) <br />  B) (x&lt; =a)||(b&lt;=x&lt;=c) <br />  C)(x&lt;=a)|| (x&gt; =b)  &amp;&amp;  (x&lt;=c) <br />  D)(x &lt; =a)&amp;&amp;(b&lt; = x &lt; = c) <br />*73 <br />C <br />判断char型变量ch是否为大写字母的正确表达式是一一一。 <br />  A)' A' &lt;=ch&lt;='z'     B)(ch&gt; = 'A')&amp;(ch&lt;=' z' ) <br />  C)(ch&gt;=' A' )&amp;&amp;(ch&lt;='z') D)(' A' &lt; = ch)AND('z'&gt; = ch) <br />*74 <br />D <br />设x,y 和z是int型变量,且x = 3,y= 4,z= 5,则下面表达式中值为0的是 <br />  A)'y'&amp;&amp;'y' <br />  B)x &lt; =y <br />  C) x||y+z&amp;&amp;y-z <br />  D) !(x<y)&&!z||1)><br />*75 <br />C <br />已知x =43,ch='A',y=0则表达式(x &gt; = y &amp;&amp; ch&lt;'B'&amp;&amp;!Y) 的值是 <br />   A)0   B)语法错   C)1   D)"假"   <br />*76 <br />C <br />若希望当A的值为奇数时,表达式的值为"真",A的值为偶数 表达式的值 <br />   <br />为"假"。则以下不能满足要求的表达式是: <br />A)  A%2= =1    B)!(A%2 = =0)   C)!(A%2)    D)  A%2 <br />*77 <br />B <br />设有 int a = 1,b=2,c =3, d =4, m=2, n=2; <br />       执行(m= a&gt;b)&amp;&amp;(n=c&gt;d)后n的值为: <br />    A)1B )2       C)3      D)4 <br /> *78 <br /> D      <br />判断char型变量c1是否为小写字母 正确表达式为: <br />    A)' a' &lt;=cl&lt;=' z'    B)  (cl&gt; = a)&amp;&amp;(cl&lt;=z) <br />    C)(' a' &gt;=cl)||(' z' &lt; = cl)  D)(c1&gt;='a')&amp;&amp;(cl&lt;='z') <br />*79 <br />B <br />以下程序的运行结果是: <br />   #include  "stdio.h" <br />    main()      ,,  \ <br />    {int  a,b,d= 241; <br />      a=d/100 % 9 <br />      b= (一1)&amp;&amp;(一1); <br />    printf("%d,%d",a  ,b); <br />  }  <br />  A)6,1     B)2,1     C) 6,0     D)2,0 <br />*80 <br />C <br />执行以下语句后a的值为: <br />int a,b,c; <br />a= b= c= 1; <br />++a||++b&amp;&amp;++c     <br />  A)错误    B)  0      C)  2  D)  1 <br />*81 <br />A <br /> 执行以下语句后b 的值为: <br />    int  a=5,b=6,w=1,x=2,y=3,z=4; <br />    (a=w&gt;x)&amp;&amp;(b=y&gt;z); <br />  A)  6     B)  0     C)  1      D)  4 <br />*82 <br />C <br />以下不正确的if语句形式是:    <br />  A)if(x&gt;y &amp;&amp; x!=y); <br />  B)if(x= =y)  x+=y     <br />  C)if(x != y)scanf("%d",&amp;x )else scanf("%d",&amp;y); <br />  D)if(X<y)&nbsp;{x++;y++;}><br />*83 <br />A <br />下列运算符中优先级最低的是: <br />  A)?:   B)&amp;&amp;     c)+     D)  != <br />*84 <br />B <br />以下if语句语法正确的是一一一。 <br />    A)if(x&gt;0) <br />printf("%f",x) <br />else  printf("%f",- -x); <br />    B) if (x&gt;0) <br />       {x=x+y;printf("%f",x);} <br />       else  printf("f",-x); <br />    C) if(x&gt;0) <br />       {x=x+y;  prinrf("%f",x);}; <br />       else  printf("%f",-x); <br />    D) if(x &gt; 0) <br />       { x = x +y;printf("%f",x)} <br />else  printf("%f",-x); <br />*85 <br />D <br />   <br />请阅读以下程序: <br />    main() <br />    {int  a=5,b=0,c=0;  <br />    if(a=b+c)  printf("***\n  "); <br />    e1se   printf("$$$\n"); <br />   以上程序 : <br />      A)有语法错不能通过编译   B)可以通过编译但不能通过连接 <br />      C)输出***D)输出$$$ <br />*86 <br />C <br />以下程序的运行结果是 : <br />     main()     <br />    {int m=5; <br />    if(m++&gt; 5)  printf(" %d\n",m); <br />    e1se     printf("%d\n",m- - ); <br />  } <br />  A)4    B)5   C)6    D)7 <br />*87 <br />B <br />当a=1,b=3,c=5,d=4 ,执行完下面一段程序后x 的值是 : <br />       if(a<b)><br />       if(c<d)x><br />       else <br />       if(a<c)><br />       if(b<d)x><br />       else x= 3; <br />       else x=6; <br />       else x=7; <br />      A)18)2      C)3      D)6 <br />*88 <br />C <br />以下程序的输出结果是: <br />   main() <br />  {int  a=100,x =10,y=20,okl=5,ok2=0; <br />    if  (x<y)><br />    if(y ! =10) <br />    if(!okl)  a=1; <br />   else <br />     if(ok2)  a=10; <br />      a=-1: <br />    printf( "%d\n",a ) <br />  } <br />  A)  1      B)  0    C)一1    D)值不确定 <br />*89 <br />B <br />以下程序的输出结果是: <br />      main() <br />      {int  x= 2,y= 一1,z=2; <br />if (x<y)&nbsp;><br /> if(y&lt;0)  z= 0; <br />  else    z+ =1; <br />    printf("%d\n",z); <br />    } <br />    A)3      B )2       C)1      D) 0 <br />*90 <br />B <br />为了避免在嵌套的条件语句If一else中产生二义性,C语言规定else子句 <br />    总是与(  )配对。 <br />    A)缩排位置相同的if     B)其之前最近的if <br />    C) 之后最近的if D)同一行上的if <br />*91 <br />B <br />以下不正确的语句为: <br />  A)  if(x&gt;y); <br />  B)  if  (x= y) &amp;&amp;(x! = 0)  x+= y; <br />  C)  if(x!= y)scanf("%d",&amp;x);  else  scanf("%d",&amp;y); <br />  D)if(x&lt; y){x++ ;y十十;} <br />*92 <br />B <br />请阅读以下程序: <br />      #include<stdio.&nbsp;h /><br />       main() <br />      {float  a,b <br />      scanf("%f",&amp;a); <br />      if (a&lt;10.0) b=1.0/x; <br />      else if((a&lt;0.5)&amp;&amp;(a!=2.0))b= 1.0/(a十2.0); <br />      else  if(a&lt;10.0)  b= 1.0/x ; <br />      else  b=10.0; <br />    printf("%f\n",y); <br />    } <br />    若运行时输入2.0(回车),则上面程序的输出结果是: <br />    A)0.000000   B)0.500000  C)  1. 000000   D) 0.250000 <br />*93 <br />B <br />若有条件表达式(exp) ?  a++:b--,则以下表达式中能完全等价于表 <br />    达式(exp)的是: <br />    A)  (exp==0) B)  (exp!=0) <br />    C)  (exp== 1) D)  (exp!=1)    <br />*94 <br />A <br />若运行时给变量x 输入12,则以下程序的运行结果是: <br />    main() <br />    {int  x,y;  <br />     scanf("%d",&amp;x); <br />    y=x &gt; 12?x+10:  x一12; <br />    printf("%d\n",y); <br />    } <br />      A)0      B)22C)12       0)10 <br />*95 <br />D <br />以下程序的运行结果是: <br />      main()<br />      {int  k= 4,a=3,b=2,c=1; <br />       printf("\n %d\n",k&lt; a ? k:c<a);><br />    } <br />    A)4      B )3     C)2      D)1 <br />*96 <br />B <br />执行以下程序段后、变量a,b,c的值分别是一。 <br />    int  x=10,y=9; <br />    int  a,b,c; <br />    a =(- -x = = y++)?- -x:++y ; <br />    b = x ++; <br />    c = y; <br />    A)a=9,b= 9,c= 9       B )a=8,b=8,c=10 <br />    C)a=9,b= 10,c=9      0)a=1,b= 11,c=10 <br />*97 <br />A <br />若w,x,y,z,m均为int型变量,则执行下面语句后的m值是: <br />    w= 1;x = 2;y= 3;z=4; <br />    m=(w<x;><br />    m= (m<y;><br />    m=(m<z;><br />    A)1B )2     C)3D)4 <br />*98 <br />D <br />若w=1,X = 2,y= 3,z=4,则条件表达式w<?XML:NAMESPACE PREFIX = Y<Z?Y /?><y><z?y:z的值是:&nbsp;&nbsp;><br /></z?y:z的值是:&nbsp;&nbsp;></y>nbsp;   A)4      B)3      C)20)1 <br />*99 <br />C <br />执行以下程序段后的输出结果是 <br />    int  w=3,  z=7,x =10; <br />    printf("%d\n",x&gt; 10?  x+100:x 一10); <br />    printf("%d\n",w++||z++ ); <br />    printf("%d\n",!w&gt;z); <br />    printf("%d\n",w&amp;&amp;z); <br />    A)0       B)   1       C)0      D) 0 <br />        1            1           1          1<br />        1            1           0          0<br />        1            1           1          0<br />*100 <br />C <br />设有程序段 <br />  int  k=10; <br />  while  (k=0)  k= k—1; <br />  则下面描述中正确的是 <br />A)  while循环执行10次      B)循环是无限循环 <br />C)循环体语句一次也不执行   D)循环体语句执行一次 <br />*101 <br />B <br />有以下程序段 <br />    int  x=0,s=0; <br />    while  (!  x!= 0)  s+=++x; <br />    printf("%d",s); <br />    则 <br />A)运行程序段后输出0 B)运行程序段后输出1 <br />C)程序段中的控制表达式是非法的       D)程序段执行无限次 <br />*101 <br />A <br />语句while(!E)中的表达式!E等价于: <br />A)  E==0   B)  E!=1  C)  E!=0    D)  E==1 <br />   <br />* <br />A <br />下面程序段的运行结果是 <br />    a=1;b= 2;c=2; <br />while(a<b><c)&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;t><br />printf("%d,%d,%d",a,b,c); <br />A)1,2,0    B)2,1,0    C)1,2,1    D)2,1,1 <br />*102 <br />   <br />D<br />下面程序段的运行结果是: <br />x = y= 0; <br />while(x&lt;15)  y++,x + = ++y; <br />printf("%d,%d",y,x); <br />A)  20,7    B)6,12      c)  20,8      D)8,20 <br />* <br />C <br />【题5.6】下面程序段的运行结果是 <br />  if    n=0; <br />  while(n++&lt;=2);  printf("%d",n);  A)  2      B)  3C)  4     D)有语法错 <br />*103 <br />B <br />设有程序段 <br />    t=0; <br />    while  (printf"*"))<br />     {t++; <br />      if(t&lt;3)  break <br />      } <br />  面描述正确的是: <br />  A)其中循环控制表达式与0等价       B)其中循环控制表达式与'0'等价 <br />  C)其中循环控制表达式是不合法的     D)以上说法部不对 <br />*104 <br />B <br />下面程序的功能是将从键盘输入的一对数,由小到大排序输出。当输入一对相等数时结束循环,请选择填空: <br />    #indude  <stdio.h /><br />      main() <br />      {int  a,b,t; <br />scanf("%d%d",&amp;a,&amp;b); <br />while(  【1】  ) <br />{if(a&gt;b) <br /> {t=a;a=b;b=t;} <br />  printf("%d,%d",a,b); <br />scahf("%d%d",&amp;a,&amp;b);人 <br />} <br />【1】   A)  !a=b    B)  a!=b     C)  a= =b     D)  a=b <br />*105 <br />C <br />下面程序的功能是从键盘输入的一组字符中统计出大写字母的个数m和小写字母的个数n,并输出m、n中的较大者,请选择填空: <br />    #indude   "stdio.h"    <br />      main() <br />      {int  m=0,n=0; <br />      char    c;  1 <br />      while((【1】)!='\n') <br />      { if(c&gt;='A' &amp;&amp; C&lt;='Z') m++ ; <br />      if(c&gt;='a'  &amp;&amp; c&lt;='z')  n++; }      <br />      printf("%d\n",  m<m);&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;><br />1】 A) c=getchar()  B)  getchar()  C)c=getchar()  D)  scanf("%c",c) <br />*106 <br />C <br />下面程序的功能是将小写字母变成对应大写字母后的第二个字母。其中y变成A,z 变成B.请选择填空。 <br />    #include "stdio. H" <br />    main() <br />    {    char c; <br />  while((c=getchar())!='\n')   <br />{if(c&gt;= 'a'&amp;&amp; c&lt;='z') <br />  c - = 30;    <br />   if(c&gt;'z' &amp;&amp; c&lt;='z'+ 2) <br />      【2】; } <br />    printf(" %c",c) <br />      } <br />  【2】A)  c='B'   B)  c='A'      C)  c-=26     D)  c=c+26 <br />*107 <br />B <br />下面程序的功能是在输入的一批正整数中求出最大者,输入0结束循环,请选择填空。 <br />#include  <stdio.h /><br />      main() <br />      {int a,max= 0; <br />      scanf("%d",&amp;a)   <br />      while(【1】)     <br />     {if(max<a&nbsp;&nbsp;max><br />      scanf("%d",&amp;a); } <br />   printf("%d" ,max  );} <br />       <br />【1】  A)  a==o    B)A     C)  !A = = 1D)!a  <br />*108 <br />C <br />下面程序的运行结果是。 <br />    #include<stdio.h&nbsp;&nbsp; /><br />    main() <br />    { int  num= 0;      <br />     while(num&lt;= 2) <br />     {num++     <br />    printf ("%d\n", num);} <br />       }     <br />    A)1      B )  1   c)1D)1 <br />    2      2  2 <br />   3  3 <br />       4      <br />*109 <br />C <br />以下程序段 <br />x= -1; <br />do <br />       {x=x*x;} <br />  while(!x); <br />    A)是死循环 B)循环执行二次 <br />    C)循环执行一次     D)有语法错误 <br />*110 <br />C <br />以下描述中正确的是: <br />     A)由于do一while循环中循环体语句只能是一条可执行语句,所以循环体内不能使用复合语句 <br />     B)  do一while循环由do开始,用while结束,在while(表达式)后面不能写分号 <br />     C)在do一while循环体中,一定要有能使while后表达式值变为零("假")的操作 <br />     D)do一while循环中,根据情况可以省略while <br />*111 <br />B <br />若有如下语句 <br /> int  x=3; <br /> do { printf(" %d\n",x -=2);} while(!(--x)); <br />    则上面程序段 <br />    A)输出的是 1  B)输出的是1和-2 <br />    C)输出的是3和0      D)是死循环 <br />*112 <br />C<br />下面程序的功能是计算正整数2345的各位数字平方和,请选择填空。 <br />    #include<stdio.&nbsp;h /><br />main() <br />       {int  n,sum=0; <br />n=2345 <br />do{ sum=sum+(n%10)*n%10);  <br />n=【2】; <br />}while(n); <br />printf("sum=%d",sum);}  <br />【2】 A)  n/1000   B)n/100      C)  n/10    D)  n%10 <br />*113 <br />B <br />下面程序是从键盘输入学号,然后输出学号中百位数字是3的学号,输入0时结束循环。请选择填空。 <br />   #  include<stdio.&nbsp;h /><br />    main() <br />  {1ong  int  num; <br />    scanf("%ld",&amp;num); <br />    do  { if( 【1】) printf("%ld",num); <br />scanf("%ld",&amp;num); <br />      }while(!num==0);} <br />  【1】 A)num%100/10==3 B)num/100%10==3<br />C)num%10/10==3 D)num/10%10==3<br /> <br />*114 <br />B <br />下面程序的功能是把316表示为两个加数的和,使两个加数分别能被13和11整除。请选择填空。 <br /> #include <stdio.h />  <br />   main() <br /> {int  i=0,j,k; <br />  do{  i++;k=316一13*i;} <br />  while(【1】);j=k/11; <br />  printf(" 316=13* %d十11*%d",i,j); <br />   } <br />【1】A)k/11    B) k%11    C) k/11==0   D) k/11== 0 <br />*115 <br />D <br />下面程序的运行结果是: <br />      #indude<stdio.h /><br />      main() <br />      {int  y=10; <br />       do{y--;} <br />       while(--y); <br />       printf("%d\n",y--); <br />} <br />    A)一1B) 1      C)8      D) 0 <br />*116 <br />D <br />若运行以下程序时,从键盘输入ADescriptor<cr />(CR)表示回车),则下面程序的运行结果是: <br />      #include<stdio.&nbsp;h /><br />      main()    <br />      { char c; <br />      int v0=0.v1=0,v2=0; <br />  do{ <br />    switch(c=getchar()) <br />      {case 'a':case  'A'  : <br />case 'e':case  ' E' : <br />case 'i':case 'I' : <br />case 'o':Case  'O'  : <br />case 'u':case  'U'  :vl+=1; <br />default:v0+=1;v2+=1  ;}     ; <br />while(c!='\n'); <br />      printf("v0=%d,v1=%d,v2=%d\n",v0,v1,v2); <br />  <br />    }     <br />A)v0=7,v1=4,v2=7B)  v0=8,v 1=4,v2=8     <br />C)v0= 11,v1=4,v2=11      D)  v0=12,vl=4,v2=12 <br />*117 <br />B <br />下面程序的运行结果是:     <br />      #include<stdio.&nbsp;h /><br />       main()   <br />      {int   a==1,b=10; <br /> do <br />{b-=a;a++;}while(b--&lt;0); <br />printf("a=%d,b=%d\n",a,b); <br />      }    ' <br />   A)a=3,b=11   B )a=2,b=8    C)a=1,  b=一1    D)a=4,b=9 <br />*118 <br />D <br />下面有关for循环的正确描述是:    <br />    A)  for循环只能用于循环次数已经确定的情况 <br />    B)  for循环是先执行循环体语句,后判断表达式 <br />    C)  在for循环中,不能用break语句跳出循环体 <br />    D)  for循环的循环体语句中, 可以包含多条语句,但必须用花括号括起来 <br />*119 <br />B <br />对for(表达式1;;表达式3)可理解为: <br />A)  for(表达式1;  0;表达式3) <br />B)  for(表达式1;1;表达式3) <br />C)    for(表达式1;表达式1;表达式3) <br />D)    for(表达式1;表达式3;表达式3) <br />*120 <br />B <br />若i为整型变量,则以下循环执行次数是: <br />  for  (i=2;i==0;)  printf("%d",i-- );  <br />  A)无限次   B) 0次   C) 1 次     D)  2次 <br />*121 <br />C <br />以下for循环的执行次数是: <br />  for  (x=0,y一0;  (y=123)&amp;&amp;(x&lt;4);  x++); <br />  A)是无限循环 B)循环次数不定 C)执行4次  D)执行3次 <br />*122 <br />A <br />以下不是无限循环的语句为: <br />  A)  for  (y=0,x=1;x &gt; ++y;x =i++)  i=x ; <br />  B)   for (;;  x++=i); <br />  C)  while  (1){x ++;} <br />  D)  for(i=10; ;i--)sum+=i; <br />*123 <br />C <br />下面程序段的运行结果是: <br />for (y= 1;y&lt;10;) y=((x=3* y,x+1),x-1); <br />printf  ("x=%d,y=%d",x,y); <br />A)x=27,y=27   B)x=12,y=13   C)x=15,y=14   D)x=y=27 <br />*124 <br />D <br />下面程序段的运行结果是 <br /> for(x=3;x&lt;6;x++)printf((x %2)?("**%d"):(" ##%d\n"),x); <br />   A)**3      B )##3     C)##3  D)**3##4 <br />      ##4  **4 **4##5 **5 <br />      **5  ##5 <br />*125 <br />C <br />下列程序段不是死循环的是 <br />      A)  int  i=100; <br />   whiLe  (1) <br />  {i=i%100+1; <br />  if  (i&gt;100)  break;     <br />   }    <br />      B)  for(;;);     <br />      C)  int  k=0;      <br />   do{++k;}  while  (k&gt; =0)  ; <br />      D)  int  s=36; <br />   while  (s);--s   <br />*126 <br />C <br />执行语句for(i=1;i++&lt;4;);后变量i的值是 <br />  A)3    B )4     C)5    D)不定 <br />       <br />*127 <br />D <br />下面程序的功能是计算:至50中是7的倍数的数值之和,请选择填空。 <br />     #include<stdio.&nbsp;h />    • <br />     main() <br />    {int  i,sum= 0;   <br />      for(i=1;i&lt;=50;i++) <br />if([1])  sum+=i; <br />      printf("%d",sum); <br />    }      <br />A) (int)(i/7)==i/7  <br />B) (int)i/7==i/7 <br />C)  i%7= 0 <br />D)  i%7==0 <br />*128 <br />B <br />下面程序的功能是计算 1至10之间的奇数之和及偶数之和。请选择填空; <br />    # include <stdio.&nbsp;h /><br />    main() <br />   {int  a,b,c,i; <br />     a=c=0; <br />    for(i=0;i&lt;=10;i+=2) <br />    { a+=i; <br />     [1]; <br />     c+=b; <br />} <br />     printf("偶数之和=%d\n",a);       <br />     printf("奇数之和=%d\n",c-11), <br />} <br />[1] A )b=i--  B)  b=i+1   C)  b=i++   D)b=i-1 <br />*129 <br />B <br />下面程序的运行结果是: <br />    #include  <stdio.&nbsp;h /><br />      main()      <br />    {int i; <br />for(i=1;i&lt;=5;i++) <br />  switch(i%5) <br />{case  0:printf("* ");break; <br />  case  1  :printf("#");break; <br />  default  :printf("\n"); <br />  case  2  :printf("&amp;"); <br />    } <br />      A)#&amp;&amp;&amp;*  B) #&amp;   C)  #    D)#&amp;  <br />        &amp;         &amp; <br />        &amp;         &amp; <br />                  * <br />*130 <br />D <br />下面程序的运行结果是: <br />    #include <stdio.&nbsp;h /><br />      main() <br />     {  int    x,i;     <br />      for(j-1;i&lt;=100;i++) <br />      {x=i; <br />  if(++x%2==0) <br />    if(++x%3==0) <br />   <br />      if(++x%7==0) <br />      printf("%d",x); <br />   <br />  } <br />      } <br />    A)39  81     日)42  84      C)26  68       D)  28  70 <br />*131 <br />C <br />下面程序段的功能是计算1000!的末尾含有多少个零。请选择填空。 <br />     (提示:只要算出1000!中含有因数5的个数即可 <br />    for(k=0,i=5;i&lt;=1000;  i+=5) <br />      <br />    while(【1】){k++; m=m/5;} <br />      <br />【1】A)m%5==0   B)m=m%5==0  C)m%5==0  D)m%5!=0 <br />*132 <br />D <br />下面程序的运行结果是: <br />    #include <stdio.h /><br />    main() <br />    {int i,b,k=0; <br />    for(i=1;i&lt; =5;i++) <br />      {b=i%2; <br />       while(b--&gt;=0)     k++; <br />      } <br />    printf("%d,%d",k,b); <br />  } <br />    A)3,-1     B )8,-1    C)3,0     D)8,-2 <br />*133 <br />B <br />以下正确的描述是。 <br />   <br />    A)continue语句的作用是结束整个循环的执行 <br />    B)只能在循环体内和switch语句体内使用break语句 <br />    C)在循环体内使用break语句或continue语句的作用相同 <br />    D)从多层循环嵌套中退出时, 只能使用goto语句 <br />   <br />*134 <br />D <br />下面程序段:     <br />      for  (t=1;  t&lt;=100;t++) <br />  {scanf("%d",&amp;x);  <br />   if(x&lt;0)  continue;  <br />      printf("%3d",t);}  <br />   A) 当x&lt;0时整个循环结束 <br />   B) x&gt;=0时什么也不输出 <br />   C) printf函数永远也不执行 <br />   D)最多允许输出100个非负整数 <br />*135 <br />C <br />下面程序段: <br />  x=3; <br />  do <br />  {y=x--; <br />  if(!y){ printf("x");continue;} <br />  printf("#"); <br />  }  while (1&lt;=x&lt;=2); <br />    A)将输出##       B)将输出##* <br />    C)是死循环 D)含有不合法的控制表达式 <br />   <br />   <br />*136 <br />C <br />以下描述正确的是 <br />    A)  goto语句只能用于退出多层循环 <br />    B)  switch语句中不能出现continue语句 <br />    C)  只能用continue语句来终止本次循环 <br />    D)  在循环中break语句不能独立出现 <br />*137 <br />C <br />与下面程序段等价的是: <br />  for(n=100;n&lt;= 200; n++) <br />  {if (n%3==0)  continue; <br />      printf("%4d",n);}  <br />     A) for(n=100;(n%3)&amp;&amp; n&lt;=200;n++) printf("%4d",n); <br />     B)  for(n=100;(n%3)|| n&lt;=200;n++) printf("%4d",n); <br />     C) for(n=100;n&lt;=200;n++)if(n%3!=0)printf("%4d",n) <br />     D) for(n=100;n&lt;=200;  n++) <br />      {if(n%3)  printf("%4d",n); <br />       else  continue; <br />       break;}  <br />*138 <br />B <br />下面程序的运行结果是:       <br />    #include<stdio.h />    <br />  { int k=0; <br />     char  c='A'; <br />      do <br />       {switch(c++) <br />{case  'A':k++;break; <br /> case  'B':k--; <br /> case  'C':k+=2;break; <br /> case  'D':k=k%2;continue; <br /> case  'E':k=k*10;breab; <br /> default:k=k/3;} <br /> k++;} <br />      while(c&lt;'G') <br /> printf("k=%d",k);}      <br />      A)k=3     B)k=4    C)k=2    D)k=0 <br />*139 <br />B <br />若运行以下程序时,从键盘输入3.6,2.4<cr />,(<cr />表示回车),则下面程序的运行结果是: <br />  #indude  <math.h /><br />  # include <stdio.h /> <br /> main() <br />  {float x,y,z; <br />   scanf("%f%f",&amp;x,&amp;y); <br />   z=x/y; <br />   while(1) <br />    {if(fabs(z)&gt;1.0) {x=y;y=z;z=x/y;} <br />    else break;} <br />    printf("%f",y);} <br />   A) 1.5  B)1.6 C) 2.0 D)  2.4    <br />   <br />*140 <br />B <br />下面程序的运行结果是: <br />#include  "stdio.h" <br />main() <br />{int a,b; <br />for(a=1,b=1;a&lt;=100;a++) <br />{if (b&gt;=20) break; <br />if(b%3==1) {b+=3;continue;} <br />b-=5;} <br />printf("%d\n",a);} <br />A) 7      B) 8     C)9      D) 10 <br />*141 <br />B <br />下面程序的运行结果是: <br /> #include "stdio.h" <br /> main() <br />{int i; <br />for(i=1;i&lt;=5;i++)  <br />{if (i%2) printf("#"); <br />else continue; <br />printf("*"); <br />} <br />printf("$");} <br />A) *#*#$   B)   #*#*#*$   C) *#*#$   D)#*#*$ <br />*142 <br />A <br />下面程序的运行结果是: <br />  <br />main() <br />{int i,j,a=0; <br />for (i=0;i&lt;2;i++) <br />{ for (j=0;j&lt;=4;j++) <br />{if (j%2)  break; <br />a++;} <br />a++;} <br />printf("%d\n",a); <br />} <br />A)  4       B)  5       C)  6      D)  7  <br />   <br />*179 <br />c <br />在c语言中,引用数组元素时,其数组下标的数据类型允许是__. <br />        A)整型常量         B)整型表达式 <br />        c)整型常量或整型表达式D)任何类型的表达式 <br />*180 <br />D <br />以下对一维整型数组a的正确说明是__。 <br />        A)  int a(10);  B)int n= 10,a[n]; <br />        c)  int  n;       D)  #define  SIZE  10; <br />scanf("%",&amp;J1);  int a[SIZE]; <br />int a[n]; <br />*181 <br />D <br />若有说明:int a[10];则对a数组元素的正确引用是__。 <br />        A)a[10] B) a[3.5] C) a(5) D) a[10-10] <br /><p>*182 <br />A <br />在C语言中,一维数组的定义方式为:,类型说说明符 数组名__。 <br />A) [常量表达式]   B) [整形表达式]     <br />c)[ 整型常量]或[整型表达式] D)[整型常量] <br />   <br />*183 <br />C <br />以下能对一维数组a进行正确初始化的语句是__。 <br />        A)  int a[10]=(0,0,0,0,0) B)int  a[10]={} <br />C)  int  a[]={0};D) int a[10]={10*1}; <br />*184 <br />C <br />以下对二维数组a的正确说明是__。 <br />      A)  int a[3][];   B)  floatf a(3,4); <br />       c)  double a[1][4];  D)  float a(3)(4); <br />*185 <br />C <br />若有说明:int a[3][4]; 则对a数组元素的正确引用是__。 <br />        A)  a[2][4]     B)  a[1,3]   C)  a[1+1][0]    D)  a(2)(1); <br />*186 <br />D <br />若有说明:int a[3][4];则对a数组元素的非法引用是__。 <br />   <br />   <br />A)  a[0][2*1]  B)  a[1][3]  C)a[4-2][0]    D)a[0][4]"       " <br />*187 <br />B <br />以下能对二维数组a进行正确初始化的语句是__。 <br />   <br />        A)  int a[2][]={{1,0,1},{5,2,3}}; <br />        B)  int a[][3」={{1,2,3},{4,5,6}}; <br />        C)   int  a  [2][4]={{1,2,3},{4,5},{6}}; <br />        D)  int  a[][3={{1,0,1},{},{1,1}}; <br />*188 <br />C <br />以下不能对二维数组a进行正确初始化的语句是__。 <br />   <br /> A)  int  a[2][3]={0}; <br /> B)  int  a[][3」={{1,2,3},{4,5,6}}; <br /> C)  int  a[2][4]={{1,2,3},{4,5}{6}}; <br /> D)   int  a[][3]={{1,0,1},{0},{1,1}};  <br />*189 <br />D <br />若有说明：  int  a[3]「4]={0};则下面正确的叙述是 <br /> A）只有元素a[0][0]可得到初值0  <br /> B）此说明语句不正确：。     <br /> C）数组a中各元素都可得到初值，但其值不一定为0。 <br /> D）数组a中每个元素均可得到初值0 <br /><br />*190 <br />D <br />若有说明：int  a[][4]={0,0};则下面不正确的叙述是__。 <br />A）数组a的每个元素都可得到初值0   <br /> B）二维数组a的第一维大小为1   <br /> C）因为二维数组0中第二维大小的值除以初值个数的商为1,故数组a行 <br /> 数为1 <br /> D）只有元素a[0]「0」和a[0]「1」可得初值0，其余元素均得不到初值0 <br />*191 <br />B <br />若有说明：int  a[3]「4]；则数组a各元素 <br /> A）可在程序的运行阶段得到初值0 <br /> B）可在程序的编译阶段得到初值0    <br /> C）不能得到确定的初值<br />D）可在程序的编译或运行阶段得初值0 <br />*192 <br />C <br />以下各组选项中，均能正确定义二维实型数组a的选项是 <br />   <br /> A）float a[3][4];                      B）float a(3,4);   <br /> float a[][4];                             float a[3][4]; <br /> float a[3][]={{1},{0}};                   float a[][]={{0},{0}}; <br /> C）float a[3][4];                      D）float a[3][4]; <br />static float a[][4]={{0},{0}};             float a[3][]; <br />auto float a[][4]={{0},{0},{0}};           float a[][4] <br />*193 <br />A  <br />下面程序(每行程序前面的数字表示行号) <br /> 1   main() <br /> 2     { <br /> 3     int  a[3]={3*0}; <br /> 4     int  i; <br /> 5     for(i=0;i&lt;3;i++)  scanf("%d",&amp;a[ i]); <br /> 6     for(i=1;i&lt;3;i++) a[0]=a[0]+a[ i] ;<br /> 7     printf("%d\n",a[0]); }<br />A)没有错误B)第3行有错误 <br />C)第5行有错误 D)第7行有错误  <br />*194 <br />C  <br />下面程序一一一(每行程序前面的数字表示行号)。 <br />1    main() <br />2    { <br />3     float a[10]={0.0}; <br />4     int  i <br />5     for(i=0;i&lt;3;i++)  scanf("%d",&amp;a[ i]); <br />6     for(i=0;i&lt;10;i++)  a[0]=a[0]+a[ i]; <br />7     printf("%d\n",a[0]); <br />8     } <br />A)没有错误         B)第3行有错误 <br />C)第5行有错误      D)第7行有错误   <br />*195 <br />D <br />下面程序有错的行是 <br /> 1  main() <br /> 2{ <br /> 3   int  a[3]={1}; <br /> 4   int  i; <br /> 5   scanf("%d",&amp;a); <br /> 6   for(i=1;i&lt;3;i++)  a[0]=a[0]+a[ i]; <br /> 7   printf("a[0]=%d\n",a[0]); <br /> 8   } <br /> A)3      B)6   C)7      D)5 <br />*196 <br />D <br />下面程序(每行程序前面的数字表示行号) <br /> 1  main() <br /> 2  { <br /> 3   int a[3]={0}; <br /> 4   int i; <br /> 5   for(i=0;i&lt;3;i++)scanf("%d",&amp;a[ i]); <br /> 6   for(i=1;i&lt;4;i++)a[0]=a[0]+a[ i]; <br /> 7   printf("%d\n",a[0]); <br /> 8                     } <br />  <br /> A)没有错误            B)第3行有错误 <br /> C)第5行有错误        D)第6行有错误 <br />*197 <br />D <br />若二维数组a有m列,则计算任一元素a[ i][j]在数组中位置的公式为 <br />(假设a[0][0]位于数组的第一个位置上。) <br /> A)i*m+j    B)j*p+i。C)i*m+j-1    D)i*m+j+1   <br />*198 <br />B <br />对以下说明语句的正确理解是 <br /> int a[10]={6,7,8,9,10}; <br /> A)将5个初值依次赋给a[1]至a[5] <br /> B)将5个初值依次赋给a[0]至a[4] <br /> C)将5个初值依次赋给a[6]至a[10] <br /> D)因为数组长度与初值的个数不相同,所以此语句不正确 <br />*199 <br />B <br />以下不正确的定义语句是__. <br /> A)  double  x[5]={2.0,4.0,6.0,8.0,10.0}; <br /> B)  int  y「5」={0,1,3,5,7,9}; <br /> C)  char  c1[]={’1’,’2’,’3’,’4’,’5’};    4       。二入广    \   "’   (: <br /> D)  char c2[]=}{'\x10','\xa','\x8'}; <br />*200 <br />B <br />若有说明:int  a[」「3」={1,2,3,4,5,6,7};则a数组第一维的大小是__. <br /> A)  2    B)  3  C)  4  D)无确定值 <br />*201 <br />B <br />若二维数组a有m列,则在a[ i][j]前的元素个数为__. <br /> A)j*m+j  B)i*m+j    C)i*m+j  D)i*m+j+1 <br />*202 <br />A <br />定义如下变量和数组:  <br /> int k;  <br /> int a[3][3]={1,2,3,4,5,6,7,8,9}; <br /> 则下面语句的输出结果是    。" <br /> for(k=0;k&lt;3;k++)  printf  ("%d",a[k][2-k]); <br /> A)  3  5  7B)3 6  9   C) 1 5 9 D)  1 4 7 <br />*203 <br />B <br />若有以下程序段: <br />...... <br /> int a[]={4,0,2,3,1};i,j,t; <br /> for(i=1;i&lt;5;i++) <br /> {t=a[ i];j=i-1; <br /> while(j&gt;=0&amp;&amp;t&gt;a[j]) <br />   {a[j+1]=a[j];j--;} <br />        ...... <br /> 则该程序段的功能是 __.   <br /> A)对数组a进行插入排序(升序) <br /> B)对数组a进行插入排序(降序) <br /> C)对数组a进行选择排序(升序) <br /> D)对数组a进行选择排序(降序) <br />*204 <br />D <br />以下正确的定义语句是__. <br /> A)  int  a[1」[4」={1,2,3,4,5}; <br /> B)  float x[3][]={{1},{2},{3}}; <br /> C)  long b[2][3]={{1},{1,2},{1,2,3}}; <br /> D)  double  y[][3]={0}; <br />*205 <br />C <br />下面程序的运行结果是__.<br />main()  <br /> {int  a[6」「6」,i,j; <br />   for(i=1;i&lt;6;i++) <br />   for(j=1;j&lt;6,j++) <br />     a[ i][j]=(i/j)*(j/i); <br />   for(i=1;i&lt;6;i++) <br />     {for(j=1;j&lt;6;j十十) <br />   printf("%2d",a[ i][j]); <br />       printf("\n"_);} <br />         } <br />   <br />A)11111     B)00001 C)10000     D)10001 <br />11111        00010    01000         01010 <br />11111        00100    00100         00100 <br />11111        01000    00010         01010 <br />11111        10000    00001         10001 <br />*206 <br />C <br />下面程序的运行结果是 __. <br />     main() <br />     {int  a[6],i; <br />         for(i=1;i&lt;6;i十十) <br />     {a[ i]=9*(i-2+4*(i&gt;3))%5; <br />      printf("%2d",a[ i]); <br />}         <br />       } <br />   <br />     A)—40404B)—40403 <br />     C)一40443D)一40440 <br />*207 <br />D <br />下面是对s的初始化,其中不正确的是__. <br /> A)  char  s[5」={"abc"} B)char s[5]={'a','b','c'}; <br /> C)  char  s[5]=""       D) char s[5]="abcdef"; <br />*208 <br />B <br />下面程序段的运行结果是 __. <br /> char  c[5]={'a','b','\0','c','\0'}; <br />   printf("%s",c);} <br />A)’a’’b’ B)  ab   C)  ab c   D)  ab     <br />   (其中 表示空格)     <br />*209 <br />D <br />对两个数组a和6进行如下初始化         <br />   char  a[]="ABCDEF"; <br />   char  b[]={’A’,’B’,’C’,’D’,’E’,’F’};卜 <br />   则以下叙述正确的是 __.    <br />   A)  a与b数组完全相同       B)  a与b长度相同 <br />   C)  a和b中都存放字符串     D)  a数组比b数组长度长 <br />*210 <br />B <br />有两个字符数组a、b,则以下正确的输入格式是  __. <br />   A)  gets  (a,b);   B)  scanf  ("%s%s",a,b); <br />   C)  scanf  ("%s%s",&amp;a,&amp;b);  D)  gets  ("a"),  gets  ("b"); <br />*211 <br />D <br />有字符数组a[80]和b[80],则正确的输出形式是__. <br />A)  puts  (a,b);  B)  printf  ("%s,%s,a[],b[]); <br />C)  putchar(a,b);  D)  puts  (a),  puts  (b); <br />*212 <br />D <br />下面程序段的运行结果是__. <br /> char  a[7]="abcdef"; <br /> char  b[4]="ABC"; <br /> strcpy(a,b); <br /> printf  ("%c",a[5]); <br />         J。        "      了 <br />A)一   B)\O  C) e D)f(其中一表示空格) <br />*213 <br />D <br />有下面的程序段 <br />char  a[3],b[]="china"; <br />a=b; <br />printf("%s",a); <br />则__. <br />A)运行后•将输出Chm、"、B)运行后将输出Ch’一 <br />C)运行后将输出Chi      D)编译出错 <br />*214 <br />B <br />下面程序段的运行结果是__. <br />char  c[]="\t\v\\\0will\n"; <br />printf("%d",strlen(c));        <br />A)14     B)  3  C)  9  D)字符串中有非法字符,输出值不确定 <br />*215 <br />D <br />判断字符串a和b是否相等,应当使用__. <br />A)  if  (a==b)   B)  if  (a=b) <br />C)  if  (strcpy(a,b)),  D)  if  (strcmp(a,b)) <br />*216 <br />D <br />判断字符串s1是否大于字符串s2应当使用__. <br />A)  if  (sl&gt;s2)   B)  if  (strcmp(s1,s2)) <br />C)  if  (strcmp(s2,sl)&gt;0)  D)  if  (strcmp(s1,s2)&gt;0) <br />*217 <br />A <br />下面程序段是输出两个字符串中对应字符相等的字符。请选择填空。 <br /></p><pre><span lang="EN-US"><font face="黑体" size="1">char<span style="mso-spacerun: yes">  </span>x[]="programming";</font></span></pre><pre><span lang="EN-US"><font face="黑体" size="1">char<span style="mso-spacerun: yes">  </span>y[]="Fortran";</font></span></pre><pre><span lang="EN-US"><font face="黑体" size="1">int<span style="mso-spacerun: yes">  </span>i=0;</font></span></pre><pre><span lang="EN-US"><font face="黑体" size="1">while<span style="mso-spacerun: yes">  </span>(x[ i]!='\0'&amp;&amp; y[ i]!='\0')</font></span></pre><pre><span lang="EN-US"><font face="黑体"><font size="1"> {if<span style="mso-spacerun: yes">  </span>(x[i ]==y[ i]) printf<span style="mso-spacerun: yes">  </span>("%c", <u>1 </u><span style="mso-spacerun: yes"> </span>);<span style="mso-spacerun: yes">  </span></font></font></span></pre><pre><span lang="EN-US"><font face="黑体"><font size="1"><span style="mso-spacerun: yes">     </span>else i++;}</font></font></span></pre><pre><span lang="EN-US"><font face="黑体" size="1">}</font></span></pre><p><br /> 【1】A)x[i++] B)y[++i]  C)x[ i]    D)y[ i] <br />*218 <br />D <br />下面描述正确的是__. <br /> A)两个字符串所包含的字符个数相同时,才能比较字符串 <br /> B)字符个数多的字符串比字符个数少的字符串大 <br /> C)字符串"STOP "与"STOp"相等 <br /> D)字符串"hat"小于字符串"he"<br /></p><p>*219 <br />C <br />下述对C语言字符数组的描述中错误的是 <br /> A)字符数组可以存放字符串 <br /> B)字符数组的字符串可以整体输入、输出 <br /> C)可以在赋值语句中通过赋值运算符"="对字符数组整体赋值 <br /> D)不可以用关系运算符对字符数组中的字符串进行比较 <br />*220 <br />B <br />有已排好序的字符串a,下面的程序是将字符串s中的每个字符按a中元素<br />的规律插入到a中。请选择填空。 <br /> #indude<stdio.h /><br />  main() <br /> {char  a[20」="cehiknqtw"; <br />   char  s[]="fbla"; <br />   int i,k,j; <br />   for(k=0;s[k]!='\0';k十+) <br />    {j=0;    <br />        <br />   while(s[k]&gt;=a[j]&amp;&amp;a[j]!='\0')j++; <br />       for(i=str1en(a);i&gt;=j;i--) 【2】; <br />      a[j」=S[k」; <br />     } <br /> puts(a); <br />   } <br />  【2】  A)  a[ i]=a[i+1]  B)  a[i+1]=a[ i]; <br />        C)   a[ i]=a[i-1] D) a[i-1]=a[ i]; <br />*221 <br />A <br />下面程序的功能是将字符串5中所有的字符c删除。请选择填空。 <br /> #include  <stdio.h /><br /> main() <br />     {char  s[80]; <br />int i,j; <br />      gets(s); <br />       for(i=j=0;s[ i]!='\0';i++) <br />         if(s[ i]!='c')【1】 <br />     <br />     puts(s); <br />  <br /> 【1】A)s[j++]=s[ i]  B)s[++j]=s[ i]; <br />   C)  s[j]=s[ i];j++;   D)  s[j]=s[ i]; <br />*222 <br />B <br />下面程序的功能是从键盘输入一行字符,统计其中有多少个单词,单词之间 <br /> 用空格分隔。请选择填空。        <br />   #indude<stdio.h /><br />   main() <br />       {char  s[80」,c1,c2=''; <br />         int  i=0,num=0; <br />         gets(s); <br />         while(s[ i]!='\0') <br />         {c1=s[ i]; <br />1f(i==0)  c2=''; <br />else  c2=s[i-1]; <br />if(【1】)  num++; <br />i++; <br /> ) <br />     printf("There are %d words.\n",num); <br /> } <br />   <br />         【1】A)c1='' &amp;&amp; c2=='' B)cl!='' &amp;&amp; c2=='' <br />   C)c1=='' &amp;&amp; c2!='' D)cl!='' &amp;&amp; c2!='' <br />*223 <br />A <br />下面程序的运行结果是 <br />   #indude<stdio.h /><br />main() <br />  {char  ch[7]={"12ab56"}; <br />         int  i,s=0; <br />   for(i=0;ch[ i]&gt;='0'&amp;&amp;ch[ i]&lt;='9';i+=2) <br />     s=10*s+ch[ i]-'0'; <br />        printf("%d\n",s); <br />       } <br />   A)1       B)1256        C)  12ab56      D)1 <br />                                                              2 <br />                                                              5 <br />                                                              6 <br />*224 <br />A <br />当运行以下程序时,从键盘输入:aa  bb<cr /><br />cc dd<cr /><br />(<cr />表示回车),则下面程序的运行结果是 <br /> #  include<stdio.h /><br /> main()     <br /> {char  a1[5],a2[5」,a3[5],a4[5]; <br /> scanf("%s%s",a1,a2);  <br /> gets(a3);  gets(a4); <br /> puts(al);  puts(a2); <br /> puts(a3);  puts(a4); <br />       } <br /></p><pre><span lang="EN-US"><font size="2"><font face="黑体"><span style="mso-spacerun: yes"> </span><font size="1">A)<span style="mso-spacerun: yes">  </span>aa<span style="mso-spacerun: yes">  </span><span style="mso-spacerun: yes">   </span>B)<span style="mso-spacerun: yes">  </span>aa ()aa<span style="mso-spacerun: yes">        </span>D)<span style="mso-spacerun: yes">  </span>aa bb</font></font></font></span></pre><pre style="TEXT-INDENT: 25pt; mso-char-indent-count: 2.5"><span lang="EN-US"><font face="黑体" size="1">bb<span style="mso-spacerun: yes">      </span><span style="mso-spacerun: yes">   </span>bb<span style="mso-spacerun: yes">    </span>bb<span style="mso-spacerun: yes">           </span>cc</font></span></pre><pre style="TEXT-INDENT: 80pt; mso-char-indent-count: 8.0"><span lang="EN-US"><font face="黑体" size="1">cc<span style="mso-spacerun: yes">    </span>cc<span style="mso-spacerun: yes">  </span>dd<span style="mso-spacerun: yes">       </span>dd</font></span></pre><pre><span lang="EN-US"><font face="黑体"><font size="1"><span style="mso-spacerun: yes">   </span><span style="mso-spacerun: yes">  </span>cc dd<span style="mso-spacerun: yes">    </span><span style="mso-spacerun: yes">  </span>dd ee</font></font></span></pre><p><br />*225 <br />D <br />   <br />当运行以下程序时,从键盘输入:ab<cr /><br />    c<cr /><br />    dd<cr /><br /> (<cr />表示回车),则下面程序的运行结果是 <br /> #include<stdio.h /><br />   #difine N 6 <br />  main() <br />   {  char  c[N];        <br />       int  i=0; <br />       for  (;i<n;c[><br i]="getchar(),i++);      " />   for(i=0;  i<n;&nbsp;&nbsp;i++)&nbsp;&nbsp;putchar(c[><br i]);="" />、 <br /> A)abcdef   B)a    C)b      D)ab <br /> b       c         c <br /> c       d         d <br /> e       <br />     <br /> f       <br />*226 <br />A <br />当运行以下程序时,从键盘输入:AhaMA  Aha<cr />(<cr)表示回车),><br /> 则下面程序的运行结果是 <br />   #include  "stdio.h" <br />   main() <br /> {char  s[80],c='a'; <br />   int i=0; <br />   scanf("%s",s); <br />   while(s[ i]!='\0') <br />     {if(s[ i]==c)  s[ i]=s[ i]-32; <br />   else  if(s[ i]==c-32)  s[ i]=s[ i]+32; <br />     i++; <br />     } <br />   puts(s); <br />) <br />A)ahAMa      B)AhAMa      C)  AhAMa  ahA  D)  ahAMa ahA <br />*227 <br />D <br />下面程序的运行结果是一一一。 <br /> #include<stdio.h /><br /> #inc1ude<string.h /><br /> main() <br /> {char  a[80」="AB",b[80]="LMNP"; <br />       int  i=0; <br />       strcat(a,b);  <br />   whi1e(a[i++]!='\0')b[ i]=a[ i]; <br />   puts(b); <br /> } <br /> A)LB   B)ABLMNP     C)AB     D)LBLMNP <br />*228 <br />B <br />下面程序的运行结果是 <br />  #include <stdio.h /><br />   main() <br />{ <br />char str[]="SSSWLIA",c; <br />int k; <br />for(k=2;(c=str[k])!='\0';k++) <br />{switch(c)<br />{case 'I':++k;break; <br />case 'L':continue; <br />default:putchar(c);continue; <br />} <br />putchar('*'); <br />} <br />} <br />A)SSW*    B)SW*   C)  SW*A  D)SW <br />*229 <br />B <br />下面程序的运行结果是 <br /> #include <stdio.h /><br />main() <br />{char a[]="morning",t; <br />int i,j=0; <br />for(i=1;i&lt;7;i++) if(a[j]<a[><br i])j="i;" />t=a[j];a[j]=a[7]; <br />a[7]=a[j];puts(a); <br />} <br />A)  mogninr  B)  mo  C)  morning  D)  mornin </a[></cr)表示回车),></n;&nbsp;&nbsp;i++)&nbsp;&nbsp;putchar(c[></n;c[></p></a&nbsp;&nbsp;max></m);&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;></c)&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;t></b></z;></y;></x;></a);></y)&nbsp;></y)></d)x></c)></d)x></b)></y)&nbsp;{x++;y++;}></y)&&!z||1)></double></double></double></1ong&nbsp;&nbsp;int></int&nbsp;&nbsp;><img src ="http://www.cnitblog.com/zouzheng/aggbug/21758.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-11 10:07 <a href="http://www.cnitblog.com/zouzheng/articles/21758.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>delete和delete[] 的区别 </title><link>http://www.cnitblog.com/zouzheng/articles/21740.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 14:29:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21740.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21740.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21740.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21740.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21740.html</trackback:ping><description><![CDATA[
		<p>#include &lt;iostream&gt;; <br />#include "xercesc/dom/DOM.hpp" <br />int main(){ <br /><br />        char* pc = 0; <br />        char* pc2; <br />        int i = 21; <br />        pc = new char; <br />        std::cout&lt;&lt;(long)pc&lt;&lt;std::endl; <br />        delete pc; <br />        std::cout&lt;&lt;(long)pc&lt;&lt;std::endl; <br /><br />        pc2 = new char; <br />        std::cout&lt;&lt;(long)pc2&lt;&lt;std::endl; <br /><br />        return 0; <br />} <br /><br />输出： <br />[root@ts xml]# ./a.out <br />134519536 <br />134519536 <br />134519536 <br /><br />地址没有变化，用delete[], delete都一样</p>
		<p> </p>
		<p>C++告诉我们在回收用 new 分配的单个对象的内存空间的时候用 delete，回收用 new[] 分配的一组对象的内存空间的时候用 delete[]。 <br /><br />楼主的这个问题提得很好。很多人注意到了这个问题，但是却不清楚为什么要这样做，不这样做行不行。 <br /><br />关于 new[] 和 delete[]，其中又分为两种情况：(1) 为基本数据类型分配和回收空间；(2) 为自定义类型分配和回收空间。 <br /><br />对于 (1)，上面提供的程序已经证明了 delete[] 和 delete 是等同的。但是对于 (2)，情况就发生了变化。请看下面的程序。 <br /></p>
		<pre>#include &lt;iostream&gt;;<br />using namespace std;<br /><br />class T {<br />public:<br />  T() { cout &lt;&lt; "constructor" &lt;&lt; endl; }<br />  ~T() { cout &lt;&lt; "destructor" &lt;&lt; endl; }<br />};<br /><br />int main()<br />{<br />  const int NUM = 3;<br /><br />  T* p1 = new T[NUM];<br />  cout &lt;&lt; hex &lt;&lt; p1 &lt;&lt; endl;<br />  //  delete[] p1;<br />  delete p1;<br /><br />  T* p2 = new T[NUM];<br />  cout &lt;&lt; p2 &lt;&lt; endl;<br />  delete[] p2;<br />}<br /></pre>
		<p>
				<br />
				<br />大家可以自己运行这个程序，看一看 delete p1 和 delete[] p1 的不同结果，我就不在这里贴运行结果了。 <br /><br />从运行结果中我们可以看出，delete p1 在回收空间的过程中，只有 p1[0] 这个对象调用了析构函数，其它对象如 p1[1]、p1[2] 等都没有调用自身的析构函数，这就是问题的症结所在。如果用 delete[]，则在回收空间之前所有对象都会首先调用自己的析构函数。 <br /><br />基本类型的对象没有析构函数，所以回收基本类型组成的数组空间用 delete 和 delete[] 都是应该可以的；但是对于类对象数组，只能用 delete[]。对于 new 的单个对象，只能用 delete 不能用 delete[] 回收空间。 <br /><br />所以一个简单的使用原则就是：new 和 delete、new[] 和 delete[] 对应使用。</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21740.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 22:29 <a href="http://www.cnitblog.com/zouzheng/articles/21740.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>链表颠倒</title><link>http://www.cnitblog.com/zouzheng/articles/21735.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 13:58:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21735.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21735.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21735.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21735.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21735.html</trackback:ping><description><![CDATA[
		<font size="2">struct intNode <br />{ <br />  int data; <br />   struct intNode * next; <br />} <br />以上结果构造了链表T ,调用如下revers函数后将链表颠倒，完成该函数功能 <br />[code] <br /><br />//不知道是不是这个意思：） 欢迎大家指正 <br />struct intNode * revers( struct intNode *t) <br />{ <br />  struct intNode *temp,*head; <br />  if(t==null){ <br />   return t; <br />  } <br />  head=t; <br />  t=t-&gt;next; <br />  head-&gt;next=null; <br />  while(t!=null){ <br />      temp=t; <br />      t=t-&gt;next; <br />      temp-&gt;next=head; <br />      head=temp; <br />  } <br />  return head; <br />}</font>
		<span class="postbody">
				<br />
				<br />
				<br />#include "../my.h" <br /><br />typedef struct intNode <br />{ <br />       int data; <br />       struct intNode * next; <br />} intNode; <br /><br />struct intNode *revers(struct intNode *t) <br />{ <br />       struct intNode *next_bak = NULL, *current = t, *nn_bak = NULL; <br /><br />       if (t) next_bak = t-&gt;next; <br />       if (next_bak) nn_bak = next_bak-&gt;next; <br />       while(current) { <br />               if (next_bak) next_bak-&gt;next = current;//do it <br /><br />               current = next_bak; <br />               next_bak = nn_bak; <br />               if (nn_bak) <br />                       nn_bak = next_bak-&gt;next; <br />               else <br />                       nn_bak = NULL; <br />               //printf("%p\n%p\n%p\n\n",current,next_bak,nn_bak); <br />       } <br />       if (t) t-&gt;next = NULL; <br />} <br /><br />int main() <br />{ <br />       intNode k1,k2,k3,k4,k5, *p; <br />       bzero(&amp;k1,sizeof(intNode)); <br />       bzero(&amp;k2,sizeof(intNode)); <br />       bzero(&amp;k3,sizeof(intNode)); <br />       bzero(&amp;k4,sizeof(intNode)); <br />       bzero(&amp;k5,sizeof(intNode)); <br />       k1.data = 11; <br />       k1.next = &amp;k2; <br />       k2.data = 22; <br />       k2.next = &amp;k3; <br />       k3.data = 33; <br />       k3.next = &amp;k4; <br />       k4.data = 44; <br />       k4.next = &amp;k5; <br />       k5.data = 55; <br />       k5.next = NULL; <br /><br />       p = &amp;k1; <br />       while(p) { <br />               printf("[%p][%d][--&gt;%p]\n", p, p-&gt;data, p-&gt;next); <br />               p = p-&gt;next; <br />       } <br />       revers(&amp;k1); <br />       printf("After revers\n"); <br />       p=&amp;k5; <br />       while(p) { <br />               printf("[%p][%d][--&gt;%p]\n", p,p-&gt;data, p-&gt;next); <br />               p = p-&gt;next; <br />       } <br />} <br /></span>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21735.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 21:58 <a href="http://www.cnitblog.com/zouzheng/articles/21735.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针函数和函数指针有什么区别</title><link>http://www.cnitblog.com/zouzheng/articles/21736.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 13:58:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21736.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21736.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21736.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21736.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21736.html</trackback:ping><description><![CDATA[1,这两个概念都是简称，指针函数是指带指针的函数，即本质是一个函数。我们知道函数都又返回类型（如果不返回值，则为无值型），只不过指针函数返回类型是某一类型的指针。其定义格式如下所示： <br /><br />返回类型标识符 *返回名称（形式参数表） <br />{ 函数体 } <br /><br />返回类型可以是任何基本类型和复合类型。返回指针的函数的用途十分广泛。事实上，每一个函数，即使它不带有返回某种类型的指针，它本身都有一个入口地址，该地址相当于一个指针。比如函数返回一个整型值，实际上也相当于返回一个指针变量的值，不过这时的变量是函数本身而已，而整个函数相当于一个“变量”。例如下面一个返回指针函数的例子： <br /><br />#include <br /><br />float *find(); <br />main() <br />{ <br />    static float score[][4]={{60,70,80,90},{56,89,34,45},{34,23,56,45}}; <br />    float *p; <br />    int i,m; <br />    printf("Enter the number to be found:"); <br />    scanf("%d",&amp;m); <br />    printf("the score of NO.%d are:\n",m); <br />    p=find(score,m); <br />    for(i=0;i&lt;4;i++) <br />        printf("%5.2f\t",*(p+i)); <br />} <br /><br />float *find(float(*pionter)[4],int n)/*定义指针函数*/ <br />{ <br />    float *pt; <br />    pt=*(pionter+n); <br />    return(pt); <br />} <br /><br />学生学号从0号算起，函数find()被定义为指针函数，起形参pointer是指针指向包含4个元素的一维数组的指针变量。pointer+1指向 score的第一行。*(pointer+1)指向第一行的第0个元素。pt是一个指针变量，它指向浮点型变量。main()函数中调用find()函数，将score数组的首地址传给pointer. <br /><br />2,“函数指针”是指向函数的指针变量，因而“函数指针”本身首先应是指针变量，只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样，这里是指向函数。如前所述，C在编译时，每一个函数都有一个入口地址，该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后，可用该指针变量调用函数，就如同用指针变量可引用其他类型变量一样，在这些概念上一致的。函数指针有两个用途：调用函数和做函数的参数。函数指针的说明方法为: <br />数据类型标志符 （*指针变量名）（参数）；注：函数括号中的参数可有可无，视情况而定。 <br />下面的程序说明了函数指针调用函数的方法： <br /><br />#include <br /><br />int max(int x,int y){ return(x&gt;y?x:y); } <br /><br />void main() <br />{ <br />    int (*ptr)(); <br />    int a,b,c; <br />    ptr=max; <br />    scanf("%d,%d",&amp;a,&amp;b); <br />    c=(*ptr)(a,b); <br />    printf("a=%d,b=%d,max=%d",a,b,c); <br />} <br /><br />ptr是指向函数的指针变量，所以可把函数max()赋给ptr作为ptr的值，即把max()的入口地址赋给ptr,以后就可以用ptr来调用该函数，实际上ptr和max都指向同一个入口地址，不同就是ptr是一个指针变量，不像函数名称那样是死的，它可以指向任何函数，就看你像怎么做了。在程序中把哪个函数的地址赋给它，它就指向哪个函数。而后用指针变量调用它，因此可以先后指向不同的函数，不过注意，指向函数的指针变量没有++和--运算，用时要小心。<img src ="http://www.cnitblog.com/zouzheng/aggbug/21736.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 21:58 <a href="http://www.cnitblog.com/zouzheng/articles/21736.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c位域</title><link>http://www.cnitblog.com/zouzheng/articles/21729.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 08:16:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21729.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21729.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21729.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21729.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21729.html</trackback:ping><description><![CDATA[
		<span class="oblog_text">
				<font color="#ff0000">位域<br /><br /></font>有些信息在存储时，并不需要占用一个完整的字节， 而只需占几个或一个二进制位。例如在存放一个开关量时，只有0和1 两种状态， 用一位二进位即可。为了节省存储空间，并使处理简便，Ｃ语言又提供了一种数据结构，称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域， 并说明每个区域的位数。每个域有一个域名，允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿，其形式为： <br />struct 位域结构名 <br />{ 位域列表 };<br />其中位域列表的形式为： 类型说明符 位域名：位域长度 <br />例如： <br /><font color="#009900">struct bs<br />{<br />int a:8;<br />int b:2;<br />int c:6;<br />};<br /></font>位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明，同时定义说明或者直接说明这三种方式。例如： <br /><font color="#009900">struct bs<br />{<br />int a:8;<br />int b:2;<br />int c:6;<br />}data;</font><br />说明data为bs变量，共占两个字节。其中位域a占8位，位域b占2位，位域c占6位。对于位域的定义尚有以下几点说明：<br /><br />1. 一个位域必须存储在同一个字节中，不能跨两个字节。如一个字节所剩空间不够存放另一位域时，应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如： <br /><font color="#009900">struct bs<br />{<br />unsigned a:4<br />unsigned :0 /*空域*/<br />unsigned b:4 /*从下一单元开始存放*/<br />unsigned c:4<br />}</font><br />在这个位域定义中，a占第一字节的4位，后4位填0表示不使用，b从第二字节开始，占用4位，c占用4位。<br /><br />2. 由于位域不允许跨两个字节，因此位域的长度不能大于一个字节的长度，也就是说不能超过8位二进位。<br /><br />3. 位域可以无位域名，这时它只用来作填充或调整位置。无名的位域是不能使用的。例如： <br /><font color="#009900">struct k<br />{<br />int a:1<br />int :2 /*该2位不能使用*/<br />int b:3<br />int c:2<br />};</font><br />从以上分析可以看出，位域在本质上就是一种结构类型， 不过其成员是按二进位分配的。<br /><br /><font color="#ff0000">二、位域的使用位域的使用</font>和结构成员的使用相同，其一般形式为： 位域变量名·位域名 位域允许用各种格式输出。<br /><font color="#009900">main(){<br />struct bs<br />{<br />unsigned a:1;<br />unsigned b:3;<br />unsigned c:4;<br />} bit,*pbit;<br />bit.a=1;<br />bit.b=7;<br />bit.c=15;<br />printf("%d,%d,%d\n",bit.a,bit.b,bit.c);<br />pbit=&amp;bit;<br />pbit-&gt;a=0;<br />pbit-&gt;b&amp;=3;<br />pbit-&gt;c|=1;<br />printf("%d,%d,%d\n",pbit-&gt;a,pbit-&gt;b,pbit-&gt;c);<br />}</font><br />上例程序中定义了位域结构bs，三个位域为a,b,c。说明了bs类型的变量bit和指向bs类型的指针变量pbit。这表示位域也是可以使用指针的。<br />程序的9、10、11三行分别给三个位域赋值。( 应注意赋值不能超过该位域的允许范围)程序第12行以整型量格式输出三个域的内容。第13行把位域变量bit的地址送给指针变量pbit。第14行用指针方式给位域a重新赋值，赋为0。第15行使用了复合的位运算符"&amp;="， 该行相当于： pbit-&gt;b=pbit-&gt;b&amp;3位域b中原有值为7，与3作按位与运算的结果为3(111&amp;011=011,十进制值为3)。同样，程序第16行中使用了复合位运算"|="， 相当于： pbit-&gt;c=pbit-&gt;c|1其结果为15。程序第17行用指针方式输出了这三个域的值。</span>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21729.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 16:16 <a href="http://www.cnitblog.com/zouzheng/articles/21729.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sizeof</title><link>http://www.cnitblog.com/zouzheng/articles/21712.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 04:07:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21712.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21712.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21712.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21712.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21712.html</trackback:ping><description><![CDATA[
		<div class="diaryContent" id="diary2239207" style="DISPLAY: block">
				<p>关键字：sizeof，字节对齐，多继承，虚拟继承，成员函数指针</p>
				<p>前向声明：</p>
				<p>sizeof，一个其貌不扬的家伙，引无数菜鸟竟折腰，小虾我当初也没少犯迷糊，秉着“<br />辛苦我一个，幸福千万人”的伟大思想，我决定将其尽可能详细的总结一下。<br />但当我总结的时候才发现，这个问题既可以简单，又可以复杂，所以本文有的地方并不<br />适合初学者，甚至都没有必要大作文章。但如果你想“知其然，更知其所以然”的话，<br />那么这篇文章对你或许有所帮助。<br />菜鸟我对C++的掌握尚未深入，其中不乏错误，欢迎各位指正啊</p>
				<p>1. 定义：<br />sizeof是何方神圣sizeof乃C/C++中的一个操作符（operator）是也，简单的说其作<br />用就是返回一个对象或者类型所占的内存字节数。<br />MSDN上的解释为：<br />The sizeof keyword gives the amount of storage, in bytes, associated with a<br />variable or a type (including aggregate types). <br />This keyword returns a value of type size_t.<br />其返回值类型为size_t，在头文件stddef.h中定义。这是一个依赖于编译系统的值，一<br />般定义为<br />typedef unsigned int size_t;<br />世上编译器林林总总，但作为一个规范，它们都会保证char、signed char和unsigned<br />char的sizeof值为1，毕竟char是我们编程能用的最小数据类型。<br />2. 语法：<br />sizeof有三种语法形式，如下：<br />1) sizeof( object ); // sizeof( 对象 );<br />2) sizeof( type_name ); // sizeof( 类型 );<br />3) sizeof object; // sizeof 对象;<br />所以，<br />int i;<br />sizeof( i ); // ok<br />sizeof i; // ok<br />sizeof( int ); // ok<br />sizeof int; // error<br />既然写法3可以用写法1代替，为求形式统一以及减少我们大脑的负担，第3种写法，忘<br />掉它吧！<br />实际上，sizeof计算对象的大小也是转换成对对象类型的计算，也就是说，同种类型的<br />不同对象其sizeof值都是一致的。这里，对象可以进一步延伸至表达式，即sizeof可以<br />对一个表达式求值，编译器根据表达式的最终结果类型来确定大小，一般不会对表达式<br />进行计算。如：<br />sizeof( 2 );// 2的类型为int，所以等价于 sizeof( int );<br />sizeof( 2 + 3.14 ); // 3.14的类型为double，2也会被提升成double类型，所以等价<br />于 sizeof( double );<br />sizeof也可以对一个函数调用求值，其结果是函数返回类型的大小，函数并不会被调用<br />，我们来看一个完整的例子：<br />char foo()<br />{<br />printf("foo() has been called.\n");<br />return 'a';<br />}<br />int main()<br />{<br />size_t sz = sizeof( foo() ); // foo() 的返回值类型为char，所以sz = sizeof(<br />char )，foo()并不会被调用<br />printf("sizeof( foo() ) = %d\n", sz);<br />}<br />C99标准规定，函数、不能确定类型的表达式以及位域（bit-field）成员不能被计算s<br />izeof值，即下面这些写法都是错误的：<br />sizeof( foo );// error<br />void foo2() { }<br />sizeof( foo2() );// error<br />struct S<br />{<br />unsigned int f1 : 1;<br />unsigned int f2 : 5;<br />unsigned int f3 : 12;<br />};<br />sizeof( S.f1 );// error<br />3. sizeof的常量性<br />sizeof的计算发生在编译时刻，所以它可以被当作常量表达式使用，如：<br />char ary[ sizeof( int ) * 10 ]; // ok<br />最新的C99标准规定sizeof也可以在运行时刻进行计算，如下面的程序在Dev-C++中可以<br />正确执行：<br />int n;<br />n = 10; // n动态赋值<br />char ary[n]; // C99也支持数组的动态定义<br />printf("%d\n", sizeof(ary)); // ok. 输出10<br />但在没有完全实现C99标准的编译器中就行不通了，上面的代码在VC6中就通不过编译。<br />所以我们最好还是认为sizeof是在编译期执行的，这样不会带来错误，让程序的可移植<br />性强些。<br />4. 基本数据类型的sizeof<br />这里的基本数据类型指short、int、long、float、double这样的简单内置数据类型，<br />由于它们都是和系统相关的，所以在不同的系统下取值可能不同，这务必引起我们的注<br />意，尽量不要在这方面给自己程序的移植造成麻烦。<br />一般的，在32位编译环境中，sizeof(int)的取值为4。<br />5. 指针变量的sizeof<br />学过数据结构的你应该知道指针是一个很重要的概念，它记录了另一个对象的地址。既<br />然是来存放地址的，那么它当然等于计算机内部地址总线的宽度。所以在32位计算机中<br />，一个指针变量的返回值必定是4（注意结果是以字节为单位），可以预计，在将来的6<br />4位系统中指针变量的sizeof结果为8。<br />char* pc = "abc";<br />int* pi;<br />string* ps;<br />char** ppc = &amp;pc;<br />void (*pf)();// 函数指针<br />sizeof( pc ); // 结果为4<br />sizeof( pi ); // 结果为4<br />sizeof( ps ); // 结果为4<br />sizeof( ppc ); // 结果为4<br />sizeof( pf );// 结果为4<br />指针变量的sizeof值与指针所指的对象没有任何关系，正是由于所有的指针变量所占内<br />存大小相等，所以MFC消息处理函数使用两个参数WPARAM、LPARAM就能传递各种复杂的消<br />息结构（使用指向结构体的指针）。<br />6. 数组的sizeof<br />数组的sizeof值等于数组所占用的内存字节数，如：<br />char a1[] = "abc";<br />int a2[3];<br />sizeof( a1 ); // 结果为4，字符 末尾还存在一个NULL终止符<br />sizeof( a2 ); // 结果为3*4=12（依赖于int）<br />一些朋友刚开始时把sizeof当作了求数组元素的个数，现在，你应该知道这是不对的，<br />那么应该怎么求数组元素的个数呢Easy，通常有下面两种写法：<br />int c1 = sizeof( a1 ) / sizeof( char ); // 总长度/单个元素的长度<br />int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 总长度/第一个元素的长度<br />写到这里，提一问，下面的c3，c4值应该是多少呢<br />void foo3(char a3[3])<br />{<br />int c3 = sizeof( a3 ); // c3 == <br />}<br />void foo4(char a4[])<br />{<br />int c4 = sizeof( a4 ); // c4 == <br />}<br />也许当你试图回答c4的值时已经意识到c3答错了，是的，c3!=3。这里函数参数a3已不<br />再是数组类型，而是蜕变成指针，相当于char* a3，为什么仔细想想就不难明白，我<br />们调用函数foo1时，程序会在栈上分配一个大小为3的数组吗不会！数组是“传址”的<br />，调用者只需将实参的地址传递过去，所以a3自然为指针类型（char*），c3的值也就为<br />4。<br />7. 结构体的sizeof<br />这是初学者问得最多的一个问题，所以这里有必要多费点笔墨。让我们先看一个结构体<br />：<br />struct S1<br />{<br />char c;<br />int i;<br />};<br />问sizeof(s1)等于多少聪明的你开始思考了，char占1个字节，int占4个字节，那么<br />加起来就应该是5。是这样吗你在你机器上试过了吗也许你是对的，但很可能你是错<br />的！VC6中按默认设置得到的结果为8。<br />Why为什么受伤的总是我<br />请不要沮丧，我们来好好琢磨一下sizeof的定义——sizeof的结果等于对象或者类型所<br />占的内存字节数，好吧，那就让我们来看看S1的内存分配情况：<br />S1 s1 = { 'a', 0xFFFFFFFF };<br />定义上面的变量后，加上断点，运行程序，观察s1所在的内存，你发现了什么<br />以我的VC6.0为例，s1的地址为0x0012FF78，其数据内容如下：<br />0012FF78: 61 CC CC CC FF FF FF FF<br />发现了什么怎么中间夹杂了3个字节的CC看看MSDN上的说明：<br />When applied to a structure type or variable, sizeof returns the actual siz<br />e, which may include padding bytes inserted for alignment.<br />原来如此，这就是传说中的字节对齐啊！一个重要的话题出现了。<br />为什么需要字节对齐计算机组成原理教导我们这样有助于加快计算机的取数速度，否<br />则就得多花指令周期了。为此，编译器默认会对结构体进行处理（实际上其它地方的数<br />据变量也是如此），让宽度为2的基本数据类型（short等）都位于能被2整除的地址上，<br />让宽度为4的基本数据类型（int等）都位于能被4整除的地址上，以此类推。这样，两个<br />数中间就可能需要加入填充字节，所以整个结构体的sizeof值就增长了。<br />让我们交换一下S1中char与int的位置：<br />struct S2<br />{<br />int i;<br />char c;<br />};<br />看看sizeof(S2)的结果为多少，怎么还是8再看看内存，原来成员c后面仍然有3个填<br />充字节，这又是为什么啊别着急，下面总结规律。</p>
				<p>字节对齐的细节和编译器实现相关，但一般而言，满足三个准则：<br />1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除；<br />2) 结构体每个成员相对于结构体首地址的偏移量（offset）都是成员大小的整数倍，<br />如有需要编译器会在成员之间加上填充字节（internal adding）；<br />3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍，如有需要编译器会在最<br />末一个成员之后加上填充字节（trailing padding）。<br />对于上面的准则，有几点需要说明：<br />1) 前面不是说结构体成员的地址是其大小的整数倍，怎么又说到偏移量了呢因为有<br />了第1点存在，所以我们就可以只考虑成员的偏移量，这样思考起来简单。想想为什么。</p>
				<p>结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得，这个宏也<br />在stddef.h中定义，如下：<br />#define offsetof(s,m) (size_t)&amp;(((s *)0)-&gt;m)<br />例如，想要获得S2中c的偏移量，方法为<br />size_t pos = offsetof(S2, c);// pos等于4<br />2) 基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型<br />，这里所说的“数据宽度”就是指其sizeof的大小。由于结构体的成员可以是复合类型<br />，比如另外一个结构体，所以在寻找最宽基本类型成员时，应当包括复合类型成员的子<br />成员，而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将<br />复合类型作为整体看待。<br />这里叙述起来有点拗口，思考起来也有点挠头，还是让我们看看例子吧（具体数值仍以<br />VC6为例，以后不再说明）：<br />struct S3<br />{<br />char c1;<br />S1 s;<br />char c2<br />};<br />S1的最宽简单成员的类型为int，S3在考虑最宽简单类型成员时是将S1“打散”看的，<br />所以S3的最宽简单类型为int，这样，通过S3定义的变量，其存储空间首地址需要被4整<br />除，整个sizeof(S3)的值也应该被4整除。<br />c1的偏移量为0，s的偏移量呢这时s是一个整体，它作为结构体变量也满足前面三个<br />准则，所以其大小为8，偏移量为4，c1与s之间便需要3个填充字节，而c2与s之间就不需<br />要了，所以c2的偏移量为12，算上c2的大小为13，13是不能被4整除的，这样末尾还得补<br />上3个填充字节。最后得到sizeof(S3)的值为16。<br />通过上面的叙述，我们可以得到一个公式：<br />结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目，即：</p>
				<p>sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( tr<br />ailing padding )</p>
				<p>到这里，朋友们应该对结构体的sizeof有了一个全新的认识，但不要高兴得太早，有<br />一个影响sizeof的重要参量还未被提及，那便是编译器的pack指令。它是用来调整结构<br />体对齐方式的，不同编译器名称和用法略有不同，VC6中通过#pragma pack实现，也可以<br />直接修改/Zp编译开关。#pragma pack的基本用法为：#pragma pack( n )，n为字节对齐<br />数，其取值为1、2、4、8、16，默认是8，如果这个值比结构体成员的sizeof值小，那么<br />该成员的偏移量应该以此值为准，即是说，结构体成员的偏移量应该取二者的最小值，<br />公式如下：<br />offsetof( item ) = min( n, sizeof( item ) )<br />再看示例：<br />#pragma pack(push) // 将当前pack设置压栈保存<br />#pragma pack(2)// 必须在结构体定义之前使用<br />struct S1<br />{<br />char c;<br />int i;<br />};<br />struct S3<br />{<br />char c1;<br />S1 s;<br />char c2<br />};<br />#pragma pack(pop) // 恢复先前的pack设置<br />计算sizeof(S1)时，min(2, sizeof(i))的值为2，所以i的偏移量为2，加上sizeof(i)<br />等于6，能够被2整除，所以整个S1的大小为6。<br />同样，对于sizeof(S3)，s的偏移量为2，c2的偏移量为8，加上sizeof(c2)等于9，不能<br />被2整除，添加一个填充字节，所以sizeof(S3)等于10。<br />现在，朋友们可以轻松的出一口气了，:)<br />还有一点要注意，“空结构体”（不含数据成员）的大小不为0，而是1。试想一个“不<br />占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是<br />，“空结构体”变量也得被存储，这样编译器也就只能为其分配一个字节的空间用于占<br />位了。如下：<br />struct S5 { };<br />sizeof( S5 ); // 结果为1</p>
				<p>8. 含位域结构体的sizeof<br />前面已经说过，位域成员不能单独被取sizeof值，我们这里要讨论的是含有位域的结构<br />体的sizeof，只是考虑到其特殊性而将其专门列了出来。<br />C99规定int、unsigned int和bool可以作为位域类型，但编译器几乎都对此作了扩展，<br />允许其它类型类型的存在。<br />使用位域的主要目的是压缩存储，其大致规则为：<br />1) 如果相邻位域字段的类型相同，且其位宽之和小于类型的sizeof大小，则后面的字<br />段将紧邻前一个字段存储，直到不能容纳为止；<br />2) 如果相邻位域字段的类型相同，但其位宽之和大于类型的sizeof大小，则后面的字<br />段将从新的存储单元开始，其偏移量为其类型大小的整数倍；<br />3) 如果相邻的位域字段的类型不同，则各编译器的具体实现有差异，VC6采取不压缩方<br />式，Dev-C++采取压缩方式；<br />4) 如果位域字段之间穿插着非位域字段，则不进行压缩；<br />5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。</p>
				<p>还是让我们来看看例子。<br />示例1：<br />struct BF1<br />{<br />char f1 : 3;<br />char f2 : 4;<br />char f3 : 5;<br />};<br />其内存布局为：<br />|_f1__|__f2__|_|____f3___|____|<br />|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|<br />0 3 7 8 1316<br />位域类型为char，第1个字节仅能容纳下f1和f2，所以f2被压缩到第1个字节中，而f3只<br />能从下一个字节开始。因此sizeof(BF1)的结果为2。<br />示例2：<br />struct BF2<br />{<br />char f1 : 3;<br />short f2 : 4;<br />char f3 : 5;<br />};<br />由于相邻位域类型不同，在VC6中其sizeof为6，在Dev-C++中为2。<br />示例3：<br />struct BF3<br />{<br />char f1 : 3;<br />char f2;<br />char f3 : 5;<br />};<br />非位域字段穿插在其中，不会产生压缩，在VC6和Dev-C++中得到的大小均为3。<br />9. 联合体的sizeof<br />结构体在内存组织上是顺序式的，联合体则是重叠式，各成员共享一段内存，所以整个<br />联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型，这<br />里，复合类型成员是被作为整体考虑的。<br />所以，下面例子中，U的sizeof值等于sizeof(s)。<br />union U<br />{<br />int i;<br />char c;<br />S1 s;<br />};</p>
		</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21712.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 12:07 <a href="http://www.cnitblog.com/zouzheng/articles/21712.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sizeof</title><link>http://www.cnitblog.com/zouzheng/articles/21713.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 04:07:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21713.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21713.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21713.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21713.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21713.html</trackback:ping><description><![CDATA[
		<div class="diaryContent" id="diary2239207" style="DISPLAY: block">
				<p>关键字：sizeof，字节对齐，多继承，虚拟继承，成员函数指针</p>
				<p>前向声明：</p>
				<p>sizeof，一个其貌不扬的家伙，引无数菜鸟竟折腰，小虾我当初也没少犯迷糊，秉着“<br />辛苦我一个，幸福千万人”的伟大思想，我决定将其尽可能详细的总结一下。<br />但当我总结的时候才发现，这个问题既可以简单，又可以复杂，所以本文有的地方并不<br />适合初学者，甚至都没有必要大作文章。但如果你想“知其然，更知其所以然”的话，<br />那么这篇文章对你或许有所帮助。<br />菜鸟我对C++的掌握尚未深入，其中不乏错误，欢迎各位指正啊</p>
				<p>1. 定义：<br />sizeof是何方神圣sizeof乃C/C++中的一个操作符（operator）是也，简单的说其作<br />用就是返回一个对象或者类型所占的内存字节数。<br />MSDN上的解释为：<br />The sizeof keyword gives the amount of storage, in bytes, associated with a<br />variable or a type (including aggregate types). <br />This keyword returns a value of type size_t.<br />其返回值类型为size_t，在头文件stddef.h中定义。这是一个依赖于编译系统的值，一<br />般定义为<br />typedef unsigned int size_t;<br />世上编译器林林总总，但作为一个规范，它们都会保证char、signed char和unsigned<br />char的sizeof值为1，毕竟char是我们编程能用的最小数据类型。<br />2. 语法：<br />sizeof有三种语法形式，如下：<br />1) sizeof( object ); // sizeof( 对象 );<br />2) sizeof( type_name ); // sizeof( 类型 );<br />3) sizeof object; // sizeof 对象;<br />所以，<br />int i;<br />sizeof( i ); // ok<br />sizeof i; // ok<br />sizeof( int ); // ok<br />sizeof int; // error<br />既然写法3可以用写法1代替，为求形式统一以及减少我们大脑的负担，第3种写法，忘<br />掉它吧！<br />实际上，sizeof计算对象的大小也是转换成对对象类型的计算，也就是说，同种类型的<br />不同对象其sizeof值都是一致的。这里，对象可以进一步延伸至表达式，即sizeof可以<br />对一个表达式求值，编译器根据表达式的最终结果类型来确定大小，一般不会对表达式<br />进行计算。如：<br />sizeof( 2 );// 2的类型为int，所以等价于 sizeof( int );<br />sizeof( 2 + 3.14 ); // 3.14的类型为double，2也会被提升成double类型，所以等价<br />于 sizeof( double );<br />sizeof也可以对一个函数调用求值，其结果是函数返回类型的大小，函数并不会被调用<br />，我们来看一个完整的例子：<br />char foo()<br />{<br />printf("foo() has been called.\n");<br />return 'a';<br />}<br />int main()<br />{<br />size_t sz = sizeof( foo() ); // foo() 的返回值类型为char，所以sz = sizeof(<br />char )，foo()并不会被调用<br />printf("sizeof( foo() ) = %d\n", sz);<br />}<br />C99标准规定，函数、不能确定类型的表达式以及位域（bit-field）成员不能被计算s<br />izeof值，即下面这些写法都是错误的：<br />sizeof( foo );// error<br />void foo2() { }<br />sizeof( foo2() );// error<br />struct S<br />{<br />unsigned int f1 : 1;<br />unsigned int f2 : 5;<br />unsigned int f3 : 12;<br />};<br />sizeof( S.f1 );// error<br />3. sizeof的常量性<br />sizeof的计算发生在编译时刻，所以它可以被当作常量表达式使用，如：<br />char ary[ sizeof( int ) * 10 ]; // ok<br />最新的C99标准规定sizeof也可以在运行时刻进行计算，如下面的程序在Dev-C++中可以<br />正确执行：<br />int n;<br />n = 10; // n动态赋值<br />char ary[n]; // C99也支持数组的动态定义<br />printf("%d\n", sizeof(ary)); // ok. 输出10<br />但在没有完全实现C99标准的编译器中就行不通了，上面的代码在VC6中就通不过编译。<br />所以我们最好还是认为sizeof是在编译期执行的，这样不会带来错误，让程序的可移植<br />性强些。<br />4. 基本数据类型的sizeof<br />这里的基本数据类型指short、int、long、float、double这样的简单内置数据类型，<br />由于它们都是和系统相关的，所以在不同的系统下取值可能不同，这务必引起我们的注<br />意，尽量不要在这方面给自己程序的移植造成麻烦。<br />一般的，在32位编译环境中，sizeof(int)的取值为4。<br />5. 指针变量的sizeof<br />学过数据结构的你应该知道指针是一个很重要的概念，它记录了另一个对象的地址。既<br />然是来存放地址的，那么它当然等于计算机内部地址总线的宽度。所以在32位计算机中<br />，一个指针变量的返回值必定是4（注意结果是以字节为单位），可以预计，在将来的6<br />4位系统中指针变量的sizeof结果为8。<br />char* pc = "abc";<br />int* pi;<br />string* ps;<br />char** ppc = &amp;pc;<br />void (*pf)();// 函数指针<br />sizeof( pc ); // 结果为4<br />sizeof( pi ); // 结果为4<br />sizeof( ps ); // 结果为4<br />sizeof( ppc ); // 结果为4<br />sizeof( pf );// 结果为4<br />指针变量的sizeof值与指针所指的对象没有任何关系，正是由于所有的指针变量所占内<br />存大小相等，所以MFC消息处理函数使用两个参数WPARAM、LPARAM就能传递各种复杂的消<br />息结构（使用指向结构体的指针）。<br />6. 数组的sizeof<br />数组的sizeof值等于数组所占用的内存字节数，如：<br />char a1[] = "abc";<br />int a2[3];<br />sizeof( a1 ); // 结果为4，字符 末尾还存在一个NULL终止符<br />sizeof( a2 ); // 结果为3*4=12（依赖于int）<br />一些朋友刚开始时把sizeof当作了求数组元素的个数，现在，你应该知道这是不对的，<br />那么应该怎么求数组元素的个数呢Easy，通常有下面两种写法：<br />int c1 = sizeof( a1 ) / sizeof( char ); // 总长度/单个元素的长度<br />int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 总长度/第一个元素的长度<br />写到这里，提一问，下面的c3，c4值应该是多少呢<br />void foo3(char a3[3])<br />{<br />int c3 = sizeof( a3 ); // c3 == <br />}<br />void foo4(char a4[])<br />{<br />int c4 = sizeof( a4 ); // c4 == <br />}<br />也许当你试图回答c4的值时已经意识到c3答错了，是的，c3!=3。这里函数参数a3已不<br />再是数组类型，而是蜕变成指针，相当于char* a3，为什么仔细想想就不难明白，我<br />们调用函数foo1时，程序会在栈上分配一个大小为3的数组吗不会！数组是“传址”的<br />，调用者只需将实参的地址传递过去，所以a3自然为指针类型（char*），c3的值也就为<br />4。<br />7. 结构体的sizeof<br />这是初学者问得最多的一个问题，所以这里有必要多费点笔墨。让我们先看一个结构体<br />：<br />struct S1<br />{<br />char c;<br />int i;<br />};<br />问sizeof(s1)等于多少聪明的你开始思考了，char占1个字节，int占4个字节，那么<br />加起来就应该是5。是这样吗你在你机器上试过了吗也许你是对的，但很可能你是错<br />的！VC6中按默认设置得到的结果为8。<br />Why为什么受伤的总是我<br />请不要沮丧，我们来好好琢磨一下sizeof的定义——sizeof的结果等于对象或者类型所<br />占的内存字节数，好吧，那就让我们来看看S1的内存分配情况：<br />S1 s1 = { 'a', 0xFFFFFFFF };<br />定义上面的变量后，加上断点，运行程序，观察s1所在的内存，你发现了什么<br />以我的VC6.0为例，s1的地址为0x0012FF78，其数据内容如下：<br />0012FF78: 61 CC CC CC FF FF FF FF<br />发现了什么怎么中间夹杂了3个字节的CC看看MSDN上的说明：<br />When applied to a structure type or variable, sizeof returns the actual siz<br />e, which may include padding bytes inserted for alignment.<br />原来如此，这就是传说中的字节对齐啊！一个重要的话题出现了。<br />为什么需要字节对齐计算机组成原理教导我们这样有助于加快计算机的取数速度，否<br />则就得多花指令周期了。为此，编译器默认会对结构体进行处理（实际上其它地方的数<br />据变量也是如此），让宽度为2的基本数据类型（short等）都位于能被2整除的地址上，<br />让宽度为4的基本数据类型（int等）都位于能被4整除的地址上，以此类推。这样，两个<br />数中间就可能需要加入填充字节，所以整个结构体的sizeof值就增长了。<br />让我们交换一下S1中char与int的位置：<br />struct S2<br />{<br />int i;<br />char c;<br />};<br />看看sizeof(S2)的结果为多少，怎么还是8再看看内存，原来成员c后面仍然有3个填<br />充字节，这又是为什么啊别着急，下面总结规律。</p>
				<p>字节对齐的细节和编译器实现相关，但一般而言，满足三个准则：<br />1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除；<br />2) 结构体每个成员相对于结构体首地址的偏移量（offset）都是成员大小的整数倍，<br />如有需要编译器会在成员之间加上填充字节（internal adding）；<br />3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍，如有需要编译器会在最<br />末一个成员之后加上填充字节（trailing padding）。<br />对于上面的准则，有几点需要说明：<br />1) 前面不是说结构体成员的地址是其大小的整数倍，怎么又说到偏移量了呢因为有<br />了第1点存在，所以我们就可以只考虑成员的偏移量，这样思考起来简单。想想为什么。</p>
				<p>结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得，这个宏也<br />在stddef.h中定义，如下：<br />#define offsetof(s,m) (size_t)&amp;(((s *)0)-&gt;m)<br />例如，想要获得S2中c的偏移量，方法为<br />size_t pos = offsetof(S2, c);// pos等于4<br />2) 基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型<br />，这里所说的“数据宽度”就是指其sizeof的大小。由于结构体的成员可以是复合类型<br />，比如另外一个结构体，所以在寻找最宽基本类型成员时，应当包括复合类型成员的子<br />成员，而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将<br />复合类型作为整体看待。<br />这里叙述起来有点拗口，思考起来也有点挠头，还是让我们看看例子吧（具体数值仍以<br />VC6为例，以后不再说明）：<br />struct S3<br />{<br />char c1;<br />S1 s;<br />char c2<br />};<br />S1的最宽简单成员的类型为int，S3在考虑最宽简单类型成员时是将S1“打散”看的，<br />所以S3的最宽简单类型为int，这样，通过S3定义的变量，其存储空间首地址需要被4整<br />除，整个sizeof(S3)的值也应该被4整除。<br />c1的偏移量为0，s的偏移量呢这时s是一个整体，它作为结构体变量也满足前面三个<br />准则，所以其大小为8，偏移量为4，c1与s之间便需要3个填充字节，而c2与s之间就不需<br />要了，所以c2的偏移量为12，算上c2的大小为13，13是不能被4整除的，这样末尾还得补<br />上3个填充字节。最后得到sizeof(S3)的值为16。<br />通过上面的叙述，我们可以得到一个公式：<br />结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目，即：</p>
				<p>sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( tr<br />ailing padding )</p>
				<p>到这里，朋友们应该对结构体的sizeof有了一个全新的认识，但不要高兴得太早，有<br />一个影响sizeof的重要参量还未被提及，那便是编译器的pack指令。它是用来调整结构<br />体对齐方式的，不同编译器名称和用法略有不同，VC6中通过#pragma pack实现，也可以<br />直接修改/Zp编译开关。#pragma pack的基本用法为：#pragma pack( n )，n为字节对齐<br />数，其取值为1、2、4、8、16，默认是8，如果这个值比结构体成员的sizeof值小，那么<br />该成员的偏移量应该以此值为准，即是说，结构体成员的偏移量应该取二者的最小值，<br />公式如下：<br />offsetof( item ) = min( n, sizeof( item ) )<br />再看示例：<br />#pragma pack(push) // 将当前pack设置压栈保存<br />#pragma pack(2)// 必须在结构体定义之前使用<br />struct S1<br />{<br />char c;<br />int i;<br />};<br />struct S3<br />{<br />char c1;<br />S1 s;<br />char c2<br />};<br />#pragma pack(pop) // 恢复先前的pack设置<br />计算sizeof(S1)时，min(2, sizeof(i))的值为2，所以i的偏移量为2，加上sizeof(i)<br />等于6，能够被2整除，所以整个S1的大小为6。<br />同样，对于sizeof(S3)，s的偏移量为2，c2的偏移量为8，加上sizeof(c2)等于9，不能<br />被2整除，添加一个填充字节，所以sizeof(S3)等于10。<br />现在，朋友们可以轻松的出一口气了，:)<br />还有一点要注意，“空结构体”（不含数据成员）的大小不为0，而是1。试想一个“不<br />占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是<br />，“空结构体”变量也得被存储，这样编译器也就只能为其分配一个字节的空间用于占<br />位了。如下：<br />struct S5 { };<br />sizeof( S5 ); // 结果为1</p>
				<p>8. 含位域结构体的sizeof<br />前面已经说过，位域成员不能单独被取sizeof值，我们这里要讨论的是含有位域的结构<br />体的sizeof，只是考虑到其特殊性而将其专门列了出来。<br />C99规定int、unsigned int和bool可以作为位域类型，但编译器几乎都对此作了扩展，<br />允许其它类型类型的存在。<br />使用位域的主要目的是压缩存储，其大致规则为：<br />1) 如果相邻位域字段的类型相同，且其位宽之和小于类型的sizeof大小，则后面的字<br />段将紧邻前一个字段存储，直到不能容纳为止；<br />2) 如果相邻位域字段的类型相同，但其位宽之和大于类型的sizeof大小，则后面的字<br />段将从新的存储单元开始，其偏移量为其类型大小的整数倍；<br />3) 如果相邻的位域字段的类型不同，则各编译器的具体实现有差异，VC6采取不压缩方<br />式，Dev-C++采取压缩方式；<br />4) 如果位域字段之间穿插着非位域字段，则不进行压缩；<br />5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。</p>
				<p>还是让我们来看看例子。<br />示例1：<br />struct BF1<br />{<br />char f1 : 3;<br />char f2 : 4;<br />char f3 : 5;<br />};<br />其内存布局为：<br />|_f1__|__f2__|_|____f3___|____|<br />|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|<br />0 3 7 8 1316<br />位域类型为char，第1个字节仅能容纳下f1和f2，所以f2被压缩到第1个字节中，而f3只<br />能从下一个字节开始。因此sizeof(BF1)的结果为2。<br />示例2：<br />struct BF2<br />{<br />char f1 : 3;<br />short f2 : 4;<br />char f3 : 5;<br />};<br />由于相邻位域类型不同，在VC6中其sizeof为6，在Dev-C++中为2。<br />示例3：<br />struct BF3<br />{<br />char f1 : 3;<br />char f2;<br />char f3 : 5;<br />};<br />非位域字段穿插在其中，不会产生压缩，在VC6和Dev-C++中得到的大小均为3。<br />9. 联合体的sizeof<br />结构体在内存组织上是顺序式的，联合体则是重叠式，各成员共享一段内存，所以整个<br />联合体的sizeof也就是每个成员sizeof的最大值。结构体的成员也可以是复合类型，这<br />里，复合类型成员是被作为整体考虑的。<br />所以，下面例子中，U的sizeof值等于sizeof(s)。<br />union U<br />{<br />int i;<br />char c;<br />S1 s;<br />};</p>
		</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21713.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 12:07 <a href="http://www.cnitblog.com/zouzheng/articles/21713.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Cc++基础知识，面试常见问题</title><link>http://www.cnitblog.com/zouzheng/articles/21709.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 02:30:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21709.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21709.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21709.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21709.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21709.html</trackback:ping><description><![CDATA[
		<div class="diaryBody">
				<strong>链表与数组的区别<br /></strong>A 从逻辑结构来看<br />A-1. 数组必须事先定义固定的长度（元素个数），不能适应数据动态地增减的情况。当     数据增加时，可能超出原先定义的元素个数；当数据减少时，造成内存浪费。 
<p>A-2. 链表动态地进行存储分配，可以适应数据动态地增减的情况，且可以方便地插入、     删除数据项。（数组中插入、删除数据项时，需要移动其它数据项）</p><p><br />B 从内存存储来看<br />B-1. (静态)数组从栈中分配空间, 对于程序员方便快速,但是自由度小<br />B-2. 链表从堆中分配空间, 自由度大但是申请管理比较麻烦.<br /><br /></p><p class="MsoNormal" style="BACKGROUND: white; MARGIN: 0cm 0cm 0pt 147pt; TEXT-INDENT: 21pt; TEXT-ALIGN: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-outline-level: 3" align="left"><b><span style="FONT-SIZE: 13.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">堆和栈的区别</span></b><b><span lang="EN-US" style="FONT-SIZE: 13.5pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b></p><p></p><p class="MsoNormal" style="BACKGROUND: white; MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-pagination: widow-orphan" align="left"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">solost </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">于</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> 2004</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">年</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> 10</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">月</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">09</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">日</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">发表</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></p><p></p><p class="MsoNormal" style="BACKGROUND: white; MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">一、预备知识</span></b><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">—</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">程序的内存分配</span></b><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span></b><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">一个由</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">c/C++</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">编译的程序占用的内存分为以下几个部分</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">1</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">、<b style="mso-bidi-font-weight: normal">栈区（</b></span><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">stack</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">）</span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">—   </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">由<b style="mso-bidi-font-weight: normal">编译器</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">(Compiler)</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">自动分配释放</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">，存放<b style="mso-bidi-font-weight: normal">函数的参数值</b>，<b style="mso-bidi-font-weight: normal">局部变量</b>的值等。其操作方式类似于数据结构中的栈。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">2</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">、<b style="mso-bidi-font-weight: normal">堆区（</b></span><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">heap</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">）</span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> —   </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">一般由<b style="mso-bidi-font-weight: normal">程序员</b>分配释放，</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">若程序员不释放，程序结束时可能由</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">OS</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">回收</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表，呵呵。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">3</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">、<b style="mso-bidi-font-weight: normal">全局区（静态区）</b>（</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">static</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">）</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">—</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">，<b style="mso-bidi-font-weight: normal">全局变量和静态变量的</b>存储是放在一块的，<b style="mso-bidi-font-weight: normal">初始化</b>的全局变量和静态变量在一块区域，</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">未初始化</span></b><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">的全局变量和未初始化的静态变量在相邻的另一块区域。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> - </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">程序结束后有系统释放</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">4</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">、<b style="mso-bidi-font-weight: normal">文字常量区</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">  — </span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">常量字符串</span></b><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">就是放在这里的。</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">程序结束后由<b style="mso-bidi-font-weight: normal">系统释放</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">5</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">、<b style="mso-bidi-font-weight: normal">程序代码区</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">— </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">存放<b style="mso-bidi-font-weight: normal">函数体的二进制代码</b>。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br style="mso-special-character: line-break" /><br style="mso-special-character: line-break" /></span></p><p></p><p class="MsoNormal" style="BACKGROUND: white; MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-pagination: widow-orphan; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto" align="left"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">二、例子程序</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">这是一个前辈写的，非常详细</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />//main.cpp <br />int a = 0; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">全局初始化区</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />char *p1; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">全局未初始化区</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />main() <br />{ <br />int b; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">栈</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />char s[] = "abc"; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">栈</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />char *p2; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">栈</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />char *p3 = "123456"; 123456\0</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">在常量区，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">p3</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">在栈上。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />static int c =0</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">；</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">全局（静态）初始化区</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />p1 = (char *)malloc(10); <br />p2 = (char *)malloc(20); <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">分配得来得</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">10</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">和</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">20</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">字节的区域就在堆区。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />strcpy(p1, "123456"); 123456\0</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">放在常量区，编译器<b style="mso-bidi-font-weight: normal">可能</b>会将它与</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">p3</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">所指向的</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">"123456"</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">优化成一个地方。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />} <br /><br /><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">二、堆和栈的理论知识</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /><b style="mso-bidi-font-weight: normal">2.1</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">申请方式</span></b><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">stack: <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">由系统自动分配。</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">例如，声明在函数中一个局部变量</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> int b; </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">系统自动在栈中为</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">b</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">开辟空间</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />heap: <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">需要程序员自己申请，并指明大小，在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">c</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">中</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">malloc</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">函数</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">如</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">p1 = (char *)malloc(10); <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">C++</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">中用</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">new</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">运算符</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">如</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">p2 = (char *)malloc(10); <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">但是注意</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">p1</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">、</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">p2</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">本身是在栈中的。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /><br /><br /><b style="mso-bidi-font-weight: normal">2.2 </b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">申请后系统的响应</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">栈：只要栈的剩余空间大于所申请空间，系统将为程序提供内存，否则将报异常提示栈溢出。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">堆：首先应该知道操作系统有一个记录空闲内存地址的链表，当系统收到程序的申请时，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间分配给程序，另外，对于大多数系统，会在这块内存空间中的首地址处记录本次分配的大小，这样，代码中的</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">delete</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">语句才能正确的释放本内存空间。另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表中。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /><br /><b style="mso-bidi-font-weight: normal">2.3</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">申请大小的限制</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">栈：在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">Windows</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">下</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">, </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">栈是<b style="mso-bidi-font-weight: normal">向低地址扩展</b>的数据结构，是一块<b style="mso-bidi-font-weight: normal">连续</b>的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是<b style="mso-bidi-font-weight: normal">系统预先规定好</b>的，在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">WINDOWS</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">下，栈的大小是</span><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">2M</span></b><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">（也有的说是</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">1M</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">，总之是一个编译时就确定的常数），如果申请的空间超过栈的剩余空间时，将提示</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">overflow</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">。因此，能从栈获得的空间较小。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">堆：堆是<b style="mso-bidi-font-weight: normal">向高地址扩展</b>的数据结构，是<b style="mso-bidi-font-weight: normal">不连续</b>的内存区域。这是由于系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中<b style="mso-bidi-font-weight: normal">有效的虚拟内存</b>。由此可见，堆获得的空间比较灵活，也比较大。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /><br /><br /><b style="mso-bidi-font-weight: normal">2.4</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">申请效率的比较：</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">栈由系统自动分配，速度较快。但程序员是<b style="mso-bidi-font-weight: normal">无法控制</b>的。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">堆是由</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">new</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">分配的内存，一般速度比较慢，而且<b style="mso-bidi-font-weight: normal">容易产生内存碎片</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">,</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">不过用起来最<b style="mso-bidi-font-weight: normal">方便</b></span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">. <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">另外，在</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">WINDOWS</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">下，最好的方式是用</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">VirtualAlloc</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">分配内存，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快内存，虽然用起来最不方便。但是速度快，也最灵活。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /><br /><b style="mso-bidi-font-weight: normal">2.5</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">堆和栈中的存储内容</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">栈：</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">在函数调用时，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">(1) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">第一个进栈的是主函数中后的下一条指令（函数调用语句的下一条可执行语句）的<b style="mso-bidi-font-weight: normal">地址</b>，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">(2) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">然后是函数的各个参数，在大多数的</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">C</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">编译器中，<b style="mso-bidi-font-weight: normal">参数是由右往左入栈</b>的，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">(3) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">然后是函数中的局部变量。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><span style="mso-tab-count: 1"> </span></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">注意</span></b><b style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">: </span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">静态变量是不入栈的。</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">当本次函数调用结束后，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">(1) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">局部变量先出栈，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">(2) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">然后是参数，</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">(3) </span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">最后栈顶指针指向最开始存的地址，也就是主函数中的下一条指令，<b style="mso-bidi-font-weight: normal">程序由该点继续运行</b>。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">堆：一般是在堆的头部用一个字节存放堆的大小。<b style="mso-bidi-font-weight: normal">堆中的具体内容有程序员安排。</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /><br /><b style="mso-bidi-font-weight: normal">2.6</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">存取效率的比较</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />char s1[] = "aaaaaaaaaaaaaaa"; <br />char *s2 = "bbbbbbbbbbbbbbbbb"; <br />aaaaaaaaaaa</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">是在<b style="mso-bidi-font-weight: normal">运行时刻</b>赋值的；</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">而</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">bbbbbbbbbbb</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">是在<b style="mso-bidi-font-weight: normal">编译时</b>就确定的；</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">但是，在以后的存取中，在栈上的数组比指针所指向的字符串</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">(</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">例如堆</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">)</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">快。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">比如：</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />#include <br />void main() <br />{ <br />char a = 1; <br />char c[] = "1234567890"; <br />char *p ="1234567890"; <br />a = c[1]; <br />a = p[1]; <br />return; <br />} <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">对应的汇编代码</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br />10: a = c[1]; <br />00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] <br />0040106A 88 4D FC mov byte ptr [ebp-4],cl <br />11: a = p[1]; <br />0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] <br />00401070 8A 42 01 mov al,byte ptr [edx+1] <br />00401073 88 45 FC mov byte ptr [ebp-4],al <br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">第一种在读取时直接就把字符串中的元素读到寄存器</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">cl</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">中，而第二种则要先把指针值读到</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">edx</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">中，在根据</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">edx</span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">读取字符，显然慢了。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /><br /><br /><b style="mso-bidi-font-weight: normal">2.7</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">小结：</span></b><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">堆和栈的区别可以用如下的比喻来看出：</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使用），吃饱了就走，不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是<b style="mso-bidi-font-weight: normal">快捷，但是自由度小。</b></span><b style="mso-bidi-font-weight: normal"><span style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></b><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"><br /></span><span style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体">使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的口味，而且自由度大。</span><span lang="EN-US" style="FONT-SIZE: 9pt; FONT-FAMILY: Verdana; mso-font-kerning: 0pt; mso-bidi-font-family: 宋体"> </span></p><p></p><p><br /><strong>深度优先搜索与广度优先搜索算法有何区别呢？<br /></strong>　　通常深度优先搜索法不全部保留结点，扩展完的结点从数据库中弹出删去，这样，一般在数据库中存储的结点数就是深度值，因此它占用空间较少。所以，当搜索树的结点较多，用其它方法易产生内存溢出时，深度优先搜索不失为一种有效的求解方法。<br />　　广度优先搜索算法，一般需存储产生的所有结点，占用的存储空间要比深度优先搜索大得多，因此，程序设计中，必须考虑溢出和节省内存空间的问题。但广度优先搜索法一般无回溯操作，即入栈和出栈的操作，所以运行速度比深度优先搜索要快些</p></div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21709.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 10:30 <a href="http://www.cnitblog.com/zouzheng/articles/21709.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++中的虚函数(virtual function)</title><link>http://www.cnitblog.com/zouzheng/articles/21708.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 02:26:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21708.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21708.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21708.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21708.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21708.html</trackback:ping><description><![CDATA[
		<p>C++中的虚函数(virtual function) <br />1.简介 <br />    虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。假设我们有下面的类层次：</p>
		<p>class A<br />{<br />public:<br />    virtual void foo() { cout &lt;&lt; "A::foo() is called" &lt;&lt; endl;}<br />};</p>
		<p>class B: public A<br />{<br />public:<br />    virtual void foo() { cout &lt;&lt; "B::foo() is called" &lt;&lt; endl;}<br />};</p>
		<p>那么，在使用的时候，我们可以：</p>
		<p>A * a = new B();<br />a-&gt;foo();       // 在这里，a虽然是指向A的指针，但是被调用的函数(foo)却是B的!</p>
		<p>    这个例子是虚函数的一个典型应用，通过这个例子，也许你就对虚函数有了一些概念。它虚就虚在所谓“推迟联编”或者“动态联编”上，一个类函数的调用并不是在编译时刻被确定的，而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数，所以被成为“虚”函数。</p>
		<p>    虚函数只能借助于指针或者引用来达到多态的效果，如果是下面这样的代码，则虽然是虚函数，但它不是多态的：</p>
		<p>class A<br />{<br />public:<br />    virtual void foo();<br />};</p>
		<p>class B: public A<br />{<br />    virtual void foo();<br />};</p>
		<p>void bar()<br />{<br />    A a;<br />    a.foo();   // A::foo()被调用<br />}</p>
		<p>1.1 多态 <br />    在了解了虚函数的意思之后，再考虑什么是多态就很容易了。仍然针对上面的类层次，但是使用的方法变的复杂了一些：</p>
		<p>void bar(A * a)<br />{<br />    a-&gt;foo();  // 被调用的是A::foo() 还是B::foo()？<br />}</p>
		<p>因为foo()是个虚函数，所以在bar这个函数中，只根据这段代码，无从确定这里被调用的是A::foo()还是B::foo()，但是可以肯定的说：如果a指向的是A类的实例，则A::foo()被调用，如果a指向的是B类的实例，则B::foo()被调用。</p>
		<p>这种同一代码可以产生不同效果的特点，被称为“多态”。</p>
		<p>1.2 多态有什么用？ <br />    多态这么神奇，但是能用来做什么呢？这个命题我难以用一两句话概括，一般的C++教程（或者其它面向对象语言的教程）都用一个画图的例子来展示多态的用途，我就不再重复这个例子了，如果你不知道这个例子，随便找本书应该都有介绍。我试图从一个抽象的角度描述一下，回头再结合那个画图的例子，也许你就更容易理解。</p>
		<p>    在面向对象的编程中，首先会针对数据进行抽象（确定基类）和继承（确定派生类），构成类层次。这个类层次的使用者在使用它们的时候，如果仍然在需要基类的时候写针对基类的代码，在需要派生类的时候写针对派生类的代码，就等于类层次完全暴露在使用者面前。如果这个类层次有任何的改变（增加了新类），都需要使用者“知道”（针对新类写代码）。这样就增加了类层次与其使用者之间的耦合，有人把这种情况列为程序中的“bad smell”之一。</p>
		<p>    多态可以使程序员脱离这种窘境。再回头看看1.1中的例子，bar()作为A-B这个类层次的使用者，它并不知道这个类层次中有多少个类，每个类都叫什么，但是一样可以很好的工作，当有一个C类从A类派生出来后，bar()也不需要“知道”（修改）。这完全归功于多态--编译器针对虚函数产生了可以在运行时刻确定被调用函数的代码。</p>
		<p>1.3 如何“动态联编” <br />    编译器是如何针对虚函数产生可以再运行时刻确定被调用函数的代码呢？也就是说，虚函数实际上是如何被编译器处理的呢？Lippman在深度探索C++对象模型[1]中的不同章节讲到了几种方式，这里把“标准的”方式简单介绍一下。</p>
		<p>    我所说的“标准”方式，也就是所谓的“VTABLE”机制。编译器发现一个类中有被声明为virtual的函数，就会为其搞一个虚函数表，也就是VTABLE。VTABLE实际上是一个函数指针的数组，每个虚函数占用这个数组的一个slot。一个类只有一个VTABLE，不管它有多少个实例。派生类有自己的VTABLE，但是派生类的VTABLE与基类的VTABLE有相同的函数排列顺序，同名的虚函数被放在两个数组的相同位置上。在创建类实例的时候，编译器还会在每个实例的内存布局中增加一个vptr字段，该字段指向本类的VTABLE。通过这些手段，编译器在看到一个虚函数调用的时候，就会将这个调用改写，针对1.1中的例子：</p>
		<p>void bar(A * a)<br />{<br />    a-&gt;foo();<br />}</p>
		<p>会被改写为：</p>
		<p>void bar(A * a)<br />{<br />    (a-&gt;vptr[1])();<br />}</p>
		<p>    因为派生类和基类的foo()函数具有相同的VTABLE索引，而他们的vptr又指向不同的VTABLE，因此通过这样的方法可以在运行时刻决定调用哪个foo()函数。</p>
		<p>    虽然实际情况远非这么简单，但是基本原理大致如此。</p>
		<p>1.4 overload和override <br />    虚函数总是在派生类中被改写，这种改写被称为“override”。我经常混淆“overload”和“override”这两个单词。但是随着各类C++的书越来越多，后来的程序员也许不会再犯我犯过的错误了。但是我打算澄清一下：</p>
		<p>override是指派生类重写基类的虚函数，就象我们前面B类中重写了A类中的foo()函数。重写的函数必须有一致的参数表和返回值（C++标准允许返回值不同的情况，这个我会在“语法”部分简单介绍，但是很少编译器支持这个feature）。这个单词好象一直没有什么合适的中文词汇来对应，有人译为“覆盖”，还贴切一些。 <br />overload约定成俗的被翻译为“重载”。是指编写一个与已有函数同名但是参数表不同的函数。例如一个函数即可以接受整型数作为参数，也可以接受浮点数作为参数。 <br />2. 虚函数的语法 <br />    虚函数的标志是“virtual”关键字。</p>
		<p>2.1 使用virtual关键字 <br />    考虑下面的类层次：</p>
		<p>class A<br />{<br />public:<br />    virtual void foo();<br />};</p>
		<p>class B: public A<br />{<br />public:<br />    void foo();    // 没有virtual关键字!<br />};</p>
		<p>class C: public B  // 从B继承，不是从A继承！<br />{<br />public:<br />    void foo();    // 也没有virtual关键字！<br />};</p>
		<p>    这种情况下，B::foo()是虚函数，C::foo()也同样是虚函数。因此，可以说，基类声明的虚函数，在派生类中也是虚函数，即使不再使用virtual关键字。</p>
		<p>2.2 纯虚函数 <br />    如下声明表示一个函数为纯虚函数：</p>
		<p>class A<br />{<br />public:<br />    virtual void foo()=0;   // =0标志一个虚函数为纯虚函数<br />};</p>
		<p>    一个函数声明为纯虚后，纯虚函数的意思是：我是一个抽象类！不要把我实例化！纯虚函数用来规范派生类的行为，实际上就是所谓的“接口”。它告诉使用者，我的派生类都会有这个函数。</p>
		<p>2.3 虚析构函数 <br />    析构函数也可以是虚的，甚至是纯虚的。例如：</p>
		<p>class A<br />{<br />public:<br />    virtual ~A()=0;   // 纯虚析构函数<br />};</p>
		<p>    当一个类打算被用作其它类的基类时，它的析构函数必须是虚的。考虑下面的例子：</p>
		<p>class A<br />{<br />public:<br />    A() { ptra_ = new char[10];}<br />    ~A() { delete[] ptra_;}        // 非虚析构函数<br />private:<br />    char * ptra_;<br />};</p>
		<p>class B: public A<br />{<br />public:<br />    B() { ptrb_ = new char[20];}<br />    ~B() { delete[] ptrb_;}<br />private:<br />    char * ptrb_;<br />};</p>
		<p>void foo()<br />{<br />    A * a = new B;<br />    delete a;<br />}</p>
		<p>    在这个例子中，程序也许不会象你想象的那样运行，在执行delete a的时候，实际上只有A::~A()被调用了，而B类的析构函数并没有被调用！这是否有点儿可怕？</p>
		<p>    如果将上面A::~A()改为virtual，就可以保证B::~B()也在delete a的时候被调用了。因此基类的析构函数都必须是virtual的。</p>
		<p>    纯虚的析构函数并没有什么作用，是虚的就够了。通常只有在希望将一个类变成抽象类（不能实例化的类），而这个类又没有合适的函数可以被纯虚化的时候，可以使用纯虚的析构函数来达到目的。</p>
		<p>2.4 虚构造函数？ <br />    构造函数不能是虚的。</p>
		<p>3. 虚函数使用技巧 3.1 private的虚函数 <br />    考虑下面的例子：</p>
		<p>class A<br />{<br />public:<br />    void foo() { bar();}<br />private:<br />    virtual void bar() { ...}<br />};</p>
		<p>class B: public A<br />{<br />private:<br />    virtual void bar() { ...}<br />};</p>
		<p>    在这个例子中，虽然bar()在A类中是private的，但是仍然可以出现在派生类中，并仍然可以与public或者protected的虚函数一样产生多态的效果。并不会因为它是private的，就发生A::foo()不能访问B::bar()的情况，也不会发生B::bar()对A::bar()的override不起作用的情况。</p>
		<p>    这种写法的语意是：A告诉B，你最好override我的bar()函数，但是你不要管它如何使用，也不要自己调用这个函数。</p>
		<p>3.2 构造函数和析构函数中的虚函数调用 <br />    一个类的虚函数在它自己的构造函数和析构函数中被调用的时候，它们就变成普通函数了，不“虚”了。也就是说不能在构造函数和析构函数中让自己“多态”。例如：</p>
		<p>class A<br />{<br />public:<br />    A() { foo();}        // 在这里，无论如何都是A::foo()被调用！<br />    ~A() { foo();}       // 同上<br />    virtual void foo();<br />};</p>
		<p>class B: public A<br />{<br />public:<br />    virtual void foo();<br />};</p>
		<p>void bar()<br />{<br />    A * a = new B;<br />    delete a;<br />}</p>
		<p>    如果你希望delete a的时候，会导致B::foo()被调用，那么你就错了。同样，在new B的时候，A的构造函数被调用，但是在A的构造函数中，被调用的是A::foo()而不是B::foo()。</p>
		<p>3.3 多继承中的虚函数 3.4 什么时候使用虚函数 <br />    在你设计一个基类的时候，如果发现一个函数需要在派生类里有不同的表现，那么它就应该是虚的。从设计的角度讲，出现在基类中的虚函数是接口，出现在派生类中的虚函数是接口的具体实现。通过这样的方法，就可以将对象的行为抽象化。</p>
		<p>    以设计模式[2]中Factory Method模式为例，Creator的factoryMethod()就是虚函数，派生类override这个函数后，产生不同的Product类，被产生的Product类被基类的AnOperation()函数使用。基类的AnOperation()函数针对Product类进行操作，当然Product类一定也有多态（虚函数）。</p>
		<p>    另外一个例子就是集合操作，假设你有一个以A类为基类的类层次，又用了一个std::vector&lt;A *&gt;来保存这个类层次中不同类的实例指针，那么你一定希望在对这个集合中的类进行操作的时候，不要把每个指针再cast回到它原来的类型（派生类），而是希望对他们进行同样的操作。那么就应该将这个“一样的操作”声明为virtual。</p>
		<p>    现实中，远不只我举的这两个例子，但是大的原则都是我前面说到的“如果发现一个函数需要在派生类里有不同的表现，那么它就应该是虚的”。这句话也可以反过来说：“如果你发现基类提供了虚函数，那么你最好override它”。</p>
		<p>4.参考资料 <br />[1] 深度探索C++对象模型，Stanley B.Lippman，侯捷译</p>
		<p>[2] Design Patterns, Elements of Reusable Object-Oriented Software, GOF</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21708.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 10:26 <a href="http://www.cnitblog.com/zouzheng/articles/21708.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重要的C++编程总结(一) －by MingGe </title><link>http://www.cnitblog.com/zouzheng/articles/21706.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 01:04:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21706.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21706.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21706.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21706.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21706.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1		．结构体对齐的具体含义（		#pragma pack		的用法）																						朋友帖了如下一段代码：																												　　				#pragma pack(4)								　　				class TestB								...&nbsp;&nbsp;<a href='http://www.cnitblog.com/zouzheng/articles/21706.html'>阅读全文</a><img src ="http://www.cnitblog.com/zouzheng/aggbug/21706.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 09:04 <a href="http://www.cnitblog.com/zouzheng/articles/21706.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sizeof()</title><link>http://www.cnitblog.com/zouzheng/articles/21704.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Wed, 10 Jan 2007 00:49:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/21704.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/21704.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/21704.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/21704.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/21704.html</trackback:ping><description><![CDATA[
		<p>
				<font size="2">
						<strong>
								<font color="#0000ff">1. 定义：<br /></font>
						</strong>    sizeof是何方神圣？</font>
		</p>
		<p>
				<font size="2">    sizeof 乃 C/C++ 中的一个操作符（operator）是也。简单说其作用就是返回一个对象或者类型所占的内存字节数。<br /><br />MSDN上的解释为：</font>
		</p>
		<p>
				<font size="2">The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types).This keyword returns a value of type size_t.</font>
		</p>
		<p>
				<font size="2">    其返回值类型为size_t，在头文件stddef.h中定义。这是一个依赖于编译系统的值，一般定义为</font>
		</p>
		<p>
				<font size="2">typedef unsigned int size_t;</font>
		</p>
		<p>
				<font size="2">    世上编译器林林总总，但作为一个规范，它们都会保证char、signed char和unsigned char的sizeof值为1，毕竟char是我们编程能用的最小数据类型。<br /><br /><strong><font color="#0000ff">2. 语法：</font></strong><br />    sizeof有三种语法形式，如下：<br />    1) sizeof( object );    // sizeof( 对象 );<br />    2) sizeof( type_name ); // sizeof( 类型 );<br />    3) sizeof object;       // sizeof 对象;</font>
		</p>
		<p>
				<font size="2">所以，<br />int i;<br />sizeof( i );     // ok<br />sizeof i;        // ok<br />sizeof( int );   // ok<br />sizeof int;      // error</font>
		</p>
		<p>
				<font size="2">既然写法2可以用写法1代替，为求形式统一以及减少我们大脑的负担，第2种写法，忘掉它吧！</font>
		</p>
		<p>
				<font size="2">实际上，sizeof计算对象的大小也是转换成对对象类型的计算。也就是说，同种类型的不同对象其sizeof值都是一致的。</font>
		</p>
		<p>
				<font size="2">这里，对象可以进一步延伸至表达式，即sizeof可以对一个表达式求值。编译器<strong>根据表达式的最终结果类型来确定大小</strong>，一般不会对表达式进行计算。</font>
		</p>
		<p>
				<font size="2">例如：</font>
		</p>
		<p>
				<font size="2">sizeof( 2 );        // 2的类型为int，所以等价于 sizeof( int );<br />sizeof( 2 + 3.14 ); // 3.14的类型为double，2也会被提升成double类型，所以等价于 sizeof( double );<br /><br />    sizeof也可以对一个函数调用求值，其<font color="#0000ff">结果是函数返回类型的大小，函数并不会被调用。</font>我们来看一个完整的例子：</font>
		</p>
		<p>
				<font color="#0000ff" size="2">*********************************************************</font>
		</p>
		<p>
				<font size="2">char foo()<br />{<br />    printf("foo() has been called.\n");<br />    return 'a';<br />}<br />int main()<br />{<br />    size_t sz = sizeof( foo() );   // foo() 的返回值类型为char，所以sz = sizeof(char)，但函数foo()并不会被调用<br />    printf("sizeof( foo() ) = %d\n", sz);<br />}</font>
		</p>
		<p>
				<font color="#0000ff" size="2">*********************************************************</font>
		</p>
		<p>
				<font size="2">C99标准规定，函数、不能确定类型的表达式以及位域（bit-field）成员不能被计算sizeof值，即下面这些写法都是错误的：</font>
		</p>
		<p>
				<font size="2">    sizeof( foo );     // error<br />    void foo2() { }<br />    sizeof( foo2() );  // error<br />    struct S<br />    {<br />        unsigned int f1 : 1;<br />        unsigned int f2 : 5;<br />        unsigned int f3 : 12;<br />    };<br />    sizeof( S.f1 );   // error</font>
		</p>
		<font size="2">
				<p>
						<br />
						<strong>
								<font color="#0000ff">3. sizeof的常量性</font>
						</strong>
				</p>
				<strong>
						<font color="#0000ff">
						</font>
				</strong>
				<p>
						<br />    sizeof的计算发生在编译时刻，所以它可以被当作常量表达式使用。如：</p>
				<p>char ary[ sizeof( int ) * 10 ];   // ok</p>
				<p>最新的C99标准规定sizeof也可以在运行时刻进行计算。如下面的程序在Dev-C++中可以正确执行：</p>
				<p>int n;<br />n = 10;        // n动态赋值<br />char ary[n];   // C99也支持数组的动态定义<br />printf("%d\n", sizeof(ary)); // ok. 输出10</p>
				<p>但在没有完全实现C99标准的编译器中就行不通了，上面的代码在VC6中就通不过编译。所以我们<font color="#0000ff">最好还是认为sizeof是在编译期执行的</font>，这样不会带来错误，让程序的可移植性强些。</p>
				<p>
						<br />
						<strong>
								<font color="#0000ff">4. 基本数据类型的sizeof</font>
						</strong>
				</p>
				<p>这里的基本数据类型指short、int、long、float、double这样的简单内置数据类型。由于它们都是和系统相关的，所以在不同的系统下取值可能不同。这务必引起我们的注意，尽量不要在这方面给自己程序的移植造成麻烦。</p>
				<p>一般的，在32位编译环境中，sizeof(int)的取值为4。</p>
				<p>
						<br />
						<strong>
								<font color="#0000ff">5. 指针变量的sizeof</font>
						</strong>
				</p>
				<p>学过数据结构的你应该知道指针是一个很重要的概念，它记录了另一个对象的地址。<font color="#0000ff">既然是来存放地址的，那么它当然等于计算机内部地址总线的宽度。</font>所以在32位计算机中，一个指针变量的返回值必定是4（注意结果是以字节为单位）。可以预计，在将来的64位系统中指针变量的sizeof结果为8。</p>
				<p>
						<font color="#0000ff">*********************************************************</font>
				</p>
				<p>char* pc = "abc";<br />int* pi;<br />string* ps;<br />char** ppc = &amp;pc;<br />void (*pf)(); // 函数指针<br />sizeof( pc ); // 结果为4<br />sizeof( pi ); // 结果为4<br />sizeof( ps ); // 结果为4<br />sizeof( ppc );// 结果为4<br />sizeof( pf ); // 结果为4</p>
				<p>
						<font color="#0000ff">*********************************************************</font>
				</p>
				<p>指针变量的sizeof值与指针所指的对象没有任何关系，正是由于所有的指针变量所占内存大小相等，所以MFC消息处理函数使用两个参数WPARAM、LPARAM就能传递各种复杂的消息结构（使用指向结构体的指针）。</p>
				<p>
						<br />
						<strong>
								<font color="#0000ff">6. 数组的sizeof</font>
						</strong>
				</p>
				<p>
						<font color="#0000ff">数组的sizeof值等于数组所占用的内存字节数</font>，如：</p>
				<p>char a1[] = "abc";<br />int a2[3];<br />sizeof( a1 ); // 结果为4，字符 末尾还存在一个NULL终止符<br />sizeof( a2 ); // 结果为3*4=12（依赖于int）</p>
				<p>一些朋友刚开始时把sizeof当作了求数组元素的个数，现在，你应该知道这是不对的。那么应该怎么求数组元素的个数呢？</p>
				<p>Easy，通常有下面两种写法：</p>
				<p>int c1 = sizeof( a1 ) / sizeof( char );    // 总长度/单个元素的长度<br />int c2 = sizeof( a1 ) / sizeof( a1[0]);    // 总长度/第一个元素的长度</p>
				<p>
						<br />写到这里，提一问，下面的c3，c4值应该是多少呢？</p>
				<p>
						<font color="#0000ff">*********************************************************</font>
				</p>
				<p>void foo3(char a3[3])<br />{<br />    int c3 = sizeof( a3 ); // c3 ==<br />}<br />void foo4(char a4[])<br />{<br />    int c4 = sizeof( a4 ); // c4 ==<br />}</p>
				<p>
						<font color="#0000ff">*********************************************************</font>
				</p>
				<p>也许当你试图回答c4的值时已经意识到c3答错了，是的，c3!=3。</p>
				<p>这里函数参数<font color="#0000ff">a3已不再是数组类型，而是蜕变成指针。</font>相当于char* a3，为什么仔细想想就不难明白。</p>
				<p>我们调用函数foo1时，程序会在栈上分配一个大小为3的数组吗？不会！</p>
				<p>数组是“传址”的，调用者只需将实参的地址传递过去，所以a3自然为指针类型（char*），c3的值也就为4。</p>
				<p>
						<br />
						<strong>
								<font color="#0000ff">7. 结构体的sizeof</font>
						</strong>
				</p>
				<p>这是初学者问得最多的一个问题，所以这里有必要多费点笔墨。让我们先看一个结构体：</p>
				<p>struct S1<br />{<br />    char c;<br />    int i;<br />};</p>
				<p>问sizeof(s1)等于多少？</p>
				<p>聪明的你开始思考了，char占1个字节，int占4个字节，那么加起来就应该是5。</p>
				<p>是这样吗？</p>
				<p>你在你机器上试过了吗？</p>
				<p>也许你是对的，但很可能你是错的！</p>
				<p>VC6中按默认设置得到的结果为8。<br /><br />    Why？为什么受伤的总是我？</p>
				<p>请不要沮丧，我们来好好琢磨一下sizeof的定义 —— sizeof的结果等于对象或者类型所占的内存字节数。好吧，那就让我们来看看S1的内存分配情况：</p>
				<p>S1 s1 = { 'a', 0xFFFFFFFF };</p>
				<p>定义上面的变量后，加上断点，运行程序，观察s1所在的内存，你发现了什么？</p>
				<p>以我的VC6.0为例，s1的地址为0x0012FF78，其数据内容如下：</p>
				<p>0012FF78: 61 CC CC CC FF FF FF FF<br /><br />发现了什么？怎么中间夹杂了3个字节的CC？</p>
				<p>看看MSDN上的说明：</p>
				<p>When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment.</p>
				<p dir="ltr">原来如此，这就是传说中的字节对齐啊！一个重要的话题出现了。</p>
				<p dir="ltr">为什么需要字节对齐？</p>
				<p dir="ltr">计算机组成原理教导我们，这样有助于加快计算机的取数速度，否则就得多花指令周期了。</p>
				<p dir="ltr">为此，编译器默认会对结构体进行处理（实际上其它地方的数据变量也是如此），<font color="#0000ff">让宽度为2的基本数据类型（short等）都位于能被2整除的地址上，让宽度为4的基本数据类型（int等）都位于能被4整除的地址上。</font>以此类推，这样，两个数中间就可能需要加入填充字节，所以整个结构体的sizeof值就增长了。</p>
				<p dir="ltr">让我们交换一下S1中char与int的位置：</p>
				<p dir="ltr">struct S2<br />{<br />    int i;<br />    char c;<br />};</p>
				<p dir="ltr">看看sizeof(S2)的结果为多少？怎么还是8。</p>
				<p dir="ltr">再看看内存，原来成员c后面仍然有3个填充字节。</p>
				<p dir="ltr">这又是为什么啊？别着急，下面总结规律。</p>
		</font>
		<p>
				<font size="2">    <strong><font color="#0000ff">字节对齐的细节和编译器实现相关，但一般而言，满足三个准则：</font></strong><br />    1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除；<br />    2) 结构体每个成员相对于结构体首地址的偏移量（offset）都是成员大小的整数倍，如有需要编译器会在成员之间加上填充字节（internal adding）；<br />    3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍，如有需要编译器会在最末一个成员之后加上填充字节（trailing padding）。<br /><br />    对于上面的准则，有几点需要说明：<br />1) 前面不是说结构体成员的地址是其大小的整数倍，怎么又说到偏移量了呢？</font>
		</p>
		<p>
				<font size="2">因为有了第1点存在，所以我们就可以只考虑成员的偏移量，这样思考起来简单。想想为什么。</font>
		</p>
		<p>
				<font size="2">结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得，这个宏也在stddef.h中定义，如下：</font>
		</p>
		<p>
				<font size="2">#define offsetof(s,m) (size_t)&amp;(((s *)0)-&gt;m)</font>
		</p>
		<p>
				<font size="2">例如，想要获得S2中c的偏移量，方法为</font>
		</p>
		<p>
				<font size="2">size_t pos = offsetof(S2, c);// pos等于4</font>
		</p>
		<font size="2">
				<p>
						<br />2) 基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型。这里所说的“数据宽度”就是指其sizeof的大小。由于结构体的成员可以是复合类型，比如另外一个结构体，所以在寻找最宽基本类型成员时，应当包括复合类型成员的子成员，而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。</p>
				<p>这里叙述起来有点拗口，思考起来也有点挠头，还是让我们看看例子吧（具体数值仍以VC6为例，以后不再说明）：</p>
				<p>struct S3<br />{<br />    char c1;<br />    S1 s;<br />    char c2;<br />};</p>
				<p>S1的最宽简单成员的类型为int，S3在考虑最宽简单类型成员时是将S1“打散”看的，所以S3的最宽简单类型为int。这样，通过S3定义的变量，其存储空间首地址需要被4整除，整个sizeof(S3)的值也应该被4整除。</p>
				<p>c1的偏移量为0，s的偏移量呢？这时s是一个整体，它作为结构体变量也满足前面三个准则，所以其大小为8，偏移量为4，c1与s之间便需要3个填充字节，而c2与s之间就不需要了，所以c2的偏移量为12，算上c2的大小为13，13是不能被4整除的，这样末尾还得补上3个填充字节。最后得到sizeof(S3)的值为16。</p>
				<p>
						<br />    通过上面的叙述，我们可以得到一个公式：<br />    <font color="#0000ff"><strong>结构体的大小等于最后一个成员的偏移量加上其大小再加上末尾的填充字节数目，</strong></font>即：</p>
		</font>
		<p>
				<font size="2">sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( trailing padding )</font>
		</p>
		<p>
				<font size="2">
				</font> </p>
		<div>
				<font face="宋体" color="#0000ff" size="2">
						<strong>8.类的sizeof</strong>
				</font>
		</div>
		<div> </div>
		<div>
				<font face="宋体" size="2">类的sizeof值等于类中成员变量所占用的内存字节数。如：<br /></font>
		</div>
		<div>
				<font face="宋体" color="#0000ff" size="2">****************************************************************</font>
		</div>
		<div>
				<font face="宋体" size="2"> </font>
		</div>
		<div>
				<font face="宋体" size="2">class A<br />{<br /> public:<br />     int b;<br />     float c;<br />     char d;<br />};</font>
		</div>
		<font face="宋体" size="2">
				<div>
						<br />int main(void)<br />{<br />  A object;<br />  cout &lt;&lt; "sizeof(object) is " &lt;&lt; sizeof(object) &lt;&lt; endl;<br />  return 0 ;<br />}</div>
				<div> </div>
				<div>
						<font color="#0000ff">***************************************************************</font>
				</div>
		</font>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" size="2">输出结果为12（我的机器上sizeof(float)值为4，字节对其前面已经讲过）。</font>
		</div>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" size="2">不过需要注意的是，如果类中存在静态成员变量，结果又会是什么样子呢？</font>
		</div>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" color="#0000ff" size="2">***************************************************************</font>
				<font size="+0">
				</font>
		</div>
		<div>
				<br />
				<font face="宋体" size="2">class A<br />{<br /> public:<br />     static int a;<br />     int b;<br />     float c;<br />     char d;<br />};</font>
		</div>
		<font face="宋体" size="2">
				<div>
						<br />int main()<br />{</div>
				<div>  A object;<br />  cout &lt;&lt; "sizeof(object) is " &lt;&lt; sizeof(object) &lt;&lt; endl;<br />  return 0 ;<br />}</div>
		</font>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" color="#0000ff" size="2">**************************************************************</font>
		</div>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" size="2">16？不对。结果仍然是12.</font>
		</div>
		<font face="宋体" size="2">
				<div>
						<br />因为在程序编译期间，就已经为static变量在静态存储区域分配了内存空间，并且这块内存在程序的整个运行期间都存在。</div>
				<div>
						<br />而每次声明了类A的一个对象的时候，为该对象在堆上，根据对象的大小分配内存。</div>
		</font>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" size="2">如果类A中包含成员函数，那么又会是怎样的情况呢？看下面的例子</font>
		</div>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" color="#0000ff" size="2">*************************************************************</font>
				<font size="+0">
				</font>
		</div>
		<div>
				<br />
				<font face="宋体" size="2">class A<br />{<br /> public:<br />     static int a;<br />     int b;<br />     float c;<br />     char d;<br />     int add(int x,int y)<br />     {<br />       return x+y;<br />     }<br />};</font>
		</div>
		<div>
				<font face="宋体" size="2">
						<br />int main()<br />{<br />  A object;<br />  cout &lt;&lt; "sizeof(object) is " &lt;&lt; sizeof(object) &lt;&lt; endl;<br />  b = object.add(3,4);<br />  cout &lt;&lt; "sizeof(object) is " &lt;&lt; sizeof(object) &lt;&lt; endl;<br />  return 0 ;<br />}</font>
		</div>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体" color="#0000ff" size="2">***************************************************************</font>
		</div>
		<div>
				<font face="宋体" size="2">
				</font> </div>
		<div>
				<font face="宋体">
						<font size="2">结果仍为12。<font face="Arial" color="#0000ff"></font></font>
				</font>
		</div>
		<div>
				<font face="宋体">
						<font face="Arial" color="#0000ff" size="2">
						</font> </font>
		</div>
		<div dir="ltr" align="left">
				<span>
						<font size="2">因为只有非静态类成员变量在新生成一个object的时候才需要自己的副本。</font>
				</span>
		</div>
		<div dir="ltr" align="left">
				<span>
						<font size="2">所以每个非静态成员变量在生成新object需要内存，而function是不需要的。</font>
				</span>
		</div>
		<div dir="ltr" align="left">
				<span>
						<font size="2">
						</font>
				</span> </div>
		<div dir="ltr" align="left">
				<span>
						<font size="2">
						</font>
				</span> </div>
		<div dir="ltr" align="left">
				<span>
						<font size="2">注：C++中的多态和虚继承也是非常重要的东西，不过比较复杂，编译器不同，细节也有所不同。（以后慢慢研究，哈哈）</font>
				</span>
		</div>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/21704.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2007-01-10 08:49 <a href="http://www.cnitblog.com/zouzheng/articles/21704.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 指针专题 </title><link>http://www.cnitblog.com/zouzheng/articles/20699.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 17 Dec 2006 10:39:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20699.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20699.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20699.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20699.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20699.html</trackback:ping><description><![CDATA[一、<strong>数组的指针、指针数组以及指向指针的指针</strong><p>　　考虑数组的指针的时候我们要同时考虑类型和维数这两个属性。换一句话，就是说一个数组排除在其中存储的数值，那么可以用类型和维数来位置表示他的种类。</p><p>A）一维数组<br />　　在c和c++中数组的指针就是数组的起始地址（也就第一个元素的地址），而且标准文档规定数组名代表数组的地址（这是地址数值层面的数组表示）。例如：</p><pre>int a[10];int *p;</pre><p>p=&amp;a[0]//和p=a是等价的：<br /><br />　　因为a是数组名，所以他是该数组的地址，同时因为第一个元素为a[0]，那么&amp;a[0]也代表了该数组的地址。但是我们是不是就说一个数组名和该数组的第一个元素的&amp;运算是一回事呢？在一维的时候当时是的，但是在高维的时候，我们要考虑到维数给数组带来的影响。<br />　　a[10]是一个数组，a是数组名，它是一个包含10个int类型的数组类型，不是一般的指针变量噢！（虽然标准文档规定在c++中从int[]到int*直接转换是可以的，在使用的时候似乎在函数的参数为指针的时候，我们将该数组名赋值没有任何异样），a代表数组的首地址，在数字层面和a[10]的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。<br />所以我们要注意以下问题：</p><p>（1） p[i]和a[i]都是代表该数组的第i+1个元素；<br />（2） p+i和a+i代表了第i+1个元素的地址，所以我们也可以使用　*（p+I）和*（a+I）来引用对象元素；<br />（3）p+1不是对于指针数量上加一，而是表示从当前的位置跳过当前指针指向类型长度的空间，对于win32的int为4byte；</p><p>B)多维数组<br />　　对于二维数组a[4][6];由于数组名代表数组的起始地址，所以a（第一层）和第一个元素a[0][0]地址的数字是相同的，但是意义却是不同的。对于该数组我们可以理解为：a的一维数组（第一层），它有四个元素a[0]、a[1]、a[2]、a[3]（第二层），而每个元素又含有6个元素a[0][0],a[0][1],a[0][2],a[0][3],a[0][4]，a[0][5]（第三层）,…到此我们终于访问到了每个元素了，这个过程我们经历了：a-&gt;a[0]-&gt;a[0][0]；<br />　　整体来讲：a是一个4行5列的二维数组，a表示它指向的数组的首地址（第一个元素地址&amp;a[0]）,同时a[0]指向一行，它是这个行的名字（和该行的第一个元素的首地址相同（第一个元素为地址&amp;a[0][0]））。所以从数字角度说：a、a[0]、&amp;a[0][0]是相同的，但是他们所处的层次是不同的。<br />　　既然a代表二维数组，那么a+i就表示它的第i+1个元素*（a+i）的地址，而在二维数组中<br />*（a+i）又指向一个数组，*（a+i）+j表示这个数组的第j+1个元素的地址，所以要访问这个元素可以使用 *（*（a+i）+j）（也就是a[i][j]）。<br />他们的示意图为（虚线代表不是实际存在的）：</p><p><img height="199" alt="" src="http://www.vckbase.com/document/journal/vckbase48/images/pntfigimg1.gif" width="291" border="0" /></p><p>对照这个图，如下的一些说法都是正确的（对于a[4][6]）：</p><ul><li>a是一个数组类型，*a指向一个数组； 
</li><li>a+i指向一个数组； 
</li><li>a、*a和&amp;a[0][0]数值相同； 
</li><li>a[i]+j和*（a+i）+j是同一个概念； </li></ul><p>　　总结一下就是：我们对于二维指针a，他指向数组a[0，1，2，3]，使用*，可以使他降级到第二层次，这样*a就指向了第一个真正的数组。对于其他的情况我们也可以采用相同的方式，对于其他维数和类型的数组我们可以采用相类似的思想。<br /><br />说到指向数组的指针，我们还可以声明一个指针变量让它指向一个数组。例如：</p><pre>int （*p）[5]；</pre><p>这时p就是一个指针，要指向一个含有5个int类型元素的数组，指向其他的就会出现问题。<br />这个时候我们可以使用上面的什么东西来初始化呢？<br />我们可以使用*a,*(a+1)，a[2]等。<br />原因很简单：我们在一个二维的数组中，那么表达方式有上面的相互类似的意义呢？只有　*a,*(a+1)，a[2]等，</p><p>C)指针数组<br />　　一个指针数组是指一个数组中的每个元素都是一个指针，例如：</p><pre>int *p[10];//而不能是int (*p)[10]</pre><p>或者</p><pre>char *p[10];</pre><p>此时p是一个指针（数值上和&amp;p[0]一样）；<br />在前面有int t[10]；</p><pre>int * pt=t;//使用pt指向t</pre><p>那么这里我们用什么指向int *t[10]中的t呢？我们要使用一个指针的指针：</p><pre>int **pt=t;</pre><p>　　这是因为：在int *t[10]中，每个元素是指针，那么同时t又指向这个数组，数组上和&amp;t[0]相同，也就是指向t[0]，指向一个指针变量，可以说是一个指针的指针了，所以自然要用</p><pre>int **pt;</pre><p><br />D)指针的指针<br />　　一个指针变量内部可以存储一个值，这个值是另外一个对象的地址，所以我们说一个指针变量可以指向一个普通变量，同样这个指针变量也有一个地址，也就是说有一个东西可以指向这个指针变量，然后再通过这个指针变量指向这个对象。那么如何来指向这个指针变量呢？由于指针变量本身已经是一个指针了（右值），那么我们这里就不能用一般的指针了，需要在指针上体现出来这些特点，我们需要定义指针的指针（二重指针）。</p><pre>int *p1=&amp;i;int**p2=&amp;p1；</pre><p>综合以上的所有点，下面是我们常常看到一些匹配（也是经常出错的地方）：</p><pre>int a[3],b[2][3],c,*d[3];void fun1(int *p)；void fun2(int (*p)[3]);void fun3(int **p);void fun4(int p[3]); void fun5(int p[]);void fun6(int p[2][3]);void fun7(int (&amp;p)[3]);</pre><p>函数 不会产生编译时刻的可能值（但逻辑上不一定都对）</p><table class="MsoNormalTable" id="table1" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 5.4pt; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" width="389" border="1"><tbody><tr style="HEIGHT: 15pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 15pt" valign="top"><p class="MsoNormal"><span style="FONT-FAMILY: 宋体">函数</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 15pt" valign="top"><p class="MsoNormal"><span style="FONT-FAMILY: 宋体">不会产生编译时刻的可能值（但逻辑上不一定都对）</span></p></td></tr><tr style="HEIGHT: 6.75pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 6.75pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun1</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 6.75pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">a, &amp;a[i], *b ,b[i],&amp;b[i][j] ,&amp;c ,d[i]</span></p></td></tr><tr style="HEIGHT: 14.25pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 14.25pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun2</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 14.25pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">b</span><span style="FONT-FAMILY: 宋体">，<span lang="EN-US">b+i</span>，</span></p></td></tr><tr style="HEIGHT: 14.25pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 14.25pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun3</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 14.25pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">d</span></p></td></tr><tr style="HEIGHT: 13.5pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.5pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun4</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.5pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">a, &amp;a[i], *b ,b[i],&amp;b[i][j] ,&amp;c ,d[i]</span></p></td></tr><tr style="HEIGHT: 17.25pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 17.25pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun5</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 17.25pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">a, &amp;a[i], *b ,b[i],&amp;b[i][j] ,&amp;c ,d[i]</span></p></td></tr><tr style="HEIGHT: 10.5pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 10.5pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun6</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 10.5pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">  b</span></p></td></tr><tr style="HEIGHT: 16.95pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 16.95pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun7</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 16.95pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">a</span></p></td></tr></tbody></table><p>为什么可以有这样的搭配，原因如下：</p><ul><li>对于fun1 fun4 fun 5: 在编译器看来fun1,fun4,fun5的声明是一样，在编译时候，编译器把数组的大小舍去不考虑，只考虑它是一个指针，也就是说有没有大小说明是一样的，所以三者的形式都是fun1的形式（其实只要提供了int*指针就可以了）； 
</li><li>对于fun7 ：以上的解释对于引用是不适用的，如果变量被声明为数组的引用，那么编译器就要考虑数组的大小了，那么必须和声明一模一样（所以fun7就只有a合适）； 
</li><li>对于fun2：p是一个指向一个含有3个元素的数组，这样b和b+i正好合适，而a却不是（它是指向a[0]的，不是指向这个数组的）； 
</li><li>对于fun3：p是一个指针的指针，而d指向d[0]，同时d[0]又是一个指针，所以d就是一个指针的指针。但是b却不是（它是一个2*3的矩阵也就是年int [2][3]类型）； 
</li><li>对于fun6，p是一个2*3的数组类型，和b恰好完全匹配；<br /></li></ul><strong><br />二、函数指针、函数的指针参数以及返回指针的函数</strong><p>A) 函数指针<br />　　C++规定，一个函数的地址就是这个函数的名字。我们需要指出的就是一个指针需要指定类型是为了后来的指针解析时候使用，通过指针有效快速访问对象。那么对于函数的指针，它要表示出该函数的那些特性才能满足解析的唯一性呢？答案就是一个函数的特性有它的参数列表和返回类型。<br /><br />下面是一个函数指针的例子：</p><pre>int (*p)(int I,int j);</pre><p>不能是</p><pre>int *p(int I,int j)，</pre><p>这样就变成了返回指针的函数声明了。</p><p>　　在C++中处于对安全性的考虑，指针和它指向的对象要类型一致，也就说上面的指针所指向的函数的特性要和它一模一样：例如指向int min(int I,int j)；是可以的。但是指向int min(double I ,double j);是不可以。函数指针也和其他的指针一样，在使用的时候很怕发生"悬空"，所以在使用的时候同样要判断有效性，或者在定义的时候就初始化。</p><pre>int (*p)(int I,int j)=min;int (*p)(int I,int j)=&amp;min;int (*p)(int I,int j)=0；</pre><p>B) 函数的指针参数<br />　　函数指针可以作函数的参数：例如我们有一个积分的算法，对于不同的数学函数可以进行积分（我们这里假设函数都是一元的）；<br />那么我们的算法接口可以定义为：</p><pre>template&lt;class T&gt;T integrate( T lower, T upper , T (*)(T)=0 )throw(integrated_exp);</pre><p>　　这里的最后的参数是一个函数的指针，并且被设定缺省值为0。这个函数返回一个值，同时需要一个参数。假如加入我们有这样的一个函数：</p><pre>double line(double x){ return a*x+b;}</pre><p>那么我就可以使用了。<br /><br />函数指针还可以作为返回类型（注意不是函数！！，某个特定的函数是不可以作为返回类型的。）假设：</p><pre>typedef int (*PF)(int );PF getProcessMethod( );//true</pre><p>C) 返回指针的函数<br />　　一个函数的返回是函数的重要接口之一，c++的一个重要的强大的功能就是能够设计足够复杂和好用的用户自定义类型。而同时处理和传递这些类型也是很麻烦的一件事情，我们不想把我们的时间都花在这些对于我们的实际工作没有很实质帮助的拷贝上，解决这个问题就要依赖我们的接口设计：c和c++都提供了相应的解决方案，在c++中我们可是使用引用，讲他们作为函数的实际参数，或者我们在函数的实际参数中使用一个指针等。同样我们还可以使用一个函数返回一个指针：但是这是一个很不好解决的问题！<br />我们首先容易出错的是：将一个局部变量的地址传出来！例如：</p><pre>UserType * Process( ){　　UserType ut(param-list);　　//process ut;　　return &amp;ut;//}</pre><p>　　这个变量在我们的函数结束的时候就被销毁了，尽管地址可以传出去，但是这个地址已经不存在了，已经不能使用的东西，在这个函数之外却不知道，难免要出错！<br />同时我还会有一个比较麻烦的问题：使用new，又容易造成内存泄露</p><pre>UserType * Process ( ){　　UserTpye *put=new UserType(param-list );　　//process put;　　return put;}</pre><p>我们在函数内部使用了一个new，分配了一个空间，这样传出来也是可以！<br />　　就是说不会发生上面的问题了。但是用户通常都会忘记在程序的外面在把这个借来的空间还回去！内存空间就这样泄露了！<br />可能也是这些另人无奈的问题，所以很多程序员把函数的参数设定为指针或者引用，以此来代替这种向外传输吧！总之，使用这种返回指针的函数要小心！</p><p><strong>三、类成员的指针</strong></p><p>　　类成员和一般的外部变量相互比较，不同就是它所在的域不同，这个域很重要，它决定了该变量可以使用的范围。那么一个指针如果要指向类的成员函数或者成员变量，那么除了要表达它的返回类型、参数列表或者类型之外，那么还要说明它所指向的变量（或者函数）的域，为了说明该域我们要使用类域限定：</p><pre>class NJUPT{　　static double money=20000000;　　int num;　　public:　　NJUPT():num(10){};　　int get(){return num;};　　double getMoney(){reuturn money;}}</pre><p>我们定义成员的指针为</p><pre>int NJUPT:: *p;//指向int型成员变量int (NJUPt::*)p()//指向int f()型成员函数。</pre><p>为了使用这些指针，我们需要使用该类型的变量或者指针。</p><pre>NJUPT s,*ps;</pre><p>那么调用的方式为：</p><pre>cout&lt;&lt;s.*p;cout&lt;&lt;(s-&gt;*p)();</pre><p>　　这个看起来似乎很奇怪！但是只要你想到我们定义的指针被限定在了类域中了（我们在开始定义的使用使用了NJUPT:: ），这么使用也是很自然的。<br />　　如果一个类还有一些静态成员变量和静态成员函数，那么我是否也想这样使用呢？<br />答案是不用，静态成员我们就可以象使用外部的普通成员一样定义一个指针或者函数指针来访问就可以了，究其原因主要是这个成员的类型性质决定的。</p><pre>double *p=&amp;NJUPT::money;double (*p)()=&amp;NJUPT::getMoney():　</pre><img src ="http://www.cnitblog.com/zouzheng/aggbug/20699.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-17 18:39 <a href="http://www.cnitblog.com/zouzheng/articles/20699.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>定义const 型指针 </title><link>http://www.cnitblog.com/zouzheng/articles/20667.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 17 Dec 2006 02:55:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20667.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20667.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20667.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20667.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20667.html</trackback:ping><description><![CDATA[有三种方法来定义const 型指针： <br />　　（1）const &lt;类型&gt; *&lt;指针变量名&gt;； <br />　　该语句的作用是：定义指针变量所指数据值为常量，即：指针变量所指数据值不能改变，但指针变量值可以改变。例如： <br />　　float x,y; <br />　　const float *p=&amp;x; //定义指针变量p所指数据值*p为常量 <br />　　*p=25; //错误，p所指变量x数据值不能用*p形式进行改变 <br />　　p=&amp;y; //正确，可改指针变量p的值 <br />　　x=25; //正确，变量x的值可以改变 <br />　　（2）&lt;类型&gt; * const &lt;指针变量名&gt;； <br />　　该语句的作用是：定义指针变量值为常量，即：指针变量值不能改变，但指针变量所指数据值可以改变。例如： <br />　　float x,y; <br />　　float * const p=&amp;x; //定义指针变量p的值为常量 <br />　　*p=25; //正确，p所指变量x数据值可以用*p形式进行改变 <br />　　p=&amp;y; //错误，指针变量p的值不能改变 <br />　　用这种形式定义的指针变量，必须在定义时赋初值。 <br />　　（3）const &lt;类型&gt; * const &lt;指针变量名&gt;； <br />　　该语句的作用是：定义指针变量值为常量，指针变量所指数据值为常量。即：指针变量值不能改变，指针变量所指数据值也不能改变。例如： <br />　　float x,y; <br />　　const float * const p=&amp;x; //定义指针变量p为常量 <br />　　*p=25; ` //错误，p所指变量x数据值不能用*p形式进行改变 <br />　　p=&amp;y; //错误，不能改变指针变量p的值 <br />　　用这种形式定义指针变量，必须在定义时赋初值。 <br />　　注意： <br />　　（1）因为引用变量类同于指针变量，所以这三种定义形式完全适应于引用类型变量。 <br />　　（2）定义const类型指针的目的是提高程序的安全性，用const 可限制程序随意修改指针值。 <br />　　（3）const 指针主要用作函数参数，以限制在函数体不能修改指针变量的值，或不能修改指针变量所指数据值。<img src ="http://www.cnitblog.com/zouzheng/aggbug/20667.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-17 10:55 <a href="http://www.cnitblog.com/zouzheng/articles/20667.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针专题</title><link>http://www.cnitblog.com/zouzheng/articles/20666.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 17 Dec 2006 02:13:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20666.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20666.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20666.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20666.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20666.html</trackback:ping><description><![CDATA[一、<strong>数组的指针、指针数组以及指向指针的指针</strong><p>　　考虑数组的指针的时候我们要同时考虑类型和维数这两个属性。换一句话，就是说一个数组排除在其中存储的数值，那么可以用类型和维数来位置表示他的种类。</p><p>A）一维数组<br />　　在c和c++中数组的指针就是数组的起始地址（也就第一个元素的地址），而且标准文档规定数组名代表数组的地址（这是地址数值层面的数组表示）。例如：</p><pre>int a[10];int *p;</pre><p>p=&amp;a[0]//和p=a是等价的：<br /><br />　　因为a是数组名，所以他是该数组的地址，同时因为第一个元素为a[0]，那么&amp;a[0]也代表了该数组的地址。但是我们是不是就说一个数组名和该数组的第一个元素的&amp;运算是一回事呢？在一维的时候当时是的，但是在高维的时候，我们要考虑到维数给数组带来的影响。<br />　　a[10]是一个数组，a是数组名，它是一个包含10个int类型的数组类型，不是一般的指针变量噢！（虽然标准文档规定在c++中从int[]到int*直接转换是可以的，在使用的时候似乎在函数的参数为指针的时候，我们将该数组名赋值没有任何异样），a代表数组的首地址，在数字层面和a[10]的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。<br />所以我们要注意以下问题：</p><p>（1） p[i]和a[i]都是代表该数组的第i+1个元素；<br />（2） p+i和a+i代表了第i+1个元素的地址，所以我们也可以使用　*（p+I）和*（a+I）来引用对象元素；<br />（3）p+1不是对于指针数量上加一，而是表示从当前的位置跳过当前指针指向类型长度的空间，对于win32的int为4byte；</p><p>B)多维数组<br />　　对于二维数组a[4][6];由于数组名代表数组的起始地址，所以a（第一层）和第一个元素a[0][0]地址的数字是相同的，但是意义却是不同的。对于该数组我们可以理解为：a的一维数组（第一层），它有四个元素a[0]、a[1]、a[2]、a[3]（第二层），而每个元素又含有6个元素a[0][0],a[0][1],a[0][2],a[0][3],a[0][4]，a[0][5]（第三层）,…到此我们终于访问到了每个元素了，这个过程我们经历了：a-&gt;a[0]-&gt;a[0][0]；<br />　　整体来讲：a是一个4行5列的二维数组，a表示它指向的数组的首地址（第一个元素地址&amp;a[0]）,同时a[0]指向一行，它是这个行的名字（和该行的第一个元素的首地址相同（第一个元素为地址&amp;a[0][0]））。所以从数字角度说：a、a[0]、&amp;a[0][0]是相同的，但是他们所处的层次是不同的。<br />　　既然a代表二维数组，那么a+i就表示它的第i+1个元素*（a+i）的地址，而在二维数组中<br />*（a+i）又指向一个数组，*（a+i）+j表示这个数组的第j+1个元素的地址，所以要访问这个元素可以使用 *（*（a+i）+j）（也就是a[i][j]）。<br />他们的示意图为（虚线代表不是实际存在的）：</p><p><img height="199" alt="" src="http://www.vckbase.com/document/journal/vckbase48/images/pntfigimg1.gif" width="291" border="0" /></p><p>对照这个图，如下的一些说法都是正确的（对于a[4][6]）：</p><ul><li>a是一个数组类型，*a指向一个数组； 
</li><li>a+i指向一个数组； 
</li><li>a、*a和&amp;a[0][0]数值相同； 
</li><li>a[i]+j和*（a+i）+j是同一个概念； </li></ul><p>　　总结一下就是：我们对于二维指针a，他指向数组a[0，1，2，3]，使用*，可以使他降级到第二层次，这样*a就指向了第一个真正的数组。对于其他的情况我们也可以采用相同的方式，对于其他维数和类型的数组我们可以采用相类似的思想。<br /><br />说到指向数组的指针，我们还可以声明一个指针变量让它指向一个数组。例如：</p><pre>int （*p）[5]；</pre><p>这时p就是一个指针，要指向一个含有5个int类型元素的数组，指向其他的就会出现问题。<br />这个时候我们可以使用上面的什么东西来初始化呢？<br />我们可以使用*a,*(a+1)，a[2]等。<br />原因很简单：我们在一个二维的数组中，那么表达方式有上面的相互类似的意义呢？只有　*a,*(a+1)，a[2]等，</p><p>C)指针数组<br />　　一个指针数组是指一个数组中的每个元素都是一个指针，例如：</p><pre>int *p[10];//而不能是int (*p)[10]</pre><p>或者</p><pre>char *p[10];</pre><p>此时p是一个指针（数值上和&amp;p[0]一样）；<br />在前面有int t[10]；</p><pre>int * pt=t;//使用pt指向t</pre><p>那么这里我们用什么指向int *t[10]中的t呢？我们要使用一个指针的指针：</p><pre>int **pt=t;</pre><p>　　这是因为：在int *t[10]中，每个元素是指针，那么同时t又指向这个数组，数组上和&amp;t[0]相同，也就是指向t[0]，指向一个指针变量，可以说是一个指针的指针了，所以自然要用</p><pre>int **pt;</pre><p><br />D)指针的指针<br />　　一个指针变量内部可以存储一个值，这个值是另外一个对象的地址，所以我们说一个指针变量可以指向一个普通变量，同样这个指针变量也有一个地址，也就是说有一个东西可以指向这个指针变量，然后再通过这个指针变量指向这个对象。那么如何来指向这个指针变量呢？由于指针变量本身已经是一个指针了（右值），那么我们这里就不能用一般的指针了，需要在指针上体现出来这些特点，我们需要定义指针的指针（二重指针）。</p><pre>int *p1=&amp;i;int**p2=&amp;p1；</pre><p>综合以上的所有点，下面是我们常常看到一些匹配（也是经常出错的地方）：</p><pre>int a[3],b[2][3],c,*d[3];void fun1(int *p)；void fun2(int (*p)[3]);void fun3(int **p);void fun4(int p[3]); void fun5(int p[]);void fun6(int p[2][3]);void fun7(int (&amp;p)[3]);</pre><p>函数 不会产生编译时刻的可能值（但逻辑上不一定都对）</p><table class="MsoNormalTable" id="table1" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 5.4pt; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" cellspacing="0" cellpadding="0" width="389" border="1"><tbody><tr style="HEIGHT: 15pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 15pt" valign="top"><p class="MsoNormal"><span style="FONT-FAMILY: 宋体">函数</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 15pt" valign="top"><p class="MsoNormal"><span style="FONT-FAMILY: 宋体">不会产生编译时刻的可能值（但逻辑上不一定都对）</span></p></td></tr><tr style="HEIGHT: 6.75pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 6.75pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun1</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 6.75pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">a, &amp;a[i], *b ,b[i],&amp;b[i][j] ,&amp;c ,d[i]</span></p></td></tr><tr style="HEIGHT: 14.25pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 14.25pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun2</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 14.25pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">b</span><span style="FONT-FAMILY: 宋体">，<span lang="EN-US">b+i</span>，</span></p></td></tr><tr style="HEIGHT: 14.25pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 14.25pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun3</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 14.25pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">d</span></p></td></tr><tr style="HEIGHT: 13.5pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.5pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun4</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.5pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">a, &amp;a[i], *b ,b[i],&amp;b[i][j] ,&amp;c ,d[i]</span></p></td></tr><tr style="HEIGHT: 17.25pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 17.25pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun5</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 17.25pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">a, &amp;a[i], *b ,b[i],&amp;b[i][j] ,&amp;c ,d[i]</span></p></td></tr><tr style="HEIGHT: 10.5pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 10.5pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun6</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 10.5pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">  b</span></p></td></tr><tr style="HEIGHT: 16.95pt"><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: windowtext 1pt solid; WIDTH: 37px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 16.95pt" valign="top"><p class="MsoNormal"><span lang="EN-US" style="FONT-FAMILY: 宋体">fun7</span></p></td><td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; WIDTH: 321px; PADDING-TOP: 0cm; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 16.95pt" valign="top"><p class="MsoNormal" style="TEXT-INDENT: 10.5pt"><span lang="EN-US" style="FONT-FAMILY: 宋体">a</span></p></td></tr></tbody></table><p>为什么可以有这样的搭配，原因如下：</p><ul><li>对于fun1 fun4 fun 5: 在编译器看来fun1,fun4,fun5的声明是一样，在编译时候，编译器把数组的大小舍去不考虑，只考虑它是一个指针，也就是说有没有大小说明是一样的，所以三者的形式都是fun1的形式（其实只要提供了int*指针就可以了）； 
</li><li>对于fun7 ：以上的解释对于引用是不适用的，如果变量被声明为数组的引用，那么编译器就要考虑数组的大小了，那么必须和声明一模一样（所以fun7就只有a合适）； 
</li><li>对于fun2：p是一个指向一个含有3个元素的数组，这样b和b+i正好合适，而a却不是（它是指向a[0]的，不是指向这个数组的）； 
</li><li>对于fun3：p是一个指针的指针，而d指向d[0]，同时d[0]又是一个指针，所以d就是一个指针的指针。但是b却不是（它是一个2*3的矩阵也就是年int [2][3]类型）； 
</li><li>对于fun6，p是一个2*3的数组类型，和b恰好完全匹配；<br /></li></ul><strong><br />二、函数指针、函数的指针参数以及返回指针的函数</strong><p>A) 函数指针<br />　　C++规定，一个函数的地址就是这个函数的名字。我们需要指出的就是一个指针需要指定类型是为了后来的指针解析时候使用，通过指针有效快速访问对象。那么对于函数的指针，它要表示出该函数的那些特性才能满足解析的唯一性呢？答案就是一个函数的特性有它的参数列表和返回类型。<br /><br />下面是一个函数指针的例子：</p><pre>int (*p)(int I,int j);</pre><p>不能是</p><pre>int *p(int I,int j)，</pre><p>这样就变成了返回指针的函数声明了。</p><p>　　在C++中处于对安全性的考虑，指针和它指向的对象要类型一致，也就说上面的指针所指向的函数的特性要和它一模一样：例如指向int min(int I,int j)；是可以的。但是指向int min(double I ,double j);是不可以。函数指针也和其他的指针一样，在使用的时候很怕发生"悬空"，所以在使用的时候同样要判断有效性，或者在定义的时候就初始化。</p><pre>int (*p)(int I,int j)=min;int (*p)(int I,int j)=&amp;min;int (*p)(int I,int j)=0；</pre><p>B) 函数的指针参数<br />　　函数指针可以作函数的参数：例如我们有一个积分的算法，对于不同的数学函数可以进行积分（我们这里假设函数都是一元的）；<br />那么我们的算法接口可以定义为：</p><pre>template&lt;class T&gt;T integrate( T lower, T upper , T (*)(T)=0 )throw(integrated_exp);</pre><p>　　这里的最后的参数是一个函数的指针，并且被设定缺省值为0。这个函数返回一个值，同时需要一个参数。假如加入我们有这样的一个函数：</p><pre>double line(double x){ return a*x+b;}</pre><p>那么我就可以使用了。<br /><br />函数指针还可以作为返回类型（注意不是函数！！，某个特定的函数是不可以作为返回类型的。）假设：</p><pre>typedef int (*PF)(int );PF getProcessMethod( );//true</pre><p>C) 返回指针的函数<br />　　一个函数的返回是函数的重要接口之一，c++的一个重要的强大的功能就是能够设计足够复杂和好用的用户自定义类型。而同时处理和传递这些类型也是很麻烦的一件事情，我们不想把我们的时间都花在这些对于我们的实际工作没有很实质帮助的拷贝上，解决这个问题就要依赖我们的接口设计：c和c++都提供了相应的解决方案，在c++中我们可是使用引用，讲他们作为函数的实际参数，或者我们在函数的实际参数中使用一个指针等。同样我们还可以使用一个函数返回一个指针：但是这是一个很不好解决的问题！<br />我们首先容易出错的是：将一个局部变量的地址传出来！例如：</p><pre>UserType * Process( ){　　UserType ut(param-list);　　//process ut;　　return &amp;ut;//}</pre><p>　　这个变量在我们的函数结束的时候就被销毁了，尽管地址可以传出去，但是这个地址已经不存在了，已经不能使用的东西，在这个函数之外却不知道，难免要出错！<br />同时我还会有一个比较麻烦的问题：使用new，又容易造成内存泄露</p><pre>UserType * Process ( ){　　UserTpye *put=new UserType(param-list );　　//process put;　　return put;}</pre><p>我们在函数内部使用了一个new，分配了一个空间，这样传出来也是可以！<br />　　就是说不会发生上面的问题了。但是用户通常都会忘记在程序的外面在把这个借来的空间还回去！内存空间就这样泄露了！<br />可能也是这些另人无奈的问题，所以很多程序员把函数的参数设定为指针或者引用，以此来代替这种向外传输吧！总之，使用这种返回指针的函数要小心！</p><p><strong>三、类成员的指针</strong></p><p>　　类成员和一般的外部变量相互比较，不同就是它所在的域不同，这个域很重要，它决定了该变量可以使用的范围。那么一个指针如果要指向类的成员函数或者成员变量，那么除了要表达它的返回类型、参数列表或者类型之外，那么还要说明它所指向的变量（或者函数）的域，为了说明该域我们要使用类域限定：</p><pre>class NJUPT{　　static double money=20000000;　　int num;　　public:　　NJUPT():num(10){};　　int get(){return num;};　　double getMoney(){reuturn money;}}</pre><p>我们定义成员的指针为</p><pre>int NJUPT:: *p;//指向int型成员变量int (NJUPt::*)p()//指向int f()型成员函数。</pre><p>为了使用这些指针，我们需要使用该类型的变量或者指针。</p><pre>NJUPT s,*ps;</pre><p>那么调用的方式为：</p><pre>cout&lt;&lt;s.*p;cout&lt;&lt;(s-&gt;*p)();</pre><p>　　这个看起来似乎很奇怪！但是只要你想到我们定义的指针被限定在了类域中了（我们在开始定义的使用使用了NJUPT:: ），这么使用也是很自然的。<br />　　如果一个类还有一些静态成员变量和静态成员函数，那么我是否也想这样使用呢？<br />答案是不用，静态成员我们就可以象使用外部的普通成员一样定义一个指针或者函数指针来访问就可以了，究其原因主要是这个成员的类型性质决定的。</p><pre>double *p=&amp;NJUPT::money;double (*p)()=&amp;NJUPT::getMoney():　</pre><img src ="http://www.cnitblog.com/zouzheng/aggbug/20666.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-17 10:13 <a href="http://www.cnitblog.com/zouzheng/articles/20666.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针（下） </title><link>http://www.cnitblog.com/zouzheng/articles/20665.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 17 Dec 2006 01:56:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20665.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20665.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20665.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20665.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20665.html</trackback:ping><description><![CDATA[前面我们已经知道了指针是用来存储变量地址的变量。我们也可以定义另外的一个指针，用来存储某一个指针的地址，也就是指针的指针。定义指针的指针同定义单纯指针的方法很类似，只要在原有的星号前再多加上一个星号就可以,比如int **pointer.在这里首先我们要搞清楚一个关系：变量中存储的是某一个数据，指针当中存储的是变量的地址，而指针的指针存储的是指针的地址，当有如下定义时：<br />                       int var=2;<br />                       int *pointer;<br />                       int **ptr_to_ptr;<br />那么：<br />pointer=&amp;var,  *pointer=var=2,  *ptr_to_ptr=&amp;pointer,  **ptr_to_ptr=pointer=&amp;var;<br /><br />每学习一样新内容的时候，我们都要问问这项新技能可以帮助我们做些什么，指针的指针也不例外。这便引出了另外一个概念，也就是指针数组。从字面意思看，指针数组就是存储了一组地址连续的指针的数组。那么指针数组又是做什么的？有些时候我们需要处理比较大量的字符串，当对字符串进行处理的时候，计算机是很难识别每个字符串之间的相互关系的，假如程序存在大量字符串的移动，那将会更大程度的造成混乱。所以我们把每一句字符串的第一个字母的地址都存放在一个指针里，当系统对字符串进行调用或者排序的时候，只要移动相应的指针就可以，不需要执行大规模的字符串，这很大程度上提高了程序的效率。而每个存储字符串首地址的指针又都存储在一段相互连续的内存地址中，也就组成了一个指针数组，指向这个数组首地址的指针，便是指针的指针。<br /><br />在程序中对于数组的调用是一件棘手的问题，指针的使用则解决了这个难题，我们可以提前定义一个指向数组首地址的指针，因为数组元素是连续存放的，所以找到首地址有助于进一步找到其他元素。当数组被调用时，只需调用指针而不需要调用整个数组，事实上，对于函数来说，也不具备调用整个数组的功能，这就使得指针在数组同程序和函数之间起到了一个桥梁作用，而且即使在一些可以直接调用或更改数组出现顺序的环境下，单纯的调用指针也要比调用或移动整个数组容易得多，这在相当程度上提高了程序的效率。<br /><br />对于一个数组a[]，我们知道a可以表示指向该数组的指针，我们同样也可以用a[]来表示指向二维数组a[][]的指针，于是我们得出以下结论：<br />——数组名后带n对方括号时，为数组的数据；<br />——数组名后带的方括号少于n对时，为一个指向数组元素的指针。<br />这并不难理解，不过还是给出一个简单的例子来说明一下：<br />#include &lt;stdio.h&gt;<br />int multi[2][4]<br />int main(void)<br />{ <br />    printf(”\nmulti = %u”,multi);<br />    printf(”\nmulti = %u”,multi[0]);<br />    printf(”\nmulti = %u”,&amp;multi[0][0]);<br />  }<br />这个例子很好的说明了多维数组间的关系，当然因为硬件环境的不同，每个人的结果都会不尽相同，但输出的三个值都是一样的。<br />n维数组的元素是n-1维数组，当n为1时，则元素为数组声明的开头指定的类型的变量。但有一点，用数组名虽然可以表示指向数组的指针，但由于数组名是指针常量，无法修改，所以我们要声明一个真正的指针来指向数组的首地址，假如有二维数组  <br />                              int multi[2][4]；<br />要声明一个名为ptr，且能够指向multi数组的元素（即能够指向包含4个int变量的数组）的指针，可以这样做：<br />                              int(*ptr)[4];<br />之所以给*ptr加上圆括号，因为方括号优先级高于星号，如果这样编写代码<br />                              int*ptr[4]；<br />则声明的是一个数组，该数组包含4个int指针。<br /> <br />最后我们看看指向函数的指针。<img src ="http://www.cnitblog.com/zouzheng/aggbug/20665.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-17 09:56 <a href="http://www.cnitblog.com/zouzheng/articles/20665.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>水滴石穿C语言之指针、数组和函数</title><link>http://www.cnitblog.com/zouzheng/articles/20664.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 17 Dec 2006 01:45:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20664.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20664.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20664.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20664.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20664.html</trackback:ping><description><![CDATA[
		<strong>基本解释<br /><br /></strong>　　1、指针的本质是一个与地址相关的复合类型，它的值是数据存放的位置（地址）；数组的本质则是一系列的变量。<br /><br />　　2、数组名对应着（而不是指向）一块内存，其地址与容量在生命期内保持不变，只有数组的内容可以改变。指针可以随时指向任意类型的内存块，它的特征是“可变”，所以我们常用指针来操作动态内存。<br /><br />　　3、当数组作为函数的参数进行传递时，该数组自动退化为同类型的指针。 <br /><br />　　<strong>问题：指针与数组</strong><br /><br />　　听说char a[]与char *a是一致的，是不是这样呢？<br /><br />　　答案与分析：<br /><br />　　指针和数组存在着一些本质的区别。当然，在某种情况下，比如数组作为函数的参数进行传递时，由于该数组自动退化为同类型的指针，所以在函数内部，作为函数参数传递进来的指针与数组确实具有一定的一致性，但这只是一种比较特殊的情况而已，在本质上，两者是有区别的。请看以下的例子：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>char a[] = "Hi, pig!";<br />char *p = "Hi, pig!";</td></tr></tbody></table><br />　　上述两个变量的内存布局分别如下：<br /><br />　　数组a需要在内存中占用8个字节的空间，这段内存区通过名字a来标志。指针p则需要4个字节的空间来存放地址，这4个字节用名字p来标志。其中存放的地址几乎可以指向任何地方，也可以哪里都不指，即空指针。目前这个p指向某地连续的8个字节，即字符串“Hi, pig!”。<br /><br />　　另外，例如：对于a[2]和p[2]，二者都返回字符‘i’，但是编译器产生的执行代码却不一样。对于a[2]，执行代码是从a的位置开始，向后移动2两个字节，然后取出其中的字符。对于p[2]，执行代码是从p的位置取出一个地址，在其上加2，然后取出对应内存中的字符。<br /><br />　　<strong>问题：数组指针</strong><br /><br />　　为什么在有些时候我们需要定义指向数组而不是指向数组元素的指针？如何定义？<br /><br />　　答案与分析：<br /><br />　　使用指针，目的是用来保存某个元素的地址，从而来利用指针独有的优点，那么在元素需要是数组的情况下，就理所当然要用到指向数组的指针，比如在高维需要动态生成情况下的多维数组。<br /><br />　　定义例子如下： int (*pElement)[2]。<br /><br />　　下面是一个例子： <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>int array[2][3] = {{1，2，3}，{4，5，6}};<br />int (*pa)[3]; //定义一个指向数组的指针 <br />pa = &amp;array[0]; // '&amp;'符号能够体现pa的含义，表示是指向数组的指针<br />printf ("%d", (*pa)[0]）; //将打印array[0][0]，即1 <br />pa++； // 猜一猜，它指向谁？array[1]？对了！<br />printf ("%d", (*pa)[0]); // 将打印array[1][0]，即4</td></tr></tbody></table><br />　　上述这个例子充分说明了数组指针—一种指向整个数组的指针的定义和使用。<br /><br />　　需要说明的是，按照我们在第四篇讨论过的，指针的步进是参照其所指对象的大小的，因此，pa++将整个向后移动一个数组的尺寸，而不是仅仅向后移动一个数组元素的尺寸。<br /><br />　　<strong>问题：指针数组</strong><br /><br />　　有如下定义：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>struct UT_TEST_STRUCT *pTo[2][MAX_NUM];</td></tr></tbody></table><br />　　请分析这个定义的意义，并尝试说明这样的定义可能有哪些好处？<br /><br />　　答案与分析：<br /><br />　　前面我们谈了数组指针，现在又提到了指针数组，两者形式很相似，那么，如何区分两者的定义呢？分析如下：<br /><br />　　数组指针是：指向数组的指针，比如 int (*pA)[5]。<br /><br />　　指针数组是：指针构成的数组，比如int *pA[5]。<br /><br />　　至于上述指针数组的好处，大致有如下两个很普遍的原因：<br /><br />　　a)、各个指针内容可以按需要动态生成，避免了空间浪费。<br /><br />　　b)、各个指针呈数组形式排列，索引起来非常方便。<br /><br />　　在实际编程中，选择使用指针数组大多都是想要获得如上两个好处。<br /><span class="f14">　　<strong>问题：指向指针的指针</strong><br /><br />　　在做一个文本处理程序的时候，有这样一个问题：什么样的数据结构适合于按行存储文本？<br /><br />　　答案与分析：<br /><br />　　首先，我们来分析文本的特点，文本的主要特征是具有很强的动态性，一行文本的字符个数或多或少不确定，整个文本所拥有的文本行数也是不确定的。这样的特征决定了用固定的二维数组存放文本行必然限制多多，缺乏灵活性。这种场合，使用指向指针的指针有很大的优越性。<br /><br />　　现实中我们尝试用动态二维数组（本质就是指向指针的指针）来解决此问题：<br /><br />　　图示是一个指针数组。所谓动态性指横向（对应每行文本的字符个数）和纵向（对应整个文本的行数）两个方向都可以变化。<br /><br />　　就横向而言，因为指针的灵活性，它可以指向随意大小的字符数组，实现了横向动态性。<br /><br />　　就竖向而言，可以动态生成及扩展需要的指针数组的大小。<br /><br />　　下面的代码演示了这种动态数组的用途：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>// 用于从文件中读取以 '\0'结尾的字符串的函数<br />extern char *getline(FILE *pFile);<br />FILE *pFile;<br />char **ppText = NULL; // 二维动态数组指针 <br />char *pCurrText = NULL； // 指向当前输入字符串的指针<br />ULONG ulCurrLines = 0；<br />ULONG ulAllocedLines = 0；<br /><br />while （p = getline(pFile))<br />{<br />　if (ulCurrLines &gt;= ulAllocedLines)<br />　{<br />　　// * 当前竖向空间已经不够了，通过realloc对其进行扩展。<br />　　ulAllocedLines += 50; // 每次扩展50行。 <br />　　ppText = realloc (ppText, ulAllocedLines * (char *));<br />　　if (NULL == ppText)<br />　　{<br />　　　return; // 内存分配失败，返回 <br />　　}<br />　}<br />　ppText[ulCurrLines++] = p; // 横向“扩展”，指向不定长字符串 <br />}</td></tr></tbody></table><br />　　<strong>问题：指针数组与数组指针与指向指针的指针</strong><br /><br />　　指针和数组分别有如下的特征：<br /><br />　　指针：动态分配，初始空间小<br /><br />　　数组：索引方便，初始空间大<br /><br />　　下面使用高维数组来说明指针数组、数组指针、指向指针的指针各自的适合场合。<br /><br />　　 多维静态数组:各维均确定，适用于整体空间需求不大的场合，此结构可方便索引，例a[10][40]。<br /><br />　　 数组指针：低维确定，高维需要动态生成的场合，例a[x][40]。<br /><br />　　 指针数组：高维确定，低维需要动态生成的场合，例a[10][y]。<br /><br />　　 指向指针的指针：高、低维均需要动态生成的场合，例a[x][y]。<br /><br />　　<strong>问题：数组名相关问题</strong><br /><br />　　假设有一个整数数组a，a和&amp;a的区别是什么？<br /><br />　　答案与分析：<br /><br />　　a == &amp;a == &amp;a[0],数组名a不占用存储空间。需要引用数组(非字符串)首地址的地方，我一般使用&amp;a[0]，使用a容易和指针混淆，使用&amp;a容易和非指针变量混淆。<br /><br />　　区别在于二者的类型。对数组a的直接引用将产生一个指向数组第一个元素的指针，而&amp;a的结果则产生一个指向全部数组的指针。例如：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>int a[2] = {1, 2};<br />int *p = 0;<br />p = a; /* p指向a[0]所在的地方 */<br />x = *p; /* x = a[0] = 1*/<br />p = &amp;a; /* 编译器会提示你错误，*/<br />/*显示整数指针与整数数组指针不一样 */</td></tr></tbody></table><br />　　<strong>问题：函数指针与指针函数</strong><br />　<br />　　请问：如下定义是什么意思：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>int *pF1();<br />int (*pF2)(); </td></tr></tbody></table><br />　　答案与分析：<br /><br />　　首先清楚它们的定义：<br /><br />　　 指针函数，返回一个指针的函数。<br /><br />　　 函数指针，指向一个函数的指针。<br /><br />　　可知：<br /><br />　　 pF1是一个指针函数，它返回一个指向int型数据的指针。<br /><br />　　 pF2是一个函数指针，它指向一个参数为空的函数，这个函数返回一个整数。<br /></span><img src ="http://www.cnitblog.com/zouzheng/aggbug/20664.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-17 09:45 <a href="http://www.cnitblog.com/zouzheng/articles/20664.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多维指针</title><link>http://www.cnitblog.com/zouzheng/articles/20662.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 17 Dec 2006 01:42:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20662.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20662.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20662.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20662.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20662.html</trackback:ping><description><![CDATA[
		<p>有人问我二三维指针的事，讲了一大堆，总结了以下代码：</p>
		<p>void TestPointer()<br />{<br />        // 优先级 [] 高于 *，()高于[]<br />        int i,j,k;<br />        // 指针和数组<br />        // 看是指针还是数组要看他的名字(不带[]也不带*)能否直接对其进行内存分配<br />        // 因为数组是确定的分配好内存地址的,而指针则是用于指向地址的<br />        // 所以为指针分配内存或赋值时,应该理解为指向数组地址或其他地址</p>
		<p>        // 读法：由低优先级向高优先级读<br />        // *Array0[10]:指针——数组<br />        // int **Array1[2]：指针——指针——数组（二维指针数组）<br />        // *(*pPointer4)[2]：指针——数组——指针(指向指针数组的指针)<br />        // (*pPointer3)[30][20]：数组——数组——指针（指向二维数组的指针）</p>
		<p>        // 1、数组和指针数组  <br />        int Array[10];    // Array是数组，有10个元素,元素的类型是int型<br />        int *Array0[10];    // Array0是指针数组，有10个元素,元素的类型是int型指针<br />        for( i=0; i&lt;10; i++ )<br />        {<br />                Array0 = new int[i+1];    // Array0是int型的指针<br />                delete[] Array0;<br />        }<br />        int **Array1[2];    // 一个指向指针的指针的数组(二维指针数组)，拥有两个元素<br />        // 每一个元素都是int ** 型(指向指针的指针) ,但 Array1 终究也是个数组，<br />        Array1[0]=new int *[10];    // int *[10]为指针数组，见下面一条<br />        Array1[1]=new int *[10];<br />        delete [] Array1[0];<br />        delete [] Array1[1];</p>
		<p>        // 2、指向数组的指针<br />        int *pPointer0=new int[10];    // pPointer0是指针,赋值号右边但申请的是一个数组,只不过用指针指向它</p>
		<p>        // 指向“指针数组”的“指向指针的指针”<br />        // 定义一个数组,有10个元素,元素的类型是int*<br />        // 用指向指针的指针指向该指针数组, pPointer1[]表示数组中的每个元素(int *)<br />        // 为指针数组中每个元素申请空间，并为申请到的空间中的元素赋值<br />        int **pPointer1=new int*[10];<br />        for( i=0; i&lt;10; i++ )<br />        {<br />                // *(pPointer1+i) = new int[i+1]; 也可<br />                pPointer1 = new int[i+1];<br />                for( j=0; j&lt;i+1; j++ )<br />                {<br />                        // *(*(pPointer1+i)+j) = 1;<br />                        pPointer1[j] = 1;<br />                }<br />                delete[] pPointer1;<br />        }<br />        delete [] pPointer1;</p>
		<p>        // 指向二维数组的指针，<br />        // 该二维数组最低维数的元素数量为8个,类型为int<br />        int (*pPointer2)[8];<br />        // 理解 (*pPointer2)[8]:<br />        // (*pPointer2)说明它是一个指针，<br />        // (*pPointer2)[8]说明p2一次跳过8个元素，<br />        //         即pPointer2指向一个二维数组，且最低维为8，该数组元素不是指针<br />        pPointer2= new int[10][8];<br />        for( i=0; i&lt;10; i++ )<br />        {<br />                for( j=0; j&lt;8; j++ )<br />                {<br />                        pPointer2[j] = 20;<br />                }<br />        }<br />        delete []pPointer2;  // 删除（释放）二维数组</p>
		<p>
				<br />        // 指向三维数组的指针，该三维数组低两维的元素数量为[30]，[20],类型为int<br />        int (*pPointer3)[30][20];    // 这个数组低两维必须是确定的 <br />        pPointer3=new int[1][30][20];<br />        delete []pPointer3;  //删除（释放）三维数组</p>
		<p>
				<br />        // 指向二维指针数组的指针<br />        // 声明一个3*2的二维数组，元素是指针，<br />        // 用一个指针的指针pPointer4指向该数组首地址<br />        // pPointer4[j]是该二维数组的每个元素，（i&lt;=3 j&lt;=2）<br />        int *(*pPointer4)[2] = new int *[3][2];<br />        // 对*(*pPointer4)[2]的理解：<br />        // (*pPointer4)说明pPointer4是指针，<br />        // (*pPointer4)[2]说明这个指针写成pPointer4时,一次跳两个元素，即pPointer4指向的数组最低维=2<br />        // *(*pPointer4)[2]说明每个元素都是指针<br />        for( i=0; i&lt;3; i++ )<br />        {<br />                for( j=0; j&lt;2; j++ )<br />                {<br />                        pPointer4[j] = new int [5];<br />                        for( k=0; k&lt;5; k++ )<br />                        {<br />                                pPointer4[j][k] = 5;<br />                        }<br />                        delete[] pPointer4[j];<br />                }<br />        }<br />        delete[] pPointer4;<br />}</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/20662.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-17 09:42 <a href="http://www.cnitblog.com/zouzheng/articles/20662.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>论指针 </title><link>http://www.cnitblog.com/zouzheng/articles/20663.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sun, 17 Dec 2006 01:42:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20663.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20663.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20663.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20663.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20663.html</trackback:ping><description><![CDATA[
		<p>指针是一种数据类型,它指向另外一个对象的地址,其内容就是一块地址,但是他本身也有地址,二者不同.</p>
		<p>C++/C 程序中，指针和数组在不少地方可以相互替换着用，让人产生一种错觉，以两者是等价的。数组要么在静态存储区被创建（如全局数组），要么在栈上被创建。数组名对应着（而不是指向）一块内存，其地址与容量在生命期内保持不变，只有数组的内容可以改变。<br />指针可以随时指向任意类型的内存块，它的特征是“可变”，所以我们常用指针来操作动态内存。指针远比数组灵活，但也更危险。</p>
		<p>
		</p>
		<p>1:指向指针的指针.</p>
		<p>看看这个例子:</p>
		<p>char ** p =NULL;</p>
		<p>char * a = "hello";</p>
		<p>以下代码有什么问题:</p>
		<p>
				<font color="#ff0000">*p = a;</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff">说明: 对于char **p = NULL; p确实被已经定义了,但是,被p指向的指针*p并没有被定义,所以*p需要被定义才可以使用.</font>
		</p>
		<p>char **p=NULL;<br />char *a = "hello";</p>
		<p>p = new char*[1];//当然[]内可以填写任意大于0的整数.<br />*p=a;<br />cout&lt;&lt;*p&lt;&lt;endl;</p>
		<p>delete []p; <font color="#339966">//modify 2005.06.02.侯说,只要是new的时候有 [],delete的时候也应该有[]</font><br />p = NULL;</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/20663.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-17 09:42 <a href="http://www.cnitblog.com/zouzheng/articles/20663.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>strings(字符串)详解(一) </title><link>http://www.cnitblog.com/zouzheng/articles/20601.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 16 Dec 2006 03:26:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20601.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20601.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20601.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20601.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20601.html</trackback:ping><description><![CDATA[
		<p>
				<span id="txtObj_0">之所以抛弃char*的字符串而选用C++标准程序库中的string类，是因为他和前者比较起来，不必担心内存是否足够、字符串长度等等，而且作为一个类出现，他集成的操作函数足以完成我们大多数情况下(甚至是100%)的需要。我们可以用 = 进行赋值操作，== 进行比较，+ 做串联（是不是很简单?）。我们尽可以把它看成是C++的基本数据类型。<br />    好了，进入正题………<br />首先，为了在我们的程序中使用string类型，我们必须包含头文件 &lt;string&gt;。如下：<br />    #include &lt;string&gt; //注意这里不是string.h string.h是C字符串头文件<br /><br />1．声明一个C++字符串<br />声明一个字符串变量很简单：<br />    string Str;<br />这样我们就声明了一个字符串变量，但既然是一个类，就有构造函数和析构函数。上面的声明没有传入参数，所以就直接使用了string的默认的构造函数，这个函数所作的就是把Str初始化为一个空字符串。String类的构造函数和析构函数如下：<br />a)    string s;  //生成一个空字符串s<br />b)    string s(str) //拷贝构造函数 生成str的复制品<br />c)    string s(str,stridx) //将字符串str内“始于位置stridx”的部分当作字符串的初值<br />d)    string s(str,stridx,strlen) //将字符串str内“始于stridx且长度顶多strlen”的部分作为字符串的初值<br />e)    string s(cstr) //将C字符串作为s的初值<br />f)    string s(chars,chars_len) //将C字符串前chars_len个字符作为字符串s的初值。<br />g)    string s(num,c) //生成一个字符串，包含num个c字符<br />h)    string s(beg,end) //以区间beg;end(不包含end)内的字符作为字符串s的初值<br />i)    s.~string() //销毁所有字符，释放内存<br />都很简单，我就不解释了。<br />2．字符串操作函数<br />    这里是C++字符串的重点，我先把各种操作函数罗列出来，不喜欢把所有函数都看完的人可以在这里找自己喜欢的函数，再到后面看他的详细解释。<br />a) =,assign()   //赋以新值<br />b) swap()   //交换两个字符串的内容<br />c) +=,append(),push_back() //在尾部添加字符<br />d) insert() //插入字符<br />e) erase() //删除字符<br />f) clear() //删除全部字符 <br />g) replace() //替换字符<br />h) + //串联字符串<br />i) ==,!=,&lt;,&lt;=,&gt;,&gt;=,compare()  //比较字符串<br />j) size(),length()  //返回字符数量<br />k) max_size() //返回字符的可能最大个数<br />l) empty()  //判断字符串是否为空<br />m) capacity() //返回重新分配之前的字符容量<br />n) reserve() //保留一定量内存以容纳一定数量的字符<br />o) [ ], at() //存取单一字符<br />p) &gt;&gt;,getline() //从stream读取某值<br />q) &lt;&lt;  //将谋值写入stream<br />r) copy() //将某值赋值为一个C_string<br />s) c_str() //将内容以C_string返回<br />t) data() //将内容以字符数组形式返回<br />u) substr() //返回某个子字符串<br />v)查找函数<br />w)begin() end() //提供类似STL的迭代器支持<br />x) rbegin() rend() //逆向迭代器<br />y) get_allocator() //返回配置器<br />下面详细介绍：<br />2．1 C++字符串和C字符串的转换<br />    C++提供的由C++字符串得到对应的C_string的方法是使用data()、c_str()和copy()，其中，data()以字符数组的形式返回字符串内容，但并不添加’\0’。c_str()返回一个以‘\0’结尾的字符数组，而copy()则把字符串的内容复制或写入既有的c_string或字符数组内。C++字符串并不以’\0’结尾。我的建议是在程序中能使用C++字符串就使用，除非万不得已不选用c_string。由于只是简单介绍，详细介绍掠过，谁想进一步了解使用中的注意事项可以给我留言(到我的收件箱)。我详细解释。<br />2．2 大小和容量函数<br />    一个C++字符串存在三种大小：a)现有的字符数，函数是size()和length()，他们等效。Empty()用来检查字符串是否为空。b)max_size() 这个大小是指当前C++字符串最多能包含的字符数，很可能和机器本身的限制或者字符串所在位置连续内存的大小有关系。我们一般情况下不用关心他，应该大小足够我们用的。但是不够用的话，会抛出length_error异常c)capacity()重新分配内存之前 string所能包含的最大字符数。这里另一个需要指出的是reserve()函数，这个函数为string重新分配内存。重新分配的大小由其参数决定，默认参数为0，这时候会对string进行非强制性缩减。</span>
		</p>
		<p>
				<span>还有必要再重复一下C++字符串和C字符串转换的问题，许多人会遇到这样的问题，自己做的程序要调用别人的函数、类什么的（比如数据库连接函数Connect(char*,char*)），但别人的函数参数用的是char*形式的，而我们知道，c_str()、data()返回的字符数组由该字符串拥有，所以是一种const char*,要想作为上面提及的函数的参数，还必须拷贝到一个char*,而我们的原则是能不使用C字符串就不使用。那么，这时候我们的处理方式是：如果此函数对参数(也就是char*)的内容不修改的话，我们可以这样Connect((char*)UserID.c_str(), (char*)PassWD.c_str()),但是这时候是存在危险的，因为这样转换后的字符串其实是可以修改的（有兴趣地可以自己试一试），所以我强调除非函数调用的时候不对参数进行修改，否则必须拷贝到一个char*上去。当然，更稳妥的办法是无论什么情况都拷贝到一个char*上去。同时我们也祈祷现在仍然使用C字符串进行编程的高手们（说他们是高手一点儿也不为过，也许在我们还穿开裆裤的时候他们就开始编程了，哈哈…）写的函数都比较规范，那样我们就不必进行强制转换了。<br /><br />2．3元素存取<br />    我们可以使用下标操作符[]和函数at()对元素包含的字符进行访问。但是应该注意的是操作符[]并不检查索引是否有效（有效索引0~str.length()），如果索引失效，会引起未定义的行为。而at()会检查，如果使用at()的时候索引无效，会抛出out_of_range异常。<br />    有一个例外不得不说，const string a;的操作符[]对索引值是a.length()仍然有效，其返回值是’\0’。其他的各种情况，a.length()索引都是无效的。举例如下：<br />const string Cstr(“const string”);<br />string Str(“string”);<br /><br />Str[3];    //ok<br />Str.at(3);  //ok<br /><br />Str[100]; //未定义的行为<br />Str.at(100);  //throw out_of_range<br /><br />Str[Str.length()]  //未定义行为<br />Cstr[Cstr.length()] //返回 ‘\0’<br />Str.at(Str.length());//throw out_of_range<br />Cstr.at(Cstr.length()) ////throw out_of_range<br /><br />我不赞成类似于下面的引用或指针赋值：<br />char&amp; r=s[2];<br />char* p= &amp;s[3];<br />因为一旦发生重新分配，r,p立即失效。避免的方法就是不使用。<br /><br />2．4比较函数<br />    C++字符串支持常见的比较操作符（&gt;,&gt;=,&lt;,&lt;=,==,!=），甚至支持string与C-string的比较(如 str&lt;”hello”)。在使用&gt;,&gt;=,&lt;,&lt;=这些操作符的时候是根据“当前字符特性”将字符按字典顺序进行逐一得比较。字典排序靠前的字符小，比较的顺序是从前向后比较，遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小。同时，string(“aaaa”) &lt;string(aaaaa)。<br />    另一个功能强大的比较函数是成员函数compare()。他支持多参数处理，支持用索引值和长度定位子串来进行比较。他返回一个整数来表示比较结果，返回值意义如下：0-相等 〉0-大于 &lt;0-小于。举例如下：<br />    string s(“abcd”);<br />    <br />    s.compare(“abcd”); //返回0<br />    s.compare(“dcba”); //返回一个小于0的值<br />    s.compare(“ab”); //返回大于0的值<br />    <br />s.compare(s); //相等<br />    s.compare(0,2,s,2,2); //用”ab”和”cd”进行比较 小于零<br />    s.compare(1,2,”bcx”,2); //用”bc”和”bc”比较。<br />怎么样？功能够全的吧！什么？还不能满足你的胃口？好吧，那等着，后面有更个性化的比较算法。先给个提示，使用的是STL的比较算法。什么？对STL一窍不通？靠，你重修吧！<br /><br />2．5 更改内容<br />这在字符串的操作中占了很大一部分。<br />首先讲赋值，第一个赋值方法当然是使用操作符=，新值可以是string(如：s=ns) 、c_string(如：s=”gaint”)甚至单一字符（如：s=’j’）。还可以使用成员函数assign()，这个成员函数可以使你更灵活的对字符串赋值。还是举例说明吧：<br />s.assign(str); //不说<br />s.assign(str,1,3);//如果str是”iamangel” 就是把”ama”赋给字符串<br />s.assign(str,2,string::npos);//把字符串str从索引值2开始到结尾赋给s<br />s.assign(“gaint”); //不说<br />s.assign(“nico”,5);//把’n’ ‘I’ ‘c’ ‘o’ ‘\0’赋给字符串<br />s.assign(5,’x’);//把五个x赋给字符串<br />把字符串清空的方法有三个：s=””;s.clear();s.erase();(我越来越觉得举例比说话让别人容易懂！)。<br />string提供了很多函数用于插入（insert）、删除（erase）、替换（replace）、增加字符。<br />先说增加字符（这里说的增加是在尾巴上），函数有 +=、append()、push_back()。举例如下：<br />s+=str;//加个字符串<br />s+=”my name is jiayp”;//加个C字符串<br />s+=’a’;//加个字符<br /><br />s.append(str);<br />s.append(str,1,3);//不解释了 同前面的函数参数assign的解释<br />s.append(str,2,string::npos)//不解释了<br /><br />s.append(“my name is jiayp”);<br />s.append(“nico”,5);<br />s.append(5,’x’);<br /><br />s.push_back(‘a’);//这个函数只能增加单个字符 对STL熟悉的理解起来很简单<br /><br />也许你需要在string中间的某个位置插入字符串，这时候你可以用insert()函数，这个函数需要你指定一个安插位置的索引，被插入的字符串将放在这个索引的后面。<br />    s.insert(0,”my name”);<br />    s.insert(1,str);<br />这种形式的insert()函数不支持传入单个字符，这时的单个字符必须写成字符串形式(让人恶心)。既然你觉得恶心，那就不得不继续读下面一段话：为了插入单个字符，insert()函数提供了两个对插入单个字符操作的重载函数：insert(size_type index,size_type num,chart c)和insert(iterator pos,size_type num,chart c)。其中size_type是无符号整数，iterator是char*,所以，你这么调用insert函数是不行的：insert(0,1,’j’);这时候第一个参数将转换成哪一个呢？所以你必须这么写：insert((string::size_type)0,1,’j’)！第二种形式指出了使用迭代器安插字符的形式，在后面会提及。顺便提一下，string有很多操作是使用STL的迭代器的，他也尽量做得和STL靠近。<br />删除函数erase()的形式也有好几种（真烦！），替换函数replace()也有好几个。举例吧：<br />string s=”il8n”;<br />s.replace(1,2,”nternationalizatio”);//从索引1开始的2个替换成后面的C_string<br />s.erase(13);//从索引13开始往后全删除<br />s.erase(7,5);//从索引7开始往后删5个<br /><br />2．6提取子串和字符串连接<br /><br />题取子串的函数是：substr(),形式如下：<br />s.substr();//返回s的全部内容<br />s.substr(11);//从索引11往后的子串<br />s.substr(5,6);//从索引5开始6个字符<br />把两个字符串结合起来的函数是+。（谁不明白请致电120）<br /><br />2．7输入输出操作<br />1．&gt;&gt; 从输入流读取一个string。<br />2．&lt;&lt; 把一个string写入输出流。<br />另一个函数就是getline(),他从输入流读取一行内容，直到遇到分行符或到了文件尾。<br /><br />2．8搜索与查找<br />查找函数很多，功能也很强大，包括了：<br />    find()<br />    rfind()<br />    find_first_of()<br />    find_last_of()<br />    find_first_not_of()<br />    find_last_not_of()<br />这些函数返回符合搜索条件的字符区间内的第一个字符的索引，没找到目标就返回npos。所有的函数的参数说明如下：<br />第一个参数是被搜寻的对象。第二个参数（可有可无）指出string内的搜寻起点索引，第三个参数（可有可无）指出搜寻的字符个数。比较简单，不多说 不理解的可以向我提出，我再仔细的解答。当然，更加强大的STL搜寻在后面会有提及。<br />最后再说说npos的含义，string::npos的类型是string::size_type,所以，一旦需要把一个索引与npos相比，这个索引值必须是string::size)type类型的，更多的情况下，我们可以直接把函数和npos进行比较（如：if(s.find(“jia”)==string::npos)）。<br />    第二部分是关于C++字符串对迭代器的支持的，视大家的需要我将写出来（意思就是不需要就算了，我乐得轻省，哈哈…）。<br />好了，大概的对string类型进行了阐述，希望起到抛砖引玉的作用，让初学者对string有个了解而不必已开始就面对复杂的内部结构和无数个注意事项。对字符串更详细地讲解有很多参考书，其实我的内容也是从C++标准程序库得来的，加上几句自己的看法，所以要感谢这本书的作者和译者。任何人对本文进行引用都要标明作者是Nicolai M.Josuttis 译者是侯捷/孟岩。不过不要提及我，任何观点的错误都与我无关（除了这里边体现我主观想法的几句话，也就那几句话）。</span>
		</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/20601.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-16 11:26 <a href="http://www.cnitblog.com/zouzheng/articles/20601.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何在C程序中处理汉字</title><link>http://www.cnitblog.com/zouzheng/articles/20599.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 16 Dec 2006 03:05:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20599.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20599.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20599.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20599.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20599.html</trackback:ping><description><![CDATA[学习过C语言的人也许有时会遇到这样一个问题：如何用变量存储汉字以及对这些变量进行操作。目前许多C语言参考书中都没涉及到这个问题，程序中多为处理英文变量和英文字符串，涉及到汉字的情况也大都是在printf语句中输出提示信息或结果，如：<br /><br />printf("请输入a,b的值：\n");<br />printf("输出功率为%s千瓦。\n",power);<br /><br />　　考虑到还有相当一部分人在学习和应用C语言，因此在这里向读者介绍一下笔者在这方面摸索出来的经验。<br /><br />　　存储汉字应该用字符数组，这一点是肯定的，关键问题是汉字在你的计算机系统上占几个字节。多数人会认为一个汉字肯定占两个字节，其实不然。汉字到底占几个字节，这是随系统不同而不同的，而且还依赖于软件环境，如Visual Basic中汉字占一个字节。可以在你的计算机上用strlen（）函数测试一下，如：<br /><br />printf("%d",strlen("计算机")); <br /><br />　　若输出为6，则每个汉字占两个字节；若输出12，则每个汉字占四个字节。大多数系统是每个汉字占两个字节的，即上述语句输出值为6。应当注意的是一个全角字符（包括标点符号）同汉字占据相同的字节。本文假定每个汉字占两个字节。<br /><br />　　这样你就可以用字符数组存储汉字了，但别忘了，由于C语言中字符串是以'\0'作为结束标记的，系统会自动加上这个标记符，而用strlen()函数测试的返回值不包括这个'\0',因此在定义和初始化字符数组时应当使数组长度最小为实际字符串长再加1，如：<br /><br />static char name[7]="孙悟空"; /*数组长度为7说明最多可存放3个汉字*/<br /><br />　　如果把数组长度定义为6，系统在编译时并不报错，但是当运行程序时有时会莫名其妙地重复输出或多输出汉字，如果出现这种情况，应当首先考虑数组长度是否有问题。当然在定义的同时初始化可以省略数组长度，如：<br /><br />static char array[]={"中国计算机软件专业技术水平考试"};<br /><br />　　同样可以定义二维数组，下面这个二维数组可以存放10个人名：<br /><br />char member[10][8]; /*10个元素，每个元素中最多能放3个汉字(不是4个！)*/<br /><br />　　因为二维数组member[10][8]可以看成特殊的一维数组member[0],member[1],…，member[9],这些一维数组分别表示各行，而一行就表示了一个人名，因此如果想操作每个人名，只需写成member[0],member[1],…而不写成member[0][8]，member[1][8],…。<br /><br />　　顺便提一下，在C编译窗口中输入汉字需要中文环境。如果读者用的是DOS操作系统，那么就需要安装CCDOS或UCDOS；如果是WIN95/98操作系统，则在DOS命令窗口中运行下面这个批处理文件即可，而不必另外安装中文DOS系统：<br /><br />C:\WINDOWS&gt;PDOS95<br /><br />　　在C编辑窗口中用Ctrl+空格键启动WIN95中文输入法。另外由于C程序是基于DOS的，因此编辑、编译C程序最好在全屏幕状态，否则容易出现问题。<br /><br />　　下面给出一个实例程序，它的功能是计算出输入年份的天干地支。<br /><br />#include "string.h"<br />main()<br />{<br />int a,b,year;<br />char gz[6]; /*用来表示干支的字符变量*/ <br />/*注意：下面两个语句中{}内的引号和逗号不能为全角字符*/ <br />static char tg[10][3]={"甲","乙","丙","丁","戊","己","庚","辛","壬","癸"}; <br />static char dz[12][3]={"子","丑","寅","卯","辰","巳","午","未","申","酉","戍","亥"};<br />printf("请输入一个年份：");<br />scanf("%d",&amp;year);<br />a=(year-1804)%10; /*以1804年为参考年份，1804年是甲子年*/<br />b=(year-1804)%12;<br />strcpy(gz,tg[a]);<br />strcat(gz,dz[b]);<br />printf("%d年的干支为：%s\n",year,gz);<br />} <br /><br />　　运行结果： <br />请输入一个年份：1999<br />1999年的干支为：己卯 <img src ="http://www.cnitblog.com/zouzheng/aggbug/20599.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-16 11:05 <a href="http://www.cnitblog.com/zouzheng/articles/20599.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针与数组的对比</title><link>http://www.cnitblog.com/zouzheng/articles/20598.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 16 Dec 2006 02:49:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20598.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20598.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20598.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20598.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20598.html</trackback:ping><description><![CDATA[C++/C 程序中，指针和数组在不少地方可以相互替换着用，让人产生一种错觉，以<br />为两者是等价的。<br />数组要么在静态存储区被创建（如全局数组），要么在栈上被创建。数组名对应着<br />（而不是指向）一块内存，其地址与容量在生命期内保持不变，只有数组的内容可以改<br />变。<br />指针可以随时指向任意类型的内存块，它的特征是“可变”，所以我们常用指针来<br />操作动态内存。指针远比数组灵活，但也更危险。<br />下面以字符串为例比较指针与数组的特性。<br /><br />修改内容<br />字符数组a 的容量是6 个字符，其内容为hello\0。a 的内容可以改<br />变，如a[0]= ‘X’。指针p 指向常量字符串“world”（位于静态存储区，内容为world\0），<br />常量字符串的内容是不可以被修改的。从语法上看，编译器并不觉得语句p[0]= ‘X’有什<br />么不妥，但是该语句企图修改常量字符串的内容而导致运行错误。<br />char a[] = “hello”;<br />a[0] = ‘X’;<br />cout &lt;&lt; a &lt;&lt; endl;<br />char *p = “world”; // 注意p 指向常量字符串<br />p[0] = ‘X’; // 编译器不能发现该错误<br />cout &lt;&lt; p &lt;&lt; endl;<br />修改数组和指针的内容<br /><br />内容复制与比较<br />不能对数组名进行直接复制与比较。若想把数组a 的内容复制给数<br />组b，不能用语句 b = a ，否则将产生编译错误。应该用标准库函数strcpy 进行复制。<br />同理，比较b 和a 的内容是否相同，不能用if(b==a) 来判断，应该用标准库函数strcmp<br />进行比较。<br />语句p = a 并不能把a 的内容复制指针p，而是把a 的地址赋给了p。要想复制a<br />的内容，可以先用库函数malloc 为p 申请一块容量为strlen(a)+1 个字符的内存，再<br />用strcpy 进行字符串复制。同理，语句if(p==a) 比较的不是内容而是地址，应该用库<br />函数strcmp 来比较。<br />// 数组…<br />char a[] = "hello";<br />char b[10];<br />strcpy(b, a); // 不能用 b = a;<br />if(strcmp(b, a) == 0) // 不能用 if (b == a)<br />…<br />// 指针…<br />int len = strlen(a);<br />char *p = (char *)malloc(sizeof(char)*(len+1));<br />strcpy(p,a); // 不要用 p = a;<br />if(strcmp(p, a) == 0) // 不要用 if (p == a)<br />…<br />数组和指针的内容复制与比较<br /><br />计算内存容量<br />用运算符sizeof 可以计算出数组的容量（字节数）。（a）中，sizeof(a)<br />的值是12（注意别忘了’\0’）。指针p 指向a，但是sizeof(p)的值却是4。这是因为<br />sizeof(p)得到的是一个指针变量的字节数，相当于sizeof(char*)，而不是p 所指的内<br />存容量。C++/C 语言没有办法知道指针所指的内存容量，除非在申请内存时记住它。<br />注意当数组作为函数的参数进行传递时，该数组自动退化为同类型的指针。示例<br />（b）中，不论数组a 的容量是多少，sizeof(a)始终等于sizeof(char *)。<br />char a[] = "hello world";<br />char *p = a;<br />cout&lt;&lt; sizeof(a) &lt;&lt; endl; // 12 字节<br />cout&lt;&lt; sizeof(p) &lt;&lt; endl; // 4 字节<br />（a） 计算数组和指针的内存容量<br /><br />void Func(char a[100])<br />{<br />cout&lt;&lt; sizeof(a) &lt;&lt; endl; // 4 字节而不是100 字节<br />}<br />（b） 数组退化为指针<br /><img src ="http://www.cnitblog.com/zouzheng/aggbug/20598.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-16 10:49 <a href="http://www.cnitblog.com/zouzheng/articles/20598.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数组和字符数组首地址的输出</title><link>http://www.cnitblog.com/zouzheng/articles/20597.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 16 Dec 2006 02:48:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20597.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20597.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20597.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20597.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20597.html</trackback:ping><description><![CDATA[
		<p>char *p;int  *p1;</p>
		<p>int  a[10]={1,2,3,4,5,6,7,8,9,0};<br />char str[]="mengfan";<br />p=str;<br />p1=a; </p>
		<p>printf( "%x" , p);   /*输出的是地址*/<br />printf("%s",p);   /*输出的是字符串mengfan*/</p>
		<p>printf("%x",p1);输出的是地址</p>
		<p>printf("%s",p1);    /*输出的是字符串，乱码*/</p>
		<p>p，p1中存放的应为字符串，但在输出时还要兼顾前面的输出格式符的控制，如果</p>
		<p>是%s，则会在p的地址内的数据输出，而不再输出p本身的内容地址</p>
		<p>printf("%c",p);//会输出字符串的第一个字母m</p>
		<p>printf("%d",p1);//会输出数组的第一个数字</p>
		<p>所以 p,p1离存的都是地址，在用%c,%d时都能作为首地址输出第一个字母或数组元素，</p>
		<p>但是如果换用，%s,输出字符串，在一个地址空间内是不能存在字符串的，所以编译器</p>
		<p>会认为是连续空间内的字符串，所以就输出了字符串。</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/20597.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-16 10:48 <a href="http://www.cnitblog.com/zouzheng/articles/20597.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对字符数组，字符指针，字符串常量以及其sizeof的一些总结</title><link>http://www.cnitblog.com/zouzheng/articles/20596.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 16 Dec 2006 02:36:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20596.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20596.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20596.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20596.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20596.html</trackback:ping><description><![CDATA[
		<p>在网上经常看到一些类似字符串或者sizeof等的公司笔试题，鄙人不才，但也作了一些总结，<br />这些总结都是在vc2003下测试得到的结果，如果不对，请指出。</p>
		<p>1.以字符串形式出现的，编译器都会为该字符串自动添加一个0作为结束符，如在代码中写<br />  "abc",那么编译器帮你存储的是"abc\0"</p>
		<p>2."abc"是常量吗？答案是有时是，有时不是。</p>
		<p>  不是常量的情况："abc"作为字符数组初始值的时候就不是，如<br />                  char str[] = "abc";<br />    因为定义的是一个字符数组，所以就相当于定义了一些空间来存放"abc"，而又因为<br />    字符数组就是把字符一个一个地存放的，所以编译器把这个语句解析为<br />    char str[3] = {'a','b','c'};<br />                  又根据上面的总结1，所以char str[] = "abc";的最终结果是<br />    char str[4] = {'a','b','c','\0'};<br />    做一下扩展，如果char str[] = "abc";是在函数内部写的话，那么这里<br />    的"abc\0"因为不是常量，所以应该被放在栈上。<br />  <br />  是常量的情况：  把"abc"赋给一个字符指针变量时，如<br />                  char* ptr = "abc";<br />    因为定义的是一个普通指针，并没有定义空间来存放"abc"，所以编译器得帮我们<br />    找地方来放"abc"，显然，把这里的"abc"当成常量并把它放到程序的常量区是编译器<br />    最合适的选择。所以尽管ptr的类型不是const char*，并且ptr[0] = 'x';也能编译<br />    通过，但是执行ptr[0] = 'x';就会发生运行时异常，因为这个语句试图去修改程序<br />    常量区中的东西。<br />    记得哪本书中曾经说过char* ptr = "abc";这种写法原来在c++标准中是不允许的，<br />    但是因为这种写法在c中实在是太多了，为了兼容c，不允许也得允许。虽然允许，<br />    但是建议的写法应该是const char* ptr = "abc";这样如果后面写ptr[0] = 'x'的<br />    话编译器就不会让它编译通过，也就避免了上面说的运行时异常。<br />    又扩展一下，如果char* ptr = "abc";写在函数体内，那么虽然这里的"abc\0"被<br />    放在常量区中，但是ptr本身只是一个普通的指针变量，所以ptr是被放在栈上的，<br />    只不过是它所指向的东西被放在常量区罢了。</p>
		<p>3.数组的类型是由该数组所存放的东西的类型以及数组本身的大小决定的。<br />  如char s1[3]和char s2[4]，s1的类型就是char[3]，s2的类型就是char[4]，<br />  也就是说尽管s1和s2都是字符数组，但两者的类型却是不同的。</p>
		<p>4.字符串常量的类型可以理解为相应字符常量数组的类型，<br />  如"abcdef"的类型就可以看成是const char[7]</p>
		<p>5.sizeof是用来求类型的字节数的。如int a;那么无论sizeof(int)或者是sizeof(a)都<br />  是等于4，因为sizeof(a)其实就是sizeof(type of a)</p>
		<p>6.对于函数参数列表中的以数组类型书写的形式参数，编译器把其解释为普通<br />  的指针类型，如对于void func(char sa[100],int ia[20],char *p)<br />  则sa的类型为char*，ia的类型为int*，p的类型为char*</p>
		<p>
				<br />7.根据上面的总结，来实战一下：<br />  对于char str[] = "abcdef";就有sizeof(str) == 7,因为str的类型是char[7]，<br />  也有sizeof("abcdef") == 7，因为"abcdef"的类型是const char[7]。<br />  对于char *ptr = "abcdef";就有sizeof(ptr) == 4，因为ptr的类型是char*。<br />  对于char str2[10] = "abcdef";就有sizeof(str2) == 10，因为str2的类型是char[10]。<br />  对于void func(char sa[100],int ia[20],char *p);<br />  就有sizeof(sa) == sizeof(ia) == sizeof(p) == 4，<br />  因为sa的类型是char*，ia的类型是int*，p的类型是char*。</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/20596.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-16 10:36 <a href="http://www.cnitblog.com/zouzheng/articles/20596.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>给结构里的char指针赋值的一个难点 </title><link>http://www.cnitblog.com/zouzheng/articles/20595.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 16 Dec 2006 02:28:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20595.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20595.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20595.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20595.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20595.html</trackback:ping><description><![CDATA[struct msg{<br />    char *p1;<br />    char *p2;<br />}myptrs;<br /><br />myprts.p1 = "Teach Yourself C In 21 Days";<br />myprts.p2 = "By SAMS Publishing";<br /><br />就是这里赋值不理解，赋给p1的应该是地址才对啊，怎么是字符串？<br />还有，为什么不是'Teach Yourself C In 21 Days';<br />而是<br />"Teach Yourself C In 21 Days";<br />什么时候用单引号，什么时候用双引号？<br /><br />*************************************<br /><font color="#0000ff">回复人： mystaring(龙的传人)</font> <br />就像char p1[5]="abcd"一样，p1只是一个地址，指向字符串的首地址，输出时遇到'\0'时结束。<br />printf("%s",p1); 会输出abcd；<br />但若开始时定义为char p1[4]="abcd"则输出的字符串会不止abcd，后面还有乱码，因为机器会一直遇到内存中的'\0'才结束<br />*************************************<br /><font color="#0000ff">回复人： foochow(恰似你的温柔)</font><br />单引号用来给出一个字符常量，而双引号用来给出一个字符串常量,前者（字符常量）由编译器直接算出对应值，赋给某个变量或参加表达式的运算，它并不会在内存中占用空间,而后者（字符串常量）则会在内存中分配一定的单元放字符串，并把其起始地址传给一个指针或是作为函数的某个参数.<br />*************************************<br /><font color="#0000ff">回复人： kgdiwss(∮明天去要饭)</font> <br />换句话说：<br />myprts.p1 = "Teach Yourself C In 21 Days";<br />myprts.p2 = "By SAMS Publishing";<br />这里赋给p1和p2的，是字符串"Teach Yourself C In 21 Days"和"By SAMS Publishing"的首地址是吧。如果*p1和*p2读的话，会从首地址开始读，直到读到\0为止。是这样理解吗？<br /><br />不过printf("%s",p1);这个p1保存的不是地址吗？怎么可以把地址输出成%s啊。为什么不是：<br />printf("%s",*p1);<br />*************************************<br /><font color="#0000ff">回复人： foochow(恰似你的温柔)</font> <br />不过printf("%s",p1);这个p1保存的不是地址吗？怎么可以把地址输出成%s啊。为什么不是：<br />printf("%s",*p1);<br />.................<br />这个就是字符串指针的特殊地方,输出指针的时候printf("%s",p1),就会输出p1指向的字符串的字符直到'\0'为止,printf("%s",*p1);这个只输出一个字符.<br />*************************************<br /><img src ="http://www.cnitblog.com/zouzheng/aggbug/20595.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-16 10:28 <a href="http://www.cnitblog.com/zouzheng/articles/20595.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针与数组的特性</title><link>http://www.cnitblog.com/zouzheng/articles/20594.html</link><dc:creator>zz</dc:creator><author>zz</author><pubDate>Sat, 16 Dec 2006 02:20:00 GMT</pubDate><guid>http://www.cnitblog.com/zouzheng/articles/20594.html</guid><wfw:comment>http://www.cnitblog.com/zouzheng/comments/20594.html</wfw:comment><comments>http://www.cnitblog.com/zouzheng/articles/20594.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/zouzheng/comments/commentRss/20594.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/zouzheng/services/trackbacks/20594.html</trackback:ping><description><![CDATA[
		<p>字符数组a的容量是6个字符，其内容为hello\0。a的内容可以改变，如a[0]= ‘X’。指针p指向常量字符串“world”（位于静态存储区，内容为world\0），常量字符串的内容是不可以被修改的。从语法上看，编译器并不觉得语句p[0]= ‘X’有什么不妥，但是该语句企图修改常量字符串的内容而导致运行错误。</p>
		<p>char a[] = “hello”;<br />a[0] = ‘X’;<br />cout &lt;&lt; a &lt;&lt; endl;<br />char *p = “world”;     // 注意p指向常量字符串<br />p[0] = ‘X’;            // 编译器不能发现该错误<br />cout &lt;&lt; p &lt;&lt; endl;</p>
<img src ="http://www.cnitblog.com/zouzheng/aggbug/20594.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/zouzheng/" target="_blank">zz</a> 2006-12-16 10:20 <a href="http://www.cnitblog.com/zouzheng/articles/20594.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>