﻿<?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>C++博客-. . . . . . . . . . . . . . Blog Garden'  C plus plus (My technology Impire!)-随笔分类-C plus plus</title><link>http://www.cppblog.com/stevennash/category/2749.html</link><description>................................................................ It‘s a age of economic globalization and Infomation globalization........................................</description><language>zh-cn</language><lastBuildDate>Sat, 24 May 2008 11:03:37 GMT</lastBuildDate><pubDate>Sat, 24 May 2008 11:03:37 GMT</pubDate><ttl>60</ttl><item><title>下面的声明分别是什么意思?</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13318.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:23:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13318.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13318.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13318.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13318.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13318.html</trackback:ping><description><![CDATA[
		<font color="#800080">下面的声明分别是什么意思?<br /><br /><p>int(* foo())()<br />int(* foo())[]<br />int(* foo[])()<br /><br />变态的：char * const *(*next)()<br /><br />更变态的：char *(* c[10])(int **p)<br /><br />int(* foo())()中，foo应该与右边的()先结合，后与左边的*结合，所以应该是:foo是个函数，它的返回值是个函数指针</p><p>int(* foo())[]  //返回值是整型数组的函数指针<br /><br /><font class="f006">int(* foo[])()  //一个返回值是整型的函数指针数组</font></p><p>char * const *(*next)()  //返回值是指向常量字符指针的指针的函数指针</p><p>char *(* c[10])(int **p) //一个有10个元素的返回值是字符指针、参数是指针指针的函数指针数组<br /></p></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13318.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:23 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13318.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2个难的函数指针说明</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13316.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:21:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13316.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13316.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13316.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13316.html</trackback:ping><description><![CDATA[
		<font color="#000080">2个难的函数指针说明<br /><br /><p><font face="Courier New" color="#0000ff">void (*(*papf)[2])(int);</font></p><p><font face="Courier New" color="#0000ff">papf是一个指针，指向一个数组，该数组拥有两个元素，都是函数指针，其函数形式拥有一个int参数，无返回值。</font></p><p><font face="Courier New" color="#0000ff">void(*apf[20])(double);</font></p><p><font face="Courier New" color="#0000ff">apf是一个数组，拥有20个元素，每个元素都是函数指针，该函数有一个double参数，没有返回值。</font></p></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:21 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>复杂的声明和定义</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13315.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:20:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13315.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13315.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13315.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13315.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13315.html</trackback:ping><description><![CDATA[
		<font color="#000080">复杂的声明和定义<br /><br /><font color="#000000">先看一个简单例子： <br />        void(*funcPtr)(); <br />        当看到这样的一个定义时，最好的处理方法是从中间开始和向外扩展以及挖空括号。<br />        “从中间开始”的意思是从变量名开始，“向外扩展”的意思是先注意右边最近的项，遇括号变向，如此持续下去。“挖空括号”是指把变量名所在的括号去掉并去掉参数表所在括号，就得到其函数的返回值（仅限于函数声明）。<br />        以上分析如下： 中间开始----&gt; funcPtr 向右走------&gt; 遇括号 转向--------&gt; 遇* ，funcPtr是一个指针 ,又是括号 转向--------&gt; 一个空参数表（不带参数的函数） 向左走------&gt; void 返回值------&gt; void 最终结果是： funcPtr是一个指针，它指向一个不带参数并返回void的函数。 <br /><br />再看几个更复杂的： <br />        void *(*(*fp1)(int))[10]; fp1是一个指向函数的指针，它接受一个int参数并返回一个指向含有10个void指针数组的指针(void * [10])。 <br />        float (*(*fp2)(int,int,float))(int); fp2是一个指向函数的指针，它接受三个参数：int, int, float且返回一个指向函数的指针，该函数有一个int参数并返回一个float。 <br />        typedef double (*(*(*fp3)())[10])(); //返回值为:double (*(*)[10])(); fp3 a; fp3是一个指向函数的指针，该函数无参数并返回一个指向含有10个指向函数指针数组的指针,这些函数不接受参数并返回double值. <br />        int (*(*f4())[10])(); //返回值为 int(*([10]))(); f4是一个返回指针的函数,该指针指向含有10个函数指针的数组,这些函数返回整型值. <br /><br />总结: <br />        我们可能很少甚至是从不使用过如此复杂的声明和定义,但如果通过练习能把它搞清楚的话,就不会在现实生活中可能遇到的稍微复杂的情况所困惑!参考资料: &lt;<c++编程思想第二版>&gt;</c++编程思想第二版></font><br /></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13315.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:20 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13315.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>看懂复杂C++</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13314.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:15:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13314.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13314.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13314.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13314.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13314.html</trackback:ping><description><![CDATA[
		<font color="#000080">看懂复杂C++<br /><br /><p>typedef的妙用</p><p>typedef给你一种方式来克服“*只适合于变量而不适合于类型”的弊端。你可以如下使用typedef：</p><p>typedef char * PCHAR;<br />PCHAR p,q;</p><p>这里的p和q都被声明为指针。（如果不使用typedef，q将被声明为一个char变量，这跟我们的第一眼感觉不太一致！）下面有一些使用typedef的声明，并且给出了解释：</p><p>typedef char * a;  // a is a pointer to a char</p><p>typedef a b();     // b is a function that returns<br />                   // a pointer to a char</p><p>typedef b *c;      // c is a pointer to a function<br />                   // that returns a pointer to a char</p><p>typedef c d();     // d is a function returning<br />                   // a pointer to a function<br />                   // that returns a pointer to a char</p><p>typedef d *e;      // e is a pointer to a function <br />                   // returning  a pointer to a <br />                   // function that returns a <br />                   // pointer to a char</p><p>e var[10];         // var is an array of 10 pointers to <br />                   // functions returning pointers to <br />                   // functions returning pointers to chars.</p><p>typedef经常用在一个结构声明之前，如下。这样，当创建结构变量的时候，允许你不使用关键字struct（在C中，创建结构变量时要求使用struct关键字，如struct tagPOINT a；而在C++中，struct可以忽略，如tagPOINT b）。</p><p>typedef struct tagPOINT<br />{<br />    int x;<br />    int y;<br />}POINT;</p><p>POINT p; /* Valid C code */</p><p></p><p>函数指针</p><p>函数指针可能是最容易引起理解上的困惑的声明。函数指针在DOS时代写TSR程序时用得最多；在Win32和X-Windows时代，他们被用在需要回调函数的场合。当然，还有其它很多地方需要用到函数指针：虚函数表，STL中的一些模板，Win NT/2K/XP系统服务等。让我们来看一个函数指针的简单例子：</p><p>int (*p)(char);</p><p>这里p被声明为一个函数指针，这个函数带一个char类型的参数，并且有一个int类型的返回值。另外，带有两个float类型参数、返回值是char类型的指针的指针的函数指针可以声明如下：</p><p>char ** (*p)(float, float);</p><p>那么，带两个char类型的const指针参数、无返回值的函数指针又该如何声明呢？参考如下：</p><p>void * (*a[5])(char * const, char * const);</p><p></p><p>“右左法则”[重要！！！]</p><p>The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.</p><p>这是一个简单的法则，但能让你准确理解所有的声明。这个法则运用如下：从最内部的括号开始阅读声明，向右看，然后向左看。当你碰到一个括号时就调转阅读的方向。括号内的所有内容都分析完毕就跳出括号的范围。这样继续，直到整个声明都被分析完毕。</p><p>对上述“右左法则”做一个小小的修正：当你第一次开始阅读声明的时候，你必须从变量名开始，而不是从最内部的括号。</p><p>下面结合例子来演示一下“右左法则”的使用。</p><p>int * (* (*fp1) (int) ) [10];</p><p>阅读步骤：<br />1. 从变量名开始 -------------------------------------------- fp1<br />2. 往右看，什么也没有，碰到了)，因此往左看，碰到一个* ------ 一个指针<br />3. 跳出括号，碰到了(int) ----------------------------------- 一个带一个int参数的函数<br />4. 向左看，发现一个* --------------------------------------- （函数）返回一个指针<br />5. 跳出括号，向右看，碰到[10] ------------------------------ 一个10元素的数组<br />6. 向左看，发现一个* --------------------------------------- 指针<br />7. 向左看，发现int ----------------------------------------- int类型</p><p>总结：fp1被声明成为一个函数的指针的指针的数组，这个数组有10个元素，函数的原型为带一个int类型的参数，返回值为一个指针？</p><p>再来看一个例子：</p><p>int *( *( *arr[5])())();</p><p>阅读步骤：<br />1. 从变量名开始 -------------------------------------------- arr<br />2. 往右看，发现是一个数组 ---------------------------------- 一个5元素的数组<br />3. 向左看，发现一个* --------------------------------------- 指针<br />4. 跳出括号，向右看，发现() -------------------------------- 不带参数的函数<br />5. 向左看，碰到* ------------------------------------------- （函数）返回一个指针<br />6. 跳出括号，向右发现() ------------------------------------ 不带参数的函数<br />7. 向左，发现* --------------------------------------------- （函数）返回一个指针<br />8. 继续向左，发现int --------------------------------------- int类型</p><p>总结：？？</p><p><br />还有更多的例子：</p><p>float ( * ( *b()) [] )();              // b is a function that returns a <br />                                       // pointer to an array of pointers<br />                                       // to functions returning floats.</p><p>void * ( *c) ( char, int (*)());       // c is a pointer to a function that takes<br />                                       // two parameters:<br />                                       //     a char and a pointer to a<br />                                       //     function that takes no<br />                                       //     parameters and returns<br />                                       //     an int<br />                                       // and returns a pointer to void.</p><p>void ** (*d) (int &amp;, <br />  char **(*)(char *, char **));        // d is a pointer to a function that takes<br />                                       // two parameters:<br />                                       //     a reference to an int and a pointer<br />                                       //     to a function that takes two parameters:<br />                                       //        a pointer to a char and a pointer<br />                                       //        to a pointer to a char<br />                                       //     and returns a pointer to a pointer <br />                                       //     to a char<br />                                       // and returns a pointer to a pointer to void</p><p>float ( * ( * e[10]) <br />    (int &amp;) ) [5];                    // e is an array of 10 pointers to <br />                                       // functions that take a single<br />                                       // reference to an int as an argument <br />                                       // and return pointers to<br />                                       // an array of 5 floats.</p></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13314.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:15 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13314.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>声明与函数、函数指针</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13313.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:14:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13313.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13313.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13313.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13313.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13313.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 声明与函数、函数指针概述 　　在很多情况下，尤其是读别人所写代码的时候，对C语言声明的理解能力变得非常重要，而C语言本身的凝练简约也使得C语言的声明常常会令人感到非常困惑，因此，在这里我用一篇的内容来集中阐述一下这个问题。　　问题：声明与函数 　　有一段程序存储在起始地址为0的一段内存上，如果我们想要调用这段程序，请问该如何去做？ 　　答案　　答案是(*(void (*)( ) )0)( )。看起...&nbsp;&nbsp;<a href='http://www.cppblog.com/stevennash/archive/2006/10/04/13313.html'>阅读全文</a><img src ="http://www.cppblog.com/stevennash/aggbug/13313.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:14 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13313.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>星号的秘密</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13311.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:13:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13311.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13311.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13311.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13311.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13311.html</trackback:ping><description><![CDATA[
		<font color="#000080">星号的秘密<br /><br /><div><b>1</b><b>、乘法运算符</b></div><div> </div><div><b>2</b><b>、定义指针</b></div><div>int *p = 0; 还是 int* p = 0;？</div><div>后一种比较容易这样理解：定义了一个变量p，它是指针型的（更详细一点，是指向int的指针型），相比而言，前面一种定义似乎是定义了*P这个奇怪的东西。但是后面一种写法会带来一个容易产生的误解：</div><div>int* p1, p2; </div><div>这儿给人的感觉似乎是定义了两个指针型变量p1和p2，但是，事实上，这种直觉是错误的，正确的理解方式是int *p1, p2;即p1是指针型的，而p2确是整型的。</div><div>在MS VC++ 6.0中，是按照后面一种格式写的。</div><div> </div><div><b>3</b><b>、何谓指针？</b></div><div>指针仅仅表示一个内存中的某个地址？</div><div>非也，注意到，我们在定义指针的时候，都关联了一个类型，如int，char，或者是string等等，如果说指针仅仅表示一个内存中的地址，那何必要关联这么多变化的东西呢？完全可以DWORD p＝0；这样解决问题。</div><div>关联了的数据类型是作何用的呢？</div><div>它可以指示编译器怎样解释特定地址上内存的内容，以及该内存区域应该跨越多少内存单元。如 int *p;</div><div>编译器可以从这个定义中获得信息：1、p指向的内存存放的是整型数据，2、由于该内存区域只存放了一个数据，跨越的内存区域为4个字节，即p+1的效果是跳过了四个字节。</div><div>另一个复杂一点的例子，如</div><div>struct a</div><div>{int x1;</div><div>short x2;</div><div>a *next;</div><div>}</div><div>定义指针 a *p;那么编译器对这个指针又作何解释呢？</div><div>1、p指向的内存区域依次存放了三种类型的数据，分别是int，short和一个指针型数据。</div><div>2、p指向的内存区域跨越了12个字节，即p+1的效果是跳过了12个字节。（为何不是10?对齐的原因）</div><div> </div><div>但是，C++中定义了一种特殊的指针，它去处了一般指针中对内存区域内容以及大小的解释，以满足特定定的需要，如我们只需要某块内存的首地址，不需要考虑其中的数据类型以及大小。这种形式为 void *; 这种类型的指针可以被任意数据类型的指针赋值，如上面的a* 型，void *q = p; 唯一例外的是，不能把函数指针赋给它。</div><div> </div><div><b>4</b><b>、关于const</b><b>修饰符</b></div><div>当const遇到指针，麻烦事就来了，看：const int* p; int* const p; const int* const p;</div><div>这三个表达式，第一个表示p是一个指针，p本身平凡无比，但是p所指向的对象是一个特殊的对象－－整型常量；第二个表示：这个p指针不是一个普通的指针，它是个常量指针，即只能对其初始化，而不能赋值，另外，这个指针所指向的对象是一平凡的int型变量；第三个则结合了前两者：指针和指向的对象都非同寻常，都是常量。</div><div>有了const，赋值的问题就变得麻烦起来，</div><div>首先,对于 const int* p；这儿由于p指向的对象是个常量，所以在通过p来引用这个对象的时候不可对其进行赋值！对于一个常量对象，不可以用普通的指针指向，而必须用这种指向常量的指针，原因很简单，通过普通指针可以改变指向的那个值，但是对于一个非常量对象，即普通变量，可不可以将其地址赋给指向常量的指针呢？是可以的，但是一旦这样指向之后，由于这个指针本身定义的是指向常量的指针，因而编译器统一认为其是指向变量的，因而此时不可以通过该指针修改所指向的对象的值。</div><div>第二，对于 int* const p;这儿p本身是个常量指针，所以根本就不能赋值，所以不存在赋值的问题。不可以用常量对其进行初始化，因为这个指针不是指向常量的；只能用变量对其初始化。</div><div>第三，对于 const int* const p;这儿，只能初始化，不能赋值。可以利用常量进行初始化；也可以利用变量对其初始化，不过不可以利用该指针对该变量进行赋值。</div><div> </div><div>const int* p这种指向常量对象的指针常用来用作某些函数的形参，用意是从编译器的角度防止用户在函数中将传递进去的参数修改，虽然用户本身也可以避免，但是这样更可靠一点－－当用户不小心作出修改实参的行为时，编译器发现并阻止这种行为。</div><div> </div><div>this指针是const xx* const型的。</div><div> </div><div><b>5</b><b>、函数与指针</b></div><div>指向函数的指针：可以利用它代替函数名来调用函数。</div><div>如何定义函数指针，由于一个程序中可以用多个函数名相同的情形（即函数的重载），因而，定义函数指针的时候，必须包含函数的参数，这样才能准确地将指针指向某函数。</div><div>定义：int (*p)(const char*, int); 表示p是一个指向函数的指针，该函数的两个参数为const char* 和int，另外该函数返回int型值。</div><div>容易混淆的是：int *p(const char *, int); 缺少了一个括号，此时编译器的解释是 int* p(const char*, int);即其含义是一个函数的声明，函数名为p，返回一个指向int型的指针。那么 int* (*p)(const char*, int);则是定义了一个函数指针p，它指向一个函数，该函数的两个参数为const char*和int，该函数返回一个指向int型的指针</div><div> </div><div>函数指针的初始化与初始化：</div><div>函数名如同数组名，编译器将其解释为指向该类型函数的指针，故而，可以领用函数名，或者&amp;函数名对函数指针进行初始化或者赋值，另外，可以用另一个函数指针对该指针进行初始化以及赋值。重要的一点是指针与函数名，指针与指针必须具有完全相同的参数表和返回类型（必须完全完全一样，任何一点不同都不可以）。不存在隐式的类型转换，用户必须保证完全的一致性。</div><div>初始化或者赋值为0，表示不指向任何函数。</div><div> </div><div>利用函数指针调用函数是可以p（x，y）这样调用，也可以（*p)(x,y)这样调用，前提是p已经正确的赋值或者初始化。</div><div> </div><div>函数返回指针：可以返回一个非基本类型的对象。</div><div> </div><div><b>6</b><b>、数组与指针</b></div><div>int a[3] = {1,2,3};</div><div>考虑 a，a[0], &amp;a, 以及 &amp;a[0]这三个表达式的含义:</div><div>首先这三个表达式的数值结果是一样的－－数组的首地址（即数组中第0个元素的地址），但是编译器对三者的解释不同：</div><div>对于a，编译器将其解释为一个指针，指向的是一个整型数据，因而利用a＋1即指向数组中的第一的元素，a＋2指向第二个元素。</div><div>对于a这个指针有些特殊的性质：</div><div>a不是一个普通的指针，它同时是一个数组名，即关联了一个数组，因而某些性质上与普通的指针不同。</div><div>普通的指针可以被赋值，即可以用一个地址或者另一个指针修改当前指针的指向，然而对于a这种关联了一个数组的指针，如果允许这样赋值的话，那么数组中的元素将无法被访问，所以不允许对数组名代表的指针进行赋值。在这一点上a相当于指针常量，即只能被初始化，不可以进行赋值。</div><div>   虽然a不可以被赋值，但是将a赋给其他的元素是完全可以的，这一点同普通的指针没有不同。</div><div>综上，a相当于一个指针常量。（type* const型的）</div><div> </div><div>本质上a[i]操作被编译器解释为*(a+i)操作，即[]运算符是通过数组名指针实现的，因而&amp;a[0]的含义即&amp;(*a),显然对一个指针先*(解引用),再&amp;(引用)，等价于什么都没做，还是这个指针本身，因而a完全等价于&amp;a[o],－－(&amp;a[0])[i]等价于a[i]，形式有点诡异，呵呵。而对于&amp;a这个表达式，奇怪的是这个也是数组的首地址，那么就是说，这个数组的首地址中存放了一个指针常量（即数组名），但是数组的首地址中不是存放的一个int型的数字吗？这是怎么回事呢？难道一个地址能存放两个东西？</div><div>暂时无法解释，可以这样认为编译器发现这种&amp;和数组名的结合运算时，即返回数组首地址，只不过，这是，这仅仅是个纯粹的地址，它不再具有指针的特性，即编译器不再将其解释为指针，用户不可以通过+1运算来访问下一个数组元素。它的+1就是数学上的+1。</div><div> </div><div>当数组变为多维，问题变成怎么样了呢？</div><div>考虑二维数组 int b[4][3] = {{1,2,3}, {4,5,6}, {7,8,9}, {10,11,12}}; b，&amp;b, b[0], &amp;b[0], &amp;b[0][0]几个表达式的含义：</div><div>首先，c＋＋中数组元素的存放是以行序为主序，即第一段存放第一行的数据，第二段存放第二行的数据，....，如此。</div><div>首先考虑数组名b，编译器同样将数组名b解释为一个指针，但是显然这个指针不是普通的指针。这个b指向数组所有元素的首地址，这一点是勿庸置疑的，那么这个指针一步跨越的内存是多大呢？在本例中，b一步跨越12个字节，即b一步跨越数组中的一行元素，实际上b是一个指针的指针，或者说指向指针的指针，即b所指向的内容是一个指针，(同样对于b+1，b+2)，b[i][j]这种访问方式本质上即：先通过+i，将指针跳跃到第i行，从而获得了指向第i行首地址的指针b[i],然后通过这个指针，再通过+j,跳跃j步，到达了第j个元素，即找到第i行，第j列的元素。</div><div>所以b是指针的指针，b[i]是指针，这儿，b[i]类似于一维中的a。</div><div>那么&amp;b呢？&amp;b仍然是数组的首地址，但是跟一维类似的是，这是个纯粹的地址，不再具有指针的特性，它的+1就是数学上的+1，不可以利用+1来访问下一个元素。同样的道理对于&amp;b[i],&amp;运算符加上之后，本来是作为指针的b[i]被剥夺的指针的资格，返回一个纯粹的地址。</div><div>实际上，由于[]本质上是对指针的解引用，那么我们访问数组元素时可以不拘于a[i][j]这种方式，可以这样：(*(a+i))[j], 或者*(a[i]+j),或者 *(*(a+i)+j),这几种写法是等价的。</div><div>对于&amp;b[i][j]呢？我们把b[i][j]换一种写法，写成*(*(b+i)+j),这样问题就容易看清楚了，原来的*b[i][j]就等价于&amp;(*(*(b+i)+j)),我们可以把最外层的括号脱掉，就成了*(b+i)+j,即b[i]+j，显然这是一个指针，指向第i行，第j列元素的指针，对该指针的解释是一次跨越一个int型的数据。</div><div> </div><div>让我们再变态一点，考虑三维的情形，虽然三维的数组不多见，还是考虑一下吧，毕竟空间的坐标是用三维表示的。</div><div>int c[2][3][4] = {{{1,2,3,4},{5,6,7,8},{9,10,11,12}}, {{13,14,15,16},{17,18,19,20},{21,22,23,24}}};</div><div>首先，数组名c，编译器将c解释为一个指针，指向数组的首地址，由于行序是主序，所以，该指针一步跨越12个整型数，共48个字节，实际上即跨越了一个二维数组。</div><div>对于&amp;c,跟一维二维的情形类似，是一个纯粹的地址.</div><div>c[i]呢？可以推测，c[i]与二维中的b类似，即指向指针的指针，c[i]一步跨越4个整数，16个字节。c[i]是指向指针的指针，那么c便是指向指向指针的指针的指针（晕～）。</div><div>c[i]亦等价于*(c+i)</div><div>至于c[i][j],这才是真正的int型的指针，即指向真实数据的指针，一步跨越一个int型，4个字节。跟二维类似，对于&amp;c[i][j]，编译器返回一个地址，虽然跟c[i][j]的值一样，但是只是一个纯粹的地址，跨越单元为一个字节。</div><div>对于c[i][j][k],不需要废话，对于&amp;c[i][j][k],这是一个地址吗？这是一个指针吗？我们还是要借助[]的另一种表示方法：c[i][j][k]等价于*(*(*(c+i)+j)+k)，那么&amp;c[i][j][k]就等价于*(*(c+i)+j)+k，即c[i][j]+k,即指向第（i，j，k）个元素的指针，一步跨越单元为一个int型。</div><div>让我们来看一看，寻找第（i，j，k）个元素有哪些写法：</div><div>1、c[i][j][k]</div><div>2、*(c[i][j]+k)</div><div>3、*(*(c[i]+j)+k)</div><div>4、*(*(*(c+i)+j)+k) </div><div>5、(*(c+i))[j][k] </div><div>6、(*(*(c+i)+j))[k] </div><div>7、*((*(c+i))[j]+k) </div><div>8、(*(c[i]+j))[k]</div><div>可见，共八种写法，实际上就是一共有三个解引用，选择用[]还是用*，这样，总共有8个组合。（那么二维的就是4种，一维的2种）</div><div> </div><div><b>7</b><b>、typedef</b><b>与指针</b></div><div>typedef似乎很简单，如typedef int integer；然而，这些简单的typedef语句容易让人产生一种误解，typedef就是一种宏替换,把后面的自定义类型替换成前面的已知类型，事实是这样的吗?显然不是！</div><div>考虑这样的问题：如何定义一个指向整型的指针类型？如何定义一个函数指针类型？</div><div>第一个问题很简单：typedef int* int_pointer;即可，对于第二个问题，似乎就没有那么简单了，首先，看函数指针的定义方法：int (*p)(const&amp;, int); 这个p指向的函数必须返回int，形参必须是const&amp;和int。现在要将这种指针类型命名为func_pointer，其定义的方法如下：</div><div>typedef int (*func_pointer)(const&amp;, int);</div><div>可以这样来理解:typedef int integer;将typedef去掉，那就是个变量的定义，这儿即定义了一个int型的变量integer，考虑这个integer是什么类型的，那么这个typedef语句就是将integer定义为这个类型的。将typedef int (*func_pointer)(const&amp;, int);中的typedef去掉，就成了一个函数指针定义，即func_pointer被定义为函数指针类型变量，那么原来的typedef即将func_pointer定义为函数指针类型。</div><div> </div><div><b>8</b><b>、函数，数组与指针</b></div><div>int (*testCases[10])();</div><div>这个表达式是什么意思？指针，数组，函数糅合在了一起问题变得复杂起来。它定义了数组，testCases[10],数组中的元素是函数指针，函数指针的类型是 int (*)();</div><div>怎么来理解这种定义呢？首先考虑数组的定义，数组的定义一般模式是：</div><div>类型 数组名[大小];</div><div>考虑这个表达式，似乎是定义了一个数组，但是数组名[大小]被夹在了中间，那么类型是什么呢，发现类型并不是简单的数据类型，而是一个函数指针类型int (*p)()，这个函数没有参数，返回int型。从而这个表达式的含义是：定义了一个函数指针型的数组，大小是10。</div><div>可以利用typedef来简化这种定义：</div><div>typedef int (*PFV)();</div><div>PFV testCases[10];</div><div> </div><div>其实int (*testCases[10])();这儿我们定义了一个函数指针数组，数组是主体。</div><div>下面考虑这样的问题：如何定义一个指向数组的指针？</div><div>指向数组的指针，好像比较新鲜，所谓指向数组的指针，即指针的一步跨越是一个数组，跟指向整型的指针一步跨越一个整型一个道理。事实上前面已经碰到了指向数组的指针，如二维数组名，实际上就是一个指向数组的指针，它一次跨越一行的数据，实际上即是跨越了一个一维数组，而三维数组名呢，也是一个指向数组的指针，它一次跨越的是低维组成的一个二维数组。</div><div>数组指针(即指向数组的指针)的定义: </div><div>int (*ptr)[3];  这个表达式定义了一个数组指针ptr，ptr一次跨越一个由3个int型组成的一维数组。发现其定义的方式与函数指针定义的方式很相似，只是把()换作了[]。</div><div>更进一步，如果要定义一个指向数组的指针，而数组中的元素不是简单的int型，而是比较复杂的类型，那该如何定义呢？事实上数组指针这种东西就已经够稀有的了，一般编程绝对不会用到，我们只需要能读懂一些比较复杂的东西就行了，自己没有必要构造这么复杂的类型。</div><div> </div><div>比较复杂的表达式：</div><div>      1、int (*(*(*p())[])())[];</div><div>首先，根据p()判断p是一个函数，再根据p()前面的*号判断该函数返回一个指针，下面就看这个指针指向的是什么类新了，我们可以把*p()替换成一个*pointer，这个pointer就是函数p返回的指针，那么就成了int (*(*(*pointer)[])())[];再根据(*pointer)[]，这说明了指针pointer是指向的一个数组，那么这个数组中的元素是什么类型呢？由于数组名实际上就是个指针，我们把(*pointer)[]（即(*p())[]）替换成一个array，这样就成了 int (*(*array)())[];发现array是一个函数指针，从而数组中的每个元素是函数指针，而这个函数呢，又返回一个指针类型，把(*array)()用func代替，就成了int (*func)[];这说明了func函数返回的是指向数组的指针，数组中的元素是int型。</div><div>这个表达式够酷!!!</div><div>2、p = (int( * (*)[20])[10])q;</div><div>这是一个强制类型转换，q被强制类型转换成一个这样的指针类型，这个指针呢直线一个具有20个元素的数组，这个数组中的元素也是指针，是指向另外一种数组，这种数组是含有10个int型数据的一维数组。</div><div> </div><div>可见，分析复杂的表达式时（所谓复杂，即糅合了指针，数组，函数三样，缺少了一样就不会复杂了），从括号的最里层做起，最里层的东西是复杂表达式的“根节点”，然后一层一层脱，脱的时候，是这样的，比如里层是个数组，那么就是说这个数组的元素是什么呢，那就是外层的东西，如果里层是个有返回值的函数，那么就是说这个函数返回什么值呢？那就是外层的东西，就这样一层一层地把表达式解析清楚。</div><div> </div><div> </div><div>关于typedef还有一些要说的：</div><div>typedef  int (*PFV)(); 这是定义了一个函数指针，那么PFV p；就可以定义了一个指向函数的指针。</div><div>typedef int (*p[10])(); 这是把p定义为函数指针数组，那么 p array;语句就可以定义了一个函数指针数组，数组名即为array，array这个数组含10个元素。</div><div>typedef int (*parray)[3];这是定义了一个指向整型数组的指针，那么 parray ptr；就定义了一个指向数组的指针。如何对这个ptr赋值或者初始化呢？事实上，是通过二维数组名来对其进行赋值（初始化）的，因为二维数组名作为指针来讲，就是一个指向数组的指针，一次跨越一个数组。</div><div>typedef int a[3][3]; 这个语句什么意思呢？这是把a定义为一个3*3的整型数组类型。当a b = {1}时就完成了一个3×3的整型数组的定义初始化的工作。</div><div>同样，简单一点 typedef int a[3];这个语句是把a定义为一个一维数组类型。</div><div>typedef  void func(int); 这个语句定义了一个函数类型。通过这个typedef，我们可以比较清晰地定义出函数指针，func* p；即可。</div><div> </div><div>typedef char* string;</div><div>const string str;</div><div>这个str是什么类型的呢？const char * str，即指向常量的指针类型？事实上，答案有些不可思议，str是一个常量指针，而不是指针常量，即const修饰符针对的是指针，而不是char。 </div><div> </div><div><b>9</b><b>、引用与指针</b></div><div>引用类似与指针常量，只可初始化，不可赋值。别名（alias）是引用（reference）的另一种叫法。通过引用可以间接地操操纵对象。</div><div>常量引用，即类似与指向常量的常量指针，对常量引用的初始化，有一点特殊，可以用常量，变量，甚至是常数对其进行初始化。</div><div>对于用变量初始化常量引用，那么不能通过这个引用修改这个变量，但是本来的变量名可以。这一点，类似变量地址赋给常量指针。通过常量指针不可以修改变量，但是变量自身的变量名可以。</div><div>可以有指针的引用，如 int a = 1; int* p = &amp;a; int* &amp;r = p; 那么r就成了指针p的引用。如果是const int* p = &amp;a;说明是常量指针，那么定义引用的时候，就要如此定义，const int* &amp;r = p;这个语句说明r是一个指针的引用，这个指针是个指向常量的指针变量，而并不意味着这个引用是个常量引用。那如果说这个指针不仅仅是常量指针，而且是个指针常量，即const int* const p;那么定义引用时要注意应该const int* const &amp;r = p;这样表明该引用是个常量引用。这里有一个问题当用一个变量的地址初始化引用时如，int a = 22; int* const &amp;pi_ref = &amp;a；需要注意应该定以为常量引用，因为&amp;a不是变量名，而是类似常数。而若const int a = 22;则应该const int* const &amp;pi_ref = &amp;a;即中间的const是用来定义常量引用的，而前面的const反映的是引用指向的对象（指针）是指向的的const对象。</div><div> </div><div>我们有对象名，或者对象的指针，这些都可以操纵对象，为何要引入引用的概念呢？</div><div>事实上，引用最常用的是用作函数的形参。要在函数中操纵一个外部对象的时候，利用引用是一个好办法。</div><div> </div><div>关于引用</div><div>首先，引用只可初始化，不可被赋值，因为，被初始化后的引用，就成了被引用的对象的别名，再行赋值，就不是对引用本身的赋值了，而是对所引用的对象的赋值了。</div></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13311.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:13 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13311.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>指针专题</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13312.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:13:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13312.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13312.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13312.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13312.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13312.html</trackback:ping><description><![CDATA[
		<font color="#000080">指针专题<br /><br /><font color="#000000">一、<strong>数组的指针、指针数组以及指向指针的指针</strong></font><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></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13312.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:13 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13312.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于函数指针和类型</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13309.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:12:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13309.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13309.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13309.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13309.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13309.html</trackback:ping><description><![CDATA[
		<font color="#000080">关于函数指针和类型<br /><br /><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">关于函数指针和类型</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><?XML:NAMESPACE PREFIX = O /?><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">语言本质上由两部分组成：数据和代码。编程语言主要关注于操作数据，它可以对数据进行最精细的调整，但一般并不关心生成什么样的代码。事实上代码的具体形式是由编译器控制的，编程语言只要求完成相应的功能。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">数据分数据类型和数据实例，前者是数据的格式，后者是数据在内存中的实体。当我们定义了一个数据类型时，同时也自动生成了一个相应的指针类型，所以没有必要再显式的声明一个指针类型。而对于函数类型则并非如此。首先函数类型的数据类型的一个显著不同是：同一数据类型不同实例可以有不同的数据值，每一个实例的大小是确定的；而同一函数类型的不同实例是不同函数代码，它的大小是不确定的，甚至，</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">并不关心函数的大小，编译器自动生成具体的代码。我们可以一下子声明一系列同一类型的数据（比如数组声明），然后再对实例进行操作。但我们无法一下子声明一系列的函数实例，必须对每一个函数实例一一定义。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">的指针类型大小是固定的。指针就象实例对象的名字，每一个实例都有一个唯一的名字，这个名字其实就是实例在内存中的位置。因为对计算机而言，内存大小是有限制的，从而可以生成的实例数量也是有限制的，那么它们的名字是可以用一个固定长度的整数来表示的（这个值不会大于内存的最大值）。函数指针也不例外，它也是函数在内存中的位置，具有相同的内存长度。一般来说，我们调用的函数名就是这个函数实例的指针。我们可以声明一个函数的指针类型，它是由函数的参数和返回值构成的，也就是说，只要函数的参数和返回值相同，就可以看作相同类型的函数（这里先不考虑调用方式的区别）。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">如果函数指针是函数的指针类型的实例，那么什么是函数的类型呢。本质上</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">没有函数类型的概念，我们常说的函数类型是指函数的指针类型。但是一方面为了和数据类型保持一致，更重要的是实际的需要，我们确实应该定义函数的类型。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">在提及函数类型的一个应用之前，我们不得不讨论一下类的成员函数。类（</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">class</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">或</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">struct</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">）本质上是一个数据类型，它的实例是一系列特定格式的数据。而它的成员函数并不纳入类的大小尺度的计算。因为不管是静态函数还是成员函数，它们都在内存中有固定的位置，这个位置和类的实例没有任何的关系。类的成员函数和普通的函数也没有任何的不同。当然，这是对编译器而言，对程序员来说，并非如此。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">语言中调用一个类的成员函数，意味着这个函数可以访问这个实例的成员变量，而且不用显式的在参数中传递这个实例的指针。但实际上，对编译器而言，调用一个类的成员函数，就是调用一个普通的函数，只不过多传递了一个实例指针。正如</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">a.Func()</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">和</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Func(&amp;a)</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">这两种形式，只不过书写形式不同而已（</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Func</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">是一个普通函数，但比</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">a.Func</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">多一个参数）。换言之，即使</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">没有成员函数的概念，我们几乎可以编出同样功能的程序，我们可以把一个类的成员函数以类名为前缀（容易识别），声明为一系列的普通函数，这些函数的第一个参数都是一个对应类的指针类型。当我们需要调用类的成员函数时，只要调用特定的函数，并且传递实例的指针作为第一个参数。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">当然</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类的成员函数有它的方便之处，这就是我们使用它的原因。而且</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">有类的成员函数指针的概念，但就</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">本身来说，简直是多此一举。下面是一个类的成员函数的类型声明：</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">typedef int (A::FUNC)(int);<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">FUNC</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">是</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">A</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类的一个成员函数的指针类型，那么什么函数的指针可以是这个类型呢？首先它必须是</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">A</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">的成员函数（这正是问题的所在），然后它必须是一个有一个</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">int</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类型参数且返回</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">int</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类型的函数。假设</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Func</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">是一个满足此条件的函数，</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">FUNC func = A::Func</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">；</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">在</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">VC 8.0</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">中要求用以下格式</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">:<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">FUNC func = &amp;A::Func</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">；</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">如何调用</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">func</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">呢？首先既然是类的成员函数，一定要指明是哪个实例在调用它，</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">A a;<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">A* pa = &amp;a;<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">int i = (pa-&gt;*func)(3);<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">也就是说必须有类</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">A</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">的一个实例指针</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">pa</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">，但是，我们既然有</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">pa</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">，为什么不用</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">(int i = pa-&gt;Func(3))</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">这种形式呢？</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">如果仅限于此，我想类的成员指针是没有多大用处的。当然，</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">允许对成员函数指针进行转换，这种转换必须是在成员函数指针之间，不能转换为普通函数的指针。如果我们使用类的成员函数指针，大多是因为调用者并不知道这个指针是哪个类型的，但是知道它的参数和返回值，并且有一个实例的指针。事实上，对于函数调用来说这就足够了。但是</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">不这么认为，它一定要知道那个实例指针的类型，这是有原因的，指针的类型直接控制成员变量的访问方式。成员函数在执行时，可能会访问实例的数据成员，而这种访问是由实例指针传递的，如果传递了一个不正确的指针，就可能造成内存访问错误。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">模拟</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Windows</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">回调函数的机制，假设一个类</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Control</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">需要把事件通知外部的一个</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">A</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类。而且这个类</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">(Control)</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">是封装好了的，它要通知的类的类型是不确定的。我们不能在实际使用时，再回头更改代码。一种解决方案是声明一个通用类</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Common</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">，它没有数据成员，甚至不必实例化，但是它有特定的成员函数正好符合要通知的信息的格式，但这些成员函数没有必要有代码，总之一切只是为了应对</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">的类型检查，我们只是使用这个类的类型，不使用它的任何实际的东西。在封装类</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Control</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">的内部声明一个</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Common</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">指针</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">pc</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">，再声明一个</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Common</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">的成员函数指针</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">cf</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">。这样，只要把要接收信息的类的指针传递给</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">pc</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">，再把相应格式的成员函数指针传递给</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">cf</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">，</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">Control</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类只要调用</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">(pc-&gt;*cf)(param)</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">，就可以把信息传递给任何外部的类。对于静态函数，可以不必给</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">pc</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">赋值（或赋任何一个值），但</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">不允许把一个静态函数的指针传递给</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">cf</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">，但是可以用一些技巧性的转换。实际情况比这要复杂，还要考虑虚函数，虚继承等。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">这个解决方案还有一个重要的问题，就是如果</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">pc</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">和</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">cf</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">传递了错误的值，后果会非常严重。这有两方面：</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">cf</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">并不是</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">pc</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">的成员，</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">cf</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">不是一个对应类型的函数。以下是这个问题的一个解决方案：</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">template&lt;typename R,typename T&gt; void SetFuncPointer(R* pr,T func)<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">{<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><span style="mso-tab-count: 2">     </span>typedef void (R::*FUNC)(TYPE1,TYPE2);<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><span style="mso-tab-count: 2">     </span>FUNC Func = func;<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><span style="mso-tab-count: 2">     </span>………….<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">}<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">当</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">func</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">不是</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">FUNC</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类型，或不是</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">R</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类型</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">(</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">这个类型由</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">pr</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">参数确定</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">)</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">的成员函数时，这个函数一定会编译报错。而且省去了类型转换的麻烦。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">但是在</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">FUNC</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">定义的那一行，对于不同函数，是不同的。我们希望有一个更通用的定义方式，把这个定义抽象化，公式化，而无须每一种函数都不得不写下定义式。比如以下的形式：</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">typedef R::*FUNCTYPE FT;(C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">中并没有这个语法</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">)<o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">其中</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">FUNCTYPE</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">是代表函数参数和返回类型的标识，</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">FT</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">是与之对应的类的成员函数指针定义（格式相同的函数，但是是成员函数）。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">事实上，是否定义函数的类型概念并不是根本的，对函数而言，指针类型完全可以代表函数的类型。数据需要数据类型和指针类型是因为我们需要定义相应类型的实例，而对函数来说，它的实例定义本身也就定义了一种隐含的类型（并不需要先有类型才可以定义）。这一方面省去了定义类型的麻烦，但在使用它的类型时，又不得不翻回头来重新定义。所以为了统一和方便，还是加入函数类型概念比较好，以下是一个示例语法：</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">typedef FUNC (void,int);//</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">第一个参数表示返回类型。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">FUNC</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">就是定义的函数类型，它是一个返回</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">void</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类型，有一个</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">int</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">类型参数的函数。关于函数的返回类型，事实上并不规范。函数的返回值，本质上是一个</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">out</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">参数，也就是一个无须初始化，无须定义的参数。它与其它参数没有本质区别，它不需声明，是因为编译器只是把这个值存在</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">eax</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">寄存器中。函数没有返回值机制，仍然会工作的很好。不过鉴于函数的返回值是数学和其它领域的普遍规范，还是保留这种形式。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">允许函数的类型定义自然有一个问题。如果两个函数类型定义参数相同，那么它们是否是一种类型呢？现在的</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt">C++</span><span style="FONT-SIZE: 16pt; FONT-FAMILY: 新宋体; mso-ascii-font-family: 宋体; mso-hansi-font-family: 宋体; mso-font-kerning: 0pt">对于函数类型指针的规定是可以自由的赋值无须显示转换。这一点和数据类型是不一致的，对于数据结构完全相同的类，事实上是可以通用的，尽管这时，它们的成员的名称可能不同。出现这种规定是自然而然的，但不是必须的。至少它们是可以直接相互赋值的。</span><span lang="EN-US" style="FONT-SIZE: 16pt; FONT-FAMILY: 宋体; mso-fareast-font-family: 新宋体; mso-font-kerning: 0pt"><o:p></o:p></span></p></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13309.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:12 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13309.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>声明函数指针并实现回调</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13310.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:12:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13310.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13310.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13310.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13310.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13310.html</trackback:ping><description><![CDATA[
		<font color="#000080">声明函数指针并实现回调<br /><br /><p>程序员常常需要实现回调。本文将讨论函数指针的基本原则并说明如何使用函数指针实现回调。注意这里针对的是普通的函数，不包括完全依赖于不同语法和语义规则的类成员函数（类成员指针将在另文中讨论）。<br /><br /><!--jcstart--></p><p>声明函数指针<br /><br />   回调函数是一个程序员不能显式调用的函数；通过将回调函数的地址传给调用者从而实现调用。要实现回调，必须首先定义函数指针。尽管定义的语法有点不可思议，但如果你熟悉函数声明的一般方法，便会发现函数指针的声明与函数声明非常类似。请看下面的例子：<br /><br />void f()；// 函数原型<br /><br />上面的语句声明了一个函数，没有输入参数并返回void。那么函数指针的声明方法如下：<br /><br />void (*) ();<br /><br />   让我们来分析一下，左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型（void）和由边圆括弧中的入口参数（本例中参数是空）。注意本例中还没有创建指针变量-只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小：<br /><br />// 获得函数指针的大小<br />unsigned psize = sizeof (void (*) ()); <br /><br />// 为函数指针声明类型定义<br />typedef void (*pfv) ();<br /><br />pfv是一个函数指针，它指向的函数没有输入参数，返回类行为void。使用这个类型定义名可以隐藏复杂的函数指针语法。<br /><br />指针变量应该有一个变量名：<br /><br />void (*p) (); //p是指向某函数的指针<br /><br />   p是指向某函数的指针，该函数无输入参数，返回值的类型为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋值，值的内容是署名匹配的函数名和返回类型。例如：<br /><br />void func() <br />{<br />/* do something */<br />} <br />p = func; <br /><br />p的赋值可以不同，但一定要是函数的地址，并且署名和返回类型相同。<br /><br />传递回调函数的地址给调用者<br /><br />   现在可以将p传递给另一个函数（调用者）- caller()，它将调用p指向的函数，而此函数名是未知的：<br /><br />void caller(void(*ptr)())<br />{<br />ptr(); /* 调用ptr指向的函数 */ <br />}<br />void func();<br />int main()<br />{<br />p = func; <br />caller(p); /* 传递函数地址到调用者 */<br />}<br /><br />   如果赋了不同的值给p（不同函数地址），那么调用者将调用不同地址的函数。赋值可以发生在运行时，这样使你能实现动态绑定。<br /><br />调用规范<br /><br />   到目前为止，我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中，可以在函数类型前加_cdecl，_stdcall或者_pascal来表示其调用规范（默认为_cdecl）。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名，参数传递的顺序（从右到左或从左到右），堆栈清理责任（调用者或者被调用者）以及参数传递机制（堆栈，CPU寄存器等）。<br /><br />   将调用规范看成是函数类型的一部分是很重要的；不能用不兼容的调用规范将地址赋值给函数指针。例如：<br /><br />// 被调用函数是以int为参数，以int为返回值<br />__stdcall int callee(int); <br /><br />// 调用函数以函数指针为参数<br />void caller( __cdecl int(*ptr)(int)); <br /><br />// 在p中企图存储被调用函数地址的非法操作<br />__cdecl int(*p)(int) = callee; // 出错<br /><br /><br />   指针p和callee()的类型不兼容，因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p，尽管两者有相同的返回值和参数列。</p></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13310.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:12 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13310.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言中的三大定律[转]</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13307.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:08:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13307.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13307.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13307.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13307.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13307.html</trackback:ping><description><![CDATA[
		<font color="#000080">[转] C语言中的三大定律<br /><br /><p>自己总结的，觉得能帮助初学者看透一些纷繁复杂的语法规则，理解C语言的真谛<br />第一次发布，不一定正确，欢迎讨论、指正、补充</p><p><strong><font size="3">1. 表达式定律</font></strong></p><p>任何能产生数值结果的运算、操作都可以作为表达式，并可以放到任何需要数值结果的地方，只要数值类型能够匹配</p><p>常见的可以产生数值结果的运算和操作</p><ul><li>算术、逻辑、位运算等 
</li><li>? : 
</li><li>&amp;、*等操作 
</li><li>有返回值的函数</li></ul><p> 常见的需要数值的地方有：</p><ul><li>赋值 
</li><li>条件判断 
</li><li>函数调用</li></ul><p><strong><font size="4">2. 类型定律</font></strong></p><p>任何类型都可以在任何需要类型的地方使用；用任何类型定义的变量都要占用内存</p><p>已知特例</p><ul><li>函数返回值不能定义为数组类型</li></ul><p>常用类型</p><ul><li>基本数据类型、指针、数组、结构……</li></ul><p>常见的需要类型的地方</p><ul><li>定义变量 
</li><li>定义指针、数组和结构 
</li><li>函数参数和返回值 
</li><li>sizeof</li></ul><p><strong><font size="4">3. 参数传递定律</font></strong></p><p>函数调用时的参数传递永远都是传值调用，把实参的值拷贝给形参</p><ul><li>实参：调用者提供的参数 
</li><li>形参：函数定义的参数 
</li><li>基本数据类型无容置疑 
</li><li>struct也无容置疑 
</li><li>指针作为参数时，把指针变量的内容（就是其指向的内存地址）做了拷贝 
</li><li>数组名作为参数时，把它等同于指针看待了</li></ul></font>
<img src ="http://www.cppblog.com/stevennash/aggbug/13307.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:08 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13307.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>教你理解复杂的C/C++声明</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13306.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 19:07:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13306.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13306.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13306.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13306.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13306.html</trackback:ping><description><![CDATA[教你理解复杂的C/C++声明<br /><br />陆其明 译
<p>原文：<br /><a href="http://www.codeproject.com/cpp/complex_declarations.asp"><font color="#770000">http://www.codeproject.com/cpp/complex_declarations.asp</font></a><br />作者：Vikram A Punathambekar</p><p><br />介绍</p><p>曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗？本文将由易到难，一步一步教会你如何理解这种复杂的C/C++声明：我们将从每天都能碰到的较简单的声明入手，然后逐步加入const修饰符和typedef，还有函数指针，最后介绍一个能够让你准确地理解任何C/C++声明的“右左法则”。需要强调一下的是，复杂的C/C++声明并不是好的编程风格；我这里仅仅是教你如何去理解这些声明。注意：为了保证能够在同一行上显示代码和相关注释，本文最好在至少1024x768分辨率的显示器上阅读。</p><p> </p><p>基础</p><p>让我们从一个非常简单的例子开始，如下：</p><p>int n;</p><p>这个应该被理解为“declare n as an int”（n是一个int型的变量）。</p><p>接下去来看一下指针变量，如下：</p><p>int *p;</p><p>这个应该被理解为“declare p as an int *”（p是一个int *型的变量），或者说p是一个指向一个int型变量的指针。我想在这里展开讨论一下：我觉得在声明一个指针（或引用）类型的变量时，最好将*（或&amp;）写在紧靠变量之前，而不是紧跟基本类型之后。这样可以避免一些理解上的误区，比如：</p><p>int*   p,q;</p><p>第一眼看去，好像是p和q都是int*类型的，但事实上，只有p是一个指针，而q是一个最简单的int型变量。</p><p>我们还是继续我们前面的话题，再来看一个指针的指针的例子：</p><p>char **argv;</p><p>理论上，对于指针的级数没有限制，你可以定义一个浮点类型变量的指针的指针的指针的指针...</p><p>再来看如下的声明：</p><p>int RollNum[30][4];<br />int (*p)[4]=RollNum;<br />int *q[5];</p><p>这里，p被声明为一个指向一个4元素（int类型）数组的指针，而q被声明为一个包含5个元素（int类型的指针）的数组。</p><p>另外，我们还可以在同一个声明中混合实用*和&amp;，如下：</p><p>int **p1;  //  p1 is a pointer   to a pointer   to an int.<br />int *&amp;p2;  //  p2 is a reference to a pointer   to an int.<br />int &amp;*p3;  //  ERROR: Pointer    to a reference is illegal.<br />int &amp;&amp;p4;  //  ERROR: Reference  to a reference is illegal.</p><p>注：p1是一个int类型的指针的指针；p2是一个int类型的指针的引用；p3是一个int类型引用的指针（不合法！）；p4是一个int类型引用的引用（不合法！）。</p><p> </p><p>const修饰符</p><p>当你想阻止一个变量被改变，可能会用到const关键字。在你给一个变量加上const修饰符的同时，通常需要对它进行初始化，因为以后的任何时候你将没有机会再去改变它。例如：</p><p>const int n=5;<br />int const m=10;</p><p>上述两个变量n和m其实是同一种类型的——都是const int（整形恒量）。因为C++标准规定，const关键字放在类型或变量名之前等价的。我个人更喜欢第一种声明方式，因为它更突出了const修饰符的作用。</p><p>当const与指针一起使用时，容易让人感到迷惑。例如，我们来看一下下面的p和q的声明：</p><p>const int *p;<br />int const *q;</p><p>他们当中哪一个代表const int类型的指针（const直接修饰int），哪一个代表int类型的const指针（const直接修饰指针）？实际上，p和q都被声明为const int类型的指针。而int类型的const指针应该这样声明：</p><p>int * const r= &amp;n; // n has been declared as an int</p><p>这里，p和q都是指向const int类型的指针，也就是说，你在以后的程序里不能改变*p的值。而r是一个const指针，它在声明的时候被初始化指向变量n（即r=&amp;n;）之后，r的值将不再允许被改变（但*r的值可以改变）。</p><p>组合上述两种const修饰的情况，我们来声明一个指向const int类型的const指针，如下：</p><p>const int * const p=&amp;n // n has been declared as const int</p><p>下面给出的一些关于const的声明，将帮助你彻底理清const的用法。不过请注意，下面的一些声明是不能被编译通过的，因为他们需要在声明的同时进行初始化。为了简洁起见，我忽略了初始化部分；因为加入初始化代码的话，下面每个声明都将增加两行代码。</p><p>char ** p1;                    //        pointer to       pointer to       char<br />const char **p2;               //        pointer to       pointer to const char<br />char * const * p3;             //        pointer to const pointer to       char<br />const char * const * p4;       //        pointer to const pointer to const char<br />char ** const p5;              //  const pointer to       pointer to       char<br />const char ** const p6;        //  const pointer to       pointer to const char<br />char * const * const p7;       //  const pointer to const pointer to       char<br />const char * const * const p8; //  const pointer to const pointer to const char</p><p>注：p1是指向char类型的指针的指针；p2是指向const char类型的指针的指针；p3是指向char类型的const指针；p4是指向const char类型的const指针；p5是指向char类型的指针的const指针；p6是指向const char类型的指针的const指针；p7是指向char类型const指针的const指针；p8是指向const char类型的const指针的const指针。</p><p> </p><p>typedef的妙用</p><p>typedef给你一种方式来克服“*只适合于变量而不适合于类型”的弊端。你可以如下使用typedef：</p><p>typedef char * PCHAR;<br />PCHAR p,q;</p><p>这里的p和q都被声明为指针。（如果不使用typedef，q将被声明为一个char变量，这跟我们的第一眼感觉不太一致！）下面有一些使用typedef的声明，并且给出了解释：</p><p>typedef char * a;  // a is a pointer to a char</p><p>typedef a b();     // b is a function that returns<br />                   // a pointer to a char</p><p>typedef b *c;      // c is a pointer to a function<br />                   // that returns a pointer to a char</p><p>typedef c d();     // d is a function returning<br />                   // a pointer to a function<br />                   // that returns a pointer to a char</p><p>typedef d *e;      // e is a pointer to a function <br />                   // returning  a pointer to a <br />                   // function that returns a <br />                   // pointer to a char</p><p>e var[10];         // var is an array of 10 pointers to <br />                   // functions returning pointers to <br />                   // functions returning pointers to chars.</p><p>typedef经常用在一个结构声明之前，如下。这样，当创建结构变量的时候，允许你不使用关键字struct（在C中，创建结构变量时要求使用struct关键字，如struct tagPOINT a；而在C++中，struct可以忽略，如tagPOINT b）。</p><p>typedef struct tagPOINT<br />{<br />    int x;<br />    int y;<br />}POINT;</p><p>POINT p; /* Valid C code */</p><p> </p><p>函数指针</p><p>函数指针可能是最容易引起理解上的困惑的声明。函数指针在DOS时代写TSR程序时用得最多；在Win32和X-Windows时代，他们被用在需要回调函数的场合。当然，还有其它很多地方需要用到函数指针：虚函数表，STL中的一些模板，Win NT/2K/XP系统服务等。让我们来看一个函数指针的简单例子：</p><p>int (*p)(char);</p><p>这里p被声明为一个函数指针，这个函数带一个char类型的参数，并且有一个int类型的返回值。另外，带有两个float类型参数、返回值是char类型的指针的指针的函数指针可以声明如下：</p><p>char ** (*p)(float, float);</p><p>那么，带两个char类型的const指针参数、无返回值的函数指针又该如何声明呢？参考如下：</p><p>void * (*a[5])(char * const, char * const);</p><p> </p><p>“右左法则”[重要！！！]</p><p>The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.</p><p>这是一个简单的法则，但能让你准确理解所有的声明。这个法则运用如下：从最内部的括号开始阅读声明，向右看，然后向左看。当你碰到一个括号时就调转阅读的方向。括号内的所有内容都分析完毕就跳出括号的范围。这样继续，直到整个声明都被分析完毕。</p><p>对上述“右左法则”做一个小小的修正：当你第一次开始阅读声明的时候，你必须从变量名开始，而不是从最内部的括号。</p><p>下面结合例子来演示一下“右左法则”的使用。</p><p>int * (* (*fp1) (int) ) [10];</p><p>阅读步骤：<br />1. 从变量名开始 -------------------------------------------- fp1<br />2. 往右看，什么也没有，碰到了)，因此往左看，碰到一个* ------ 一个指针<br />3. 跳出括号，碰到了(int) ----------------------------------- 一个带一个int参数的函数<br />4. 向左看，发现一个* --------------------------------------- （函数）返回一个指针<br />5. 跳出括号，向右看，碰到[10] ------------------------------ 一个10元素的数组<br />6. 向左看，发现一个* --------------------------------------- 指针<br />7. 向左看，发现int ----------------------------------------- int类型</p><p>总结：fp1被声明成为一个函数的指针的指针的数组，这个数组有10个元素，函数的原型为带一个int类型的参数，返回值为一个指针？</p><p>再来看一个例子：</p><p>int *( *( *arr[5])())();</p><p>阅读步骤：<br />1. 从变量名开始 -------------------------------------------- arr<br />2. 往右看，发现是一个数组 ---------------------------------- 一个5元素的数组<br />3. 向左看，发现一个* --------------------------------------- 指针<br />4. 跳出括号，向右看，发现() -------------------------------- 不带参数的函数<br />5. 向左看，碰到* ------------------------------------------- （函数）返回一个指针<br />6. 跳出括号，向右发现() ------------------------------------ 不带参数的函数<br />7. 向左，发现* --------------------------------------------- （函数）返回一个指针<br />8. 继续向左，发现int --------------------------------------- int类型</p><p>总结：？？</p><p><br />还有更多的例子：</p><p>float ( * ( *b()) [] )();              // b is a function that returns a <br />                                       // pointer to an array of pointers<br />                                       // to functions returning floats.</p><p>void * ( *c) ( char, int (*)());       // c is a pointer to a function that takes<br />                                       // two parameters:<br />                                       //     a char and a pointer to a<br />                                       //     function that takes no<br />                                       //     parameters and returns<br />                                       //     an int<br />                                       // and returns a pointer to void.</p><p>void ** (*d) (int &amp;, <br />  char **(*)(char *, char **));        // d is a pointer to a function that takes<br />                                       // two parameters:<br />                                       //     a reference to an int and a pointer<br />                                       //     to a function that takes two parameters:<br />                                       //        a pointer to a char and a pointer<br />                                       //        to a pointer to a char<br />                                       //     and returns a pointer to a pointer <br />                                       //     to a char<br />                                       // and returns a pointer to a pointer to void</p><p>float ( * ( * e[10]) <br />    (int &amp;) ) [5];                    // e is an array of 10 pointers to <br />                                       // functions that take a single<br />                                       // reference to an int as an argument <br />                                       // and return pointers to<br />                                       // an array of 5 floats.</p><img src ="http://www.cppblog.com/stevennash/aggbug/13306.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 03:07 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13306.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编程方法很重要</title><link>http://www.cppblog.com/stevennash/archive/2006/10/04/13297.html</link><dc:creator>Technical Consultant</dc:creator><author>Technical Consultant</author><pubDate>Tue, 03 Oct 2006 18:17:00 GMT</pubDate><guid>http://www.cppblog.com/stevennash/archive/2006/10/04/13297.html</guid><wfw:comment>http://www.cppblog.com/stevennash/comments/13297.html</wfw:comment><comments>http://www.cppblog.com/stevennash/archive/2006/10/04/13297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/stevennash/comments/commentRss/13297.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/stevennash/services/trackbacks/13297.html</trackback:ping><description><![CDATA[
		<p>
				<font color="#800080">编程方法很重要<br /><br />看了网上面编程高手写的一些如何学习编程的文章,体会到学习编程,重要的是学习编程的方法;</font>
		</p>
		<p>掌握了方法之后,对与一门新语言的学习,就只剩下该语言区别于你所熟悉的语言的一些特殊语法和这门语言的一些特殊机制而已.<br /><br /><br />一、<font color="#0033dd">英语对于编程重要吗？<br /><br /></font></p>
		<p>有些人问：英语对于编程重要吗？那你就看看下面的例子就知道了： </p>
		<pre>6d61696e(){<br />	7072696e7466("你好啊，世界！");<br />}<br /></pre>
		<p>好吧，这是一个C语言Hello world! 但它不能用TC或者BC等编绎器编绎----会出错。为了能把它编绎，你再请一个“用英语”编程的人帮你编个程序，将上面的程序转为TC和BC可以认识的 源代码。转换过程很简单，因为在ASCII码中，m=6d, a=61, n=6e, p=70, r=72, i=69, t=74, f=66， 以上都是十六进制数值。 </p>
		<p>这种“程序”，就算你不会英语也会编出来吧？就算你不会英语也不会妨碍你成为高手吧？因为所有人都能轻易学会0-9和a-e。 </p>
		<p>我曾经看到有人用中文“编写”C语言程序----把main换为“主函数”，把int换为“整型”，把printf换为“打印”......，就是： </p>
		<pre>主函数(){<br />	打印("你好啊，世界！");<br />}<br /></pre>
		<p>我认为这种方法不比我的方法好，因为别人还得学习汉字。我的方法即使是中文文盲也能很容易学习。 </p>
		<p>我的意思是，中文编程不是简单的将英文单词“一一对应”地翻译为中文名词就行了，而应该是发明出一种符合“汉语思维”的编程方法。这种“一一对应” 的中文编程只不过是一种无聊的作法。在各种编程语言的函数库和类库中，都是以英文词汇（或者26个英文字母，数字，下划线的组合）命名，当然你也可以把它 们全都翻译为表示中文意思的中文词汇，或者按照我所说的方法翻译，然后就可以“使用中文（或者不使用英语）编程”了，可是，这有什么意义？ </p>
		<p>所以，结论是：英语对编程很重要。 <br /><br />二、<font color="#000080">编程语言和编程思想<br /><br /></font></p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<font size="4">
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这么多年来，我学过和使用过的开发语言也不少了，可真正用得好一点的就只有</span>
						<span lang="EN">C</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。有一段话很流行，直到现在类似的想法还影响了很多人。“做程序员</span>
						<span lang="EN">,</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">学习程序语言最重要的是学习编程思想</span>
						<span lang="EN">,</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">而不是学习那种语言”。我曾经把这段话奉为经典。在做程序员的初期，频繁更换编程语言，这是一个重要原因，另一个重要原因是，如果不会新的语言，就显得落后了。因此流行的语言基本都涉及过，买书的钱都以千计。随着代码数量的增加，经验的增长以及对于业务了解程度的深入，自然产生了一些想法、思路和设计。</span>
				</font>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<font size="4">
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">我举的一个例子，开发过数据库的人大概都有所体会。数据库应用中常常需要构造</span>
						<span lang="EN">SQL</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">语句，但在程序中写</span>
						<span lang="EN">SQL</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">语言以及对返回结果集的处理，这往往是个体力活，特别是在嵌入式</span>
						<span lang="EN">SQL</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的编程中。重复地拷贝一段颇长代码，再修改一点点，整个程序大量的篇幅都耗在这上面了，维护起来也比较困难。我想很多人都针对此想过了很多的方法来简化这些甚是无趣的操作。我也想过好些方法，其中一个方法比较有趣。在</span>
						<span lang="EN">C</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">语言中有一个很常用的函数是</span>
						<span lang="EN">int sscanf(const char *str, const char *format, ...)</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，用起来颇为简便。我想仿照这个函数的来处理</span>
						<span lang="EN">SQL</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">语言的</span>
						<span lang="EN">select</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，因此就定义了一个获得一条记录的函数：</span>
						<span lang="EN">ifx_select(const char* sql,…); </span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用起来非常简便：</span>
				</font>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<span lang="EN">
						<font size="4">int id;</font>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<span lang="EN">
						<font size="4">char name[16];</font>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<span lang="EN">
						<font size="4">…</font>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<span lang="EN">
						<font size="4">if(ifx_select(“select id,name from users where id=10”,&amp;id,name)==0)</font>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<span lang="EN">
						<font size="4">…</font>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">
						<font size="4">这是一个很好的点子，也是很好的编程思想。但开工后，我发现我实现不了。虽然这段代码看上去很简单，但它包含的东西却不少：变参数的处理，指针及其内容的赋值，错误处理，数据类型判断与分析等等。这些东东都学过，但在实际工作中却不能灵活运用，想法和思路自然实现不了。类似的现象不仅仅大量出现在程序设计中，在现实生活中也比比皆是。</font>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<font size="4">
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">由此，下决心好好补了补课。把教科书又翻了几遍、阅读了很多开放源码的程序、在平时的编程中投入一部分精力到</span>
						<span lang="EN">C</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">本身的研究中，经常感叹，原来</span>
						<span lang="EN">C</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">语言还可以这样用。随着对语言本身（语法、技巧、组合等等）的越来越熟悉，自己的很多思路和想法也得以顺利实现。到目前语言和思想已经密不可分了，以上的代码自然搞定了。当然这是一个长期的过程，不可一蹴而就。时间一般以年计算。</span>
				</font>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<font size="4">
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">讲了这么多，我的观点也很明确，编程的语言和编程的思想同样重要，扎实的编程语言功底是编程思想得以顺利的实现的基础，二者缺一不可，相互制约也相互促进。就象人的灵魂和肉体。对于一个人来说，若只管灵魂而不管肉体，那是“鬼”；如只管肉体而不管灵魂，则会被称为“行尸走肉”。基本工具都用得不好，用得不熟，很难期待会出现一个好产品；有一个好的编程想法，却因为编程语言的能力不够而不能实现，那么这个好的想法就只是“空想”，开发者容易被划归到“眼高手低”、“不切实际”、“不踏实”的一类中去。</span>
				</font>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt">
				<font size="4">
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">另外在选择和学习编程语言上，最好根据需要选择一门主流的、应用广泛又有强大生命力的编程语言，例如</span>
						<span lang="EN">BASIC</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
						<span lang="EN">FORTRAN</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
						<span lang="EN">C/C++</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">、</span>
						<span lang="EN">JAVA</span>
						<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">等。语法学习相对容易，关键在于熟练应用。理想境界是达到卖油翁的程度：“无它，唯手熟而”。<br /><br />三、<font color="#660066" size="3"><strong>编程高手谈编程 <br /><br /></strong><span style="FONT-FAMILY: 宋体">编程并非高深莫测</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /><br /></span><span style="FONT-FAMILY: 宋体">　　李晓东</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"> e</span><span style="FONT-FAMILY: 宋体">－</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">BOOK </span><span style="FONT-FAMILY: 宋体">电子小说阅读器作者</span><span style="FONT-FAMILY: ˎ̥"></span><span style="FONT-FAMILY: 宋体">（</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">http://eb.126.com)<br /></span><span style="FONT-FAMILY: 宋体">　　要想学好编程，没有什么捷径可走，只有多动手，敢于动手，看完一本厚厚的编程书，学习效果也肯定比不上亲自上机设计一个简单的程序。就拿我来说吧，其实我很早就想学</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VB</span><span style="FONT-FAMILY: 宋体">了，在我的电脑中也多次安装过</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VB4</span><span style="FONT-FAMILY: 宋体">和</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VB5</span><span style="FONT-FAMILY: 宋体">，可我一直没有动手，也许是因为缺乏某种紧迫感吧。直到去年</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">10</span><span style="FONT-FAMILY: 宋体">月份，我在用</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VFP</span><span style="FONT-FAMILY: 宋体">设计一个文本阅读器（</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">e</span><span style="FONT-FAMILY: 宋体">－</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">BOOK</span><span style="FONT-FAMILY: 宋体">的前身）时，越来越感到</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VFP</span><span style="FONT-FAMILY: 宋体">无法满足我的一些特殊要求，于是我又想到了</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VB</span><span style="FONT-FAMILY: 宋体">，终于，我捧起一本</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VB3</span><span style="FONT-FAMILY: 宋体">的编程手册（当时我只能找到这本</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VB</span><span style="FONT-FAMILY: 宋体">编程书了），在</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VB5</span><span style="FONT-FAMILY: 宋体">中敲下了第一个键。半个多月过后，</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">e</span><span style="FONT-FAMILY: 宋体">－</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">BOOK1.01</span><span style="FONT-FAMILY: 宋体">版诞生了！在此，我要对想学编程的朋友说一句：编程并非高深莫测，只要你敢于迈出关键的第一步，你就入门了。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　另外，编程书还是要看的，但不必强求最新、最全、最多，够用、适用就行。就我个人来说，我有汇编基础（当然也是自学的），这对我现在的编程有很大帮助（尽管在</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Windows</span><span style="FONT-FAMILY: 宋体">时代汇编语言已无用武之地）。对广大编程爱好者来说，则要系统地掌握一些底层的东西，比如显卡显示图像的原理、硬盘存储文件的方式、内存管理机制等，这些知识尽管不能直接用到程序中去，但对你理解程序的运行流程、找出发生问题的原因等方面还是很有好处的。我编程的长处在于界面设计（</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">e</span><span style="FONT-FAMILY: 宋体">－</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">BOOK</span><span style="FONT-FAMILY: 宋体">就是明证）。我觉得，在</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Windows</span><span style="FONT-FAMILY: 宋体">中编程，最容易体现个性的就是界面设计，在千篇一律的灰色窗口＋菜单栏＋工具条＋状态条＋滚动条的模式下，只要你敢于打破这个框框，你的程序就很容易脱颖而出（当然内在的东西也要足够好）。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">选择方便快捷的方案</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /><br /></span><span style="FONT-FAMILY: 宋体">　　杨延哲</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"> E</span><span style="FONT-FAMILY: 宋体">－</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">port</span><span style="FONT-FAMILY: 宋体">软件小组成员</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"> http://e</span><span style="FONT-FAMILY: 宋体">－</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">port.yeah.net<br /></span><span style="FONT-FAMILY: 宋体">　　我从学习编程到现在不多不少三年半，这期间学的东西很杂，首先是从</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">QBASIC</span><span style="FONT-FAMILY: 宋体">开始，后来学上了</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VisualBasic</span><span style="FONT-FAMILY: 宋体">，再后来学校里教了</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">TurboPascal</span><span style="FONT-FAMILY: 宋体">。现在我才认定了</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Pascal</span><span style="FONT-FAMILY: 宋体">，跟了</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Delphi</span><span style="FONT-FAMILY: 宋体">。其他的语言像</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">C</span><span style="FONT-FAMILY: 宋体">＋＋我也学过，但没人教再加上已经弄懂了</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Pascal</span><span style="FONT-FAMILY: 宋体">，所以就放弃了。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Delphi</span><span style="FONT-FAMILY: 宋体">编程的最大好处就是它的控件，它将编程变成了一件快乐的事。因为在编程过程中唯一的快感就是看到自己的程序在运作了，而</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Delphi</span><span style="FONT-FAMILY: 宋体">就给了编程者这个感觉。这对于我们这些写小程序的人来说特别有帮助，可以将编程时间降到最低。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　我的编程忠告就是：如果你遇到了大的问题，解决方法有许多种。为了避免走弯路，先选择最方便的快捷方案，这样即使无法执行也不会太浪费时间。用专业的讲法就是</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Shortest Job First</span><span style="FONT-FAMILY: 宋体">。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">创意是软件的灵魂</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /><br /></span><span style="FONT-FAMILY: 宋体">　　张研</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"> Update NOW!</span><span style="FONT-FAMILY: 宋体">的软件作者</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"> (http://nowsof.yeah.net </span><span style="FONT-FAMILY: 宋体">，</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">thttp://www.ourchina.net/)<br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">1.</span><span style="FONT-FAMILY: 宋体">基础比语言更重要</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　以前有一句很有名的话：</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">“</span><span style="FONT-FAMILY: 宋体">算法＋数据结构</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">=</span><span style="FONT-FAMILY: 宋体">程序</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">"</span><span style="FONT-FAMILY: 宋体">，后来有人批评这种说法，但不管怎样，算法和数据结构的重要性可见一斑。而语言则显得不那么重要了。语言只是算法的表达方式，就拿排序来说吧，</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">C</span><span style="FONT-FAMILY: 宋体">语言用的是那些方法，</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Pascal</span><span style="FONT-FAMILY: 宋体">也是，</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Java</span><span style="FONT-FAMILY: 宋体">也一样。只是表达的方法不同。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">“</span><span style="FONT-FAMILY: 宋体">万变不离其宗</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">"</span><span style="FONT-FAMILY: 宋体">，所以说要想成为一个好的程序员，算法和数据结构是最基础的。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">2.</span><span style="FONT-FAMILY: 宋体">精通一门适合自己的语言</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　现在流行的语言很多，</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Visual Basic</span><span style="FONT-FAMILY: 宋体">、</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Visual C</span><span style="FONT-FAMILY: 宋体">＋＋、</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">JAVA</span><span style="FONT-FAMILY: 宋体">、</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"> Delphi</span><span style="FONT-FAMILY: 宋体">等。同时新语言层出不穷。谁也不可能都会。怎么办</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">?</span><span style="FONT-FAMILY: 宋体">最好只选择一门语言，集中精力钻研，精通它。没有一门语言是万能的，有算法和你精通的那门语言做基础，学一门新的语言是件轻松的事。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">3.</span><span style="FONT-FAMILY: 宋体">熟悉你使用的操作系统</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　同一种语言在不同操作系统中，会略有不同。比如</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Windows </span><span style="FONT-FAMILY: 宋体">下的</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">C</span><span style="FONT-FAMILY: 宋体">和</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Unix </span><span style="FONT-FAMILY: 宋体">下的</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">C</span><span style="FONT-FAMILY: 宋体">是都是</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">C</span><span style="FONT-FAMILY: 宋体">语言的超集。这些不同，正是我们所关心和应该潜心研究的。只有这样才能写出有特色的程序。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">4.</span><span style="FONT-FAMILY: 宋体">熟悉你使用的硬件系统</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　不了解硬件系统，很难写出好的程序。所以好的程序员，对硬件的了解都很深。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">5.</span><span style="FONT-FAMILY: 宋体">容错是很重要的</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　容错性能的好坏是评价一个程序是否专业的重要标志。好的程序员会写大量的代码让程序更</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">“</span><span style="FONT-FAMILY: 宋体">坚固</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">"</span><span style="FONT-FAMILY: 宋体">。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Update NOW!</span><span style="FONT-FAMILY: 宋体">的核心代码中有</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">20</span><span style="FONT-FAMILY: 宋体">％～</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">30</span><span style="FONT-FAMILY: 宋体">％是用于容错的。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">6.</span><span style="FONT-FAMILY: 宋体">好的程序书写风格是必要的</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　书写程序按照语法的层次缩进，是衡量程序员是否专业的一个标准。当然，还要有必要的注释。否则，过了几个月，你都很难读懂自己的程序了。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">7.</span><span style="FONT-FAMILY: 宋体">创意是软件的灵魂</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　好了，如果你作到了上面那几点，那你差不多是个好程序员了。最后要说的就是创意。没有创意的软件，只是重复劳动。即使容错再好，风格再好也没有用。回头看看，那些出色的软件都有独到之处。如果你既是个好程序员又能写出有创意的软件，那你差不多就是大师了。　　　　　　　　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">语言只是工具</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /><br /></span><span style="FONT-FAMILY: 宋体">　　梁肇新</span><span style="FONT-FAMILY: ˎ̥"></span><span style="FONT-FAMILY: 宋体">《超级解霸》的作者（</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">http://www.herosoft.com/</span><span style="FONT-FAMILY: 宋体">）</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">1.</span><span style="FONT-FAMILY: 宋体">如何成为程序员</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　要成为高手程序员首先必须有丰富的计算机知识，包括软件系统知识和硬件系统知识，掌握一种高级编程语言如</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">C/C</span><span style="FONT-FAMILY: 宋体">＋＋和掌握汇编语言，这是成为程序员高手的必备条件。一般人都会认为语言最重要，其实语言只是工具而已，重要的是如何使用工具做自己想做的事，</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">2.</span><span style="FONT-FAMILY: 宋体">如何学习编程</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　我的建议是从</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">API</span><span style="FONT-FAMILY: 宋体">入手，因为</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">API</span><span style="FONT-FAMILY: 宋体">是操作系统提供的直接接口，其他的任何东西都是在这之上，像</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">VB</span><span style="FONT-FAMILY: 宋体">和</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">Delphi</span><span style="FONT-FAMILY: 宋体">这样的开发工具尽量隐含</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">API</span><span style="FONT-FAMILY: 宋体">的内容，同时也阻隔了成为高手的机会，因为编出来的</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">“</span><span style="FONT-FAMILY: 宋体">程序</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">”</span><span style="FONT-FAMILY: 宋体">无法了解它的执行过程也就使编程中最重要的</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">“</span><span style="FONT-FAMILY: 宋体">可预测</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">”</span><span style="FONT-FAMILY: 宋体">性变得很低。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　编程序的重点不是</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">“</span><span style="FONT-FAMILY: 宋体">编</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">”</span><span style="FONT-FAMILY: 宋体">，而是调试程序，理论上的完美在实现的时候会遇到很多细节问题，这些问题必须调试才能解决。我的编程习惯是一天写五天调试，《超级解霸》就是调试出来的，而不是写出来的。调试就涉及到汇编的问题，不进行汇编级的调试是不彻底的，也不能让人放心。</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥">3.</span><span style="FONT-FAMILY: 宋体">编程忠告</span><span lang="EN-US" style="FONT-FAMILY: ˎ̥"><br /></span><span style="FONT-FAMILY: 宋体">　　我的编程经验是，编程时除了调试外还应该测试，测试是指把要完成的程序的单个功能写一个测试实验，成功后再加到软件中来。任何软件都是小功能组成的，因此不要像课本所说的那样先写个程序框图来浪费时间，而是先单个实现局部功能再组装在一起。编程时千万不要盲目使用不了解的代码，否则会增加程序的出错机会。了解底层会增加编程思路。<br /><br />四、<font color="#000080">编程语言学习随笔<br /><br />近来一直在学习c++，原因很简单，就是喜欢编程！！！<br />学习之余，若有所思，记录在此，或许N年后回首，会有更多感触！！<br />编程最重要的是什么？－－－－－是解决问题的思路和方法！<br />那么编程语言在这里扮演一个什么角色呢？－－扮演的是把你的思想和方法转化为现实的一种工具！！仅仅是工具而已！<br />学习编程语言，就像我们学习认字、写字一般。解决问题的思路和方法，我们活到这个年岁上，都肯定有一些了，只不过是多少、优庸之分而已！但是并非每个人都能用程序来体现，因为很多人没有掌握这个工具，就好像会说话当不会写字的人，虽然有很多话要说，但是不会写字，从而不能把想法写下来，让更多的人分享。掌握了编程语言，就可以和机器交流，，让机器懂得我们要想做什么。但是，，并非每个会写字的人都能写出好文章来！！！<br />     我想说的，其实只有一点，，工具的东西是必须掌握的，但是工具仅仅是工具，不要沉迷于工具而忘了最重要的东西！！！<br /><br />五、闲言碎语话编程<br /><br />     有人说：编程高手==游戏高手。如果这是真的话，我早就成高手了，因为在游戏里，我早已成为宇宙总管、富甲天下、仗剑走天涯了。可是我还是一个菜鸟：）闲来无事，随便贴点文章。<br />    程序员不应依赖开发工具，程序员更应该拥有的是一种思维、一种精神、一种观念。就像Richard.M.Stallman一样，有自己的精神，为自由软件而奋斗。就像求伯君，为民族软件的振兴而奋斗。这才是真正的程序员。<br />    应该说，他们更注重的不是技术，而是软件的思维，软件的灵魂！！<br />    真正在编写程序到达一定时候，语言的使用并不是最大的障碍，对整个项目的把握、软件工程的把握、数据库的设计以及执行效果的分析等等才是需要进一步考虑的东东！<br />    学习编程其实与学习其它东西一样,要想掌握它,就要实践,实践,再实践.当你学到了一种新的技术或知识时,多实践是巩固学习的一种最好最有效的方法。<br />    这个实践不是照著书上的例子做一遍,而是根据自己的能力,给自己出题,然后去完成 它.只有这样,你才能发现自己的不足,同时又增加了自己的编程经验. 但要成为合格的程序员,光会写代码是远远不够的,更重要的和学习其他知识一样，重要的是获得提出问题，分析问题，解决问题的能力，不是为编程而学习，你具有什么样的思想，就会写出什样的程序。学一门语言，不能仅仅是语言，要注重语言背后的思想方法是思考.谋定而后动,是 不变的真理。<br /><br /></font></span></font></span>
				</font>
		</p>
<img src ="http://www.cppblog.com/stevennash/aggbug/13297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/stevennash/" target="_blank">Technical Consultant</a> 2006-10-04 02:17 <a href="http://www.cppblog.com/stevennash/archive/2006/10/04/13297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>