<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_心之镇</title><subtitle type="text">~宁以致远~</subtitle><id>http://feed.cnblogs.com/blog/u/54266/rss</id><updated>2011-04-14T05:50:01Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/54266/rss"/><entry><id>http://www.cnblogs.com/chenleiustc/archive/2011/04/14/2015944.html</id><title type="text">Makefile文件编写</title><summary type="text">makefile中一些常用符号的意义$@表示规则中的目标文件集。在模式规则中，如果有多个目标，那么，"$@"就是匹配于目标中模式定义的集合。$%仅当目标是函数库文件中，表示规则中的目标成员名。例如，如果一个目标是"foo.a (bar.o)"，那么，"$%"就是"bar.o"，"$@"就是"foo.a"。如果目标不是函数库文件（Unix下是[.a]，Windows下是[.lib]），那么，其值为空。$&lt;依赖目标中的第一个目标名字。如果依赖目标是以模式（即"%&amp;</summary><published>2011-04-14T05:50:00Z</published><updated>2011-04-14T05:50:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/14/2015944.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/14/2015944.html"/><content type="html">&lt;p&gt;makefile中一些常用符号的意义&lt;/p&gt;&lt;p&gt;&lt;strong&gt;$@&lt;/strong&gt;&lt;br /&gt;表示规则中的目标文件集。在模式规则中，如果有多个目标，那么，"$@"就是匹配于&lt;br /&gt;目标中模式定义的集合。&lt;br /&gt;&lt;strong&gt;$%&lt;/strong&gt;&lt;br /&gt;仅当目标是函数库文件中，表示规则中的目标成员名。例如，如果一个目标是"foo.a (bar.o)"，那么，"$%"就是"bar.o"，"$@"就是"foo.a"。如果目标不是函数库文件（Unix下是[.a]，Windows下是[.lib]），那么，其值为空。&lt;br /&gt;&lt;strong&gt;$&amp;lt;&lt;/strong&gt;&lt;br /&gt;依赖目标中的第一个目标名字。如果依赖目标是以模式（即"%"）定义的，那么"$&amp;lt;"将是符合模式的一系列的文件集。注意，其是一个一个取出来的。&lt;br /&gt;&lt;strong&gt;$?&lt;/strong&gt;&lt;br /&gt;所有比目标新的依赖目标的集合。以空格分隔。&lt;br /&gt;&lt;strong&gt;$^&lt;/strong&gt;&lt;br /&gt;所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的，那个这个变量会去除重复的依赖目标，只保留一份。&lt;br /&gt;&lt;strong&gt;$+&lt;/strong&gt;&lt;br /&gt;这个变量很像"$^"，也是所有依赖目标的集合。只是它不去除重复的依赖目标。&lt;br /&gt;&lt;strong&gt;$*&lt;/strong&gt;&lt;br /&gt;这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b"，并且目标的模式是"a.%.b"，那么，"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义，那么"$*"也就不能被推导出，但是，如果目标文件的后缀是make所识别的，那么"$*"就是除了后缀的那一部分。例如：如果目标是"foo.c"，因为".c"是make所能识别的后缀名，所以，"$*"的值就是"foo"。这个特性是GNU make的，很有可能不兼容于其它版本的make，所以，你应该尽量避免使用"$*"，除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的，那么"$*"就是空值。&lt;/p&gt;&lt;p&gt;多目录编译&lt;/p&gt;&lt;p&gt;#子目录宏定义&lt;/p&gt;&lt;p&gt;SUBDIRS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;= MsgAndData&amp;nbsp; PeerDetect&amp;nbsp; PeerOptimize&amp;nbsp;Server &lt;/p&gt;&lt;p&gt;#操作宏定义&lt;/p&gt;&lt;p&gt;EVERYTHING&amp;nbsp;= all clean&lt;/p&gt;&lt;p&gt;#引用宏&lt;/p&gt;&lt;p&gt;$(EVERYTHING)::&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @forsubdir in $(SUBDIRS); \&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; do \&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo"making $@ in $$subdir"; \&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ( cd$$subdir &amp;amp;&amp;amp; $(MAKE) $@ ) || exit 1; \&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; done&lt;/p&gt;&lt;img src="http://www.cnblogs.com/chenleiustc/aggbug/2015944.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/chenleiustc/archive/2011/04/14/2015944.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010647.html</id><title type="text">关于const char*和char*、const char** 和char** 赋值问题</title><summary type="text">根据ANSI C标准的赋值约束条件： 1. 两个操作数都是指向有限定符或无限定符的相容类型的指针。 2. 左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。一、const char*和char* const char*的类型是：“指向一个具有const限定符的char类型的指针”。（不能修改其值） char*的类型是：“指向一个char类型的指针”。 因此const char*和char*都是指向char类型的指针，只不过const char*指向的char类型是const的。 因此对于代码：char* src;const char* dest ;dest = src; 这样赋值是正</summary><published>2011-04-09T09:26:00Z</published><updated>2011-04-09T09:26:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010647.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010647.html"/><content type="html">&lt;p&gt;根据ANSI C标准的赋值约束条件：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1. 两个操作数都是指向有限定符或无限定符的相容类型的指针。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2. 左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;一、const char*和char*&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; const char*的类型是：&amp;ldquo;指向一个具有const限定符的char类型的指针&amp;rdquo;。（不能修改其值）&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char*的类型是：&amp;ldquo;指向一个char类型的指针&amp;rdquo;。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因此&lt;strong&gt;const char*和char*都是指向char类型的指针，只不过const char*指向的char类型是const的。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因此对于代码：&lt;/p&gt;&lt;p&gt;char* src;&amp;nbsp;&lt;br /&gt;const char* dest ;&amp;nbsp;&lt;br /&gt;dest = src;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这样赋值是正确的，因为:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; * 操作数指向的都是char类型，因此是相容的&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; * 左操作数具有有操作数所指向类型的全部限定符（右操作数没有限定符），同时自己有限定符（const）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果反过来赋值就违反了赋值的约束条件：src指向的对象的值可以修改，而dest指向的对象的值不可修改&lt;br /&gt;如果让src去指向dest所指向的那个不可修改的对象，如果合法，岂不是变得可修改了？&lt;/p&gt;&lt;p&gt;&lt;strong&gt;src = dest; // 这样赋值，左操作数指向的类型没有右操作数指向类型的const限定符，不符合约束条件2&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;2. const char** 和char**&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; const char**的类型是：&amp;ldquo;指向一个有const限定符的char类型的指针的指针&amp;rdquo;。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char**的类型是：&amp;ldquo;指向一个char类型的指针的指针&amp;rdquo;。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于const char** 和char**来说，二者都是没有限定符的指针类型，但是它们指向的类型不一样，前者指向char*，&amp;nbsp;&amp;nbsp;&amp;nbsp; 而后者指向const char*，因此它们不相容，所以&lt;strong&gt;char**类型的操作数不能赋值给const char**类型的操作数。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 即对于下列代码，编译器会报错：char** src;&amp;nbsp;&lt;br /&gt;const char** dest;&amp;nbsp;&lt;br /&gt;dest = src;&amp;nbsp;&lt;br /&gt;// error: invalid conversion from `char**' to `const char**'&amp;nbsp;&lt;br /&gt;-------------------------------------------------------------------------------------------------------------&lt;/p&gt;&lt;p&gt;const&lt;br /&gt;1. 限定符声明变量只能被读&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int i=5;&lt;br /&gt;&amp;nbsp;&amp;nbsp; int j=0;&lt;br /&gt;&amp;nbsp;&amp;nbsp; ...&lt;br /&gt;&amp;nbsp;&amp;nbsp; i=j;&amp;nbsp;&amp;nbsp; //非法，导致编译错误&lt;br /&gt;&amp;nbsp;&amp;nbsp; j=i;&amp;nbsp;&amp;nbsp; //合法&lt;br /&gt;2. 必须初始化&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int i=5;&amp;nbsp;&amp;nbsp;&amp;nbsp; //合法&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int j;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //非法，导致编译错误&lt;br /&gt;3. 在另一连接文件中引用const常量&lt;br /&gt;&amp;nbsp;&amp;nbsp; extern const int i;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //合法&lt;br /&gt;&amp;nbsp;&amp;nbsp; extern const int j=10; //非法，常量不可以被再次赋值&lt;br /&gt;4. 便于进行类型检查&lt;br /&gt;&amp;nbsp;&amp;nbsp; 用const方法可以使编译器对处理内容有更多了解。&lt;br /&gt;&amp;nbsp;&amp;nbsp; #define I=10&lt;br /&gt;&amp;nbsp;&amp;nbsp; const long &amp;amp;i=10;&amp;nbsp;&amp;nbsp; /*dapingguo提醒：由于编译器的优化，使得在const long i=10; 时i不被分配内存，而是已10直接代入&amp;nbsp;以后的引用中，以致在以后的代码中没有错误，为达到说教效&amp;nbsp;果，特别地用&amp;amp;i明确地给出了i的内存分配。不过一旦你关闭所&amp;nbsp;有优化措施，即使const long i=10;也会引起后面的编译错误。*/&lt;br /&gt;&amp;nbsp;&amp;nbsp; char h=I;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //没有错&lt;br /&gt;&amp;nbsp;&amp;nbsp; char h=i;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //编译警告，可能由于数的截短带来错误赋值。&lt;br /&gt;5. 可以避免不必要的内存分配&lt;br /&gt;&amp;nbsp;&amp;nbsp; #define STRING "abcdefghijklmn\n"&lt;br /&gt;&amp;nbsp;&amp;nbsp; const char string[]="abcdefghijklm\n";&lt;br /&gt;&amp;nbsp;&amp;nbsp; ...&lt;br /&gt;&amp;nbsp;&amp;nbsp; printf(STRING);&amp;nbsp;&amp;nbsp; //为STRING分配了第一次内存&lt;br /&gt;&amp;nbsp;&amp;nbsp; printf(string);&amp;nbsp;&amp;nbsp; //为string一次分配了内存，以后不再分配&lt;br /&gt;&amp;nbsp;&amp;nbsp; ...&lt;br /&gt;&amp;nbsp;&amp;nbsp; printf(STRING);&amp;nbsp;&amp;nbsp; //为STRING分配了第二次内存&lt;br /&gt;&amp;nbsp;&amp;nbsp; printf(string);&lt;br /&gt;&amp;nbsp;&amp;nbsp; ...&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; 由于const定义常量从汇编的角度来看，只是给出了对应的内存地址，&lt;br /&gt;&amp;nbsp;&amp;nbsp; 而不是象#define一样给出的是立即数，所以，const定义的常量在&lt;br /&gt;&amp;nbsp;&amp;nbsp; 程序运行过程中只有一份拷贝，而#define定义的常量在内存中有&lt;br /&gt;&amp;nbsp;&amp;nbsp; 若干个拷贝。&lt;br /&gt;6. 可以通过函数对常量进行初始化&lt;br /&gt;&amp;nbsp;&amp;nbsp; int value();&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int i=value();&lt;br /&gt;&amp;nbsp;&amp;nbsp; dapingguo说：假定对ROM编写程序时，由于目标代码的不可改写，本语句将会无效，不过可以变通一下：&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int &amp;amp;i=value();&lt;br /&gt;&amp;nbsp;&amp;nbsp; 只要令i的地址处于ROM之外，即可实现：i通过函数初始化，而其值有不会被修改。&lt;br /&gt;7. 是不是const的常量值一定不可以被修改呢？&lt;br /&gt;&amp;nbsp;&amp;nbsp; 观察以下一段代码：&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int i=0;&lt;br /&gt;&amp;nbsp;&amp;nbsp; int *p=(int*)&amp;amp;i;&lt;br /&gt;&amp;nbsp;&amp;nbsp; p=100;&lt;br /&gt;&amp;nbsp;&lt;strong&gt;&amp;nbsp; 通过强制类型转换，将地址赋给变量，再作修改即可以改变const常量值。&lt;/strong&gt;&lt;br /&gt;8. 请分清数值常量和指针常量，以下声明颇为玩味：&lt;br /&gt;&amp;nbsp;&amp;nbsp; int ii=0;&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int i=0;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //i是常量，i的值不会被修改&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int *p1i=&amp;amp;i;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //指针p1i所指内容是常量，可以不初始化&lt;br /&gt;&amp;nbsp;&amp;nbsp; int * const p2i=&amp;amp;ii;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //指针p2i是常量，所指内容可修改&lt;br /&gt;&amp;nbsp;&amp;nbsp; const int * const p3i=&amp;amp;i; //指针p3i是常量，所指内容也是常量&lt;br /&gt;&amp;nbsp;&amp;nbsp; p1i=&amp;amp;ii;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //合法&lt;br /&gt;&amp;nbsp;&amp;nbsp; *p2i=100;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //合法&lt;/p&gt;&lt;p&gt;关于C++中的const关键字的用法非常灵活，而使用const将大大改善程序的健壮性，参考了康建东兄的const使用详解一文，对其中进行了一些补充，写下了本文。&lt;br /&gt;1.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const常量，如const int max = 100;&amp;nbsp;&lt;br /&gt;优点：const常量有数据类型，而宏常量没有数据类型。编译器可以对前者进行类型安全检查，而对后者只进行字符替换，没有类型安全检查，并且在字符替换时可能会产生意料不到的错误（边际效应）&lt;/p&gt;&lt;p&gt;2.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const 修饰类的数据成员。如：&lt;br /&gt;class A&lt;br /&gt;{&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;const int size;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;hellip; }&lt;/p&gt;&lt;p&gt;&lt;br /&gt;const数据成员只在某个对象生存期内是常量，而对于整个类而言却是可变的。因为类可以创建多个对象，不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员，因为类的对象未被创建时，编译器不知道const 数据成员的值是什么。如&lt;br /&gt;class A&lt;br /&gt;{&lt;br /&gt;const int size = 100;&amp;nbsp;&amp;nbsp;&amp;nbsp; //错误&lt;br /&gt;int array[size];&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //错误，未知的size&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;const数据成员的初始化只能在类的构造函数的初始化表中进行。要想建立在整个类中都恒定的常量，应该用类中的枚举常量来实现。如&lt;br /&gt;class A&lt;br /&gt;{&amp;hellip;&lt;br /&gt;enum {size1=100, size2 = 200 };&lt;br /&gt;int array1[size1];&lt;br /&gt;int array2[size2];&amp;nbsp;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;枚举常量不会占用对象的存储空间，他们在编译时被全部求值。但是枚举常量的隐含数据类型是整数，其最大值有限，且不能表示浮点数。&lt;/p&gt;&lt;p&gt;3.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const修饰指针的情况，见下式：&lt;br /&gt;int b = 500;&amp;nbsp;&lt;br /&gt;const int* a = &amp;amp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [1]&amp;nbsp;&lt;br /&gt;int const *a = &amp;amp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [2]&amp;nbsp;&lt;br /&gt;int* const a = &amp;amp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [3]&amp;nbsp;&lt;br /&gt;const int* const a = &amp;amp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [4]&lt;/p&gt;&lt;p&gt;如果你能区分出上述四种情况，那么，恭喜你，你已经迈出了可喜的一步。不知道，也没关系，我们可以参考《Effective c++》Item21上的做法，如果const位于星号的左侧，则const就是用来修饰指针所指向的变量，即指针指向为常量；如果const位于星号的右侧，const就是修饰指针本身，即指针本身是常量。因此，[1]和[2]的情况相同，都是指针所指向的内容为常量（const放在变量声明符的位置无关），这种情况下不允许对内容进行更改操作，如不能*a = 3 ；[3]为指针本身是常量，而指针所指向的内容不是常量，这种情况下不能对指针本身进行更改操作，如a++是错误的；[4]为指针本身和指向的内容均为常量。&lt;/p&gt;&lt;p&gt;4.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const的初始化&lt;/p&gt;&lt;p&gt;先看一下const变量初始化的情况&amp;nbsp;&lt;br /&gt;1) 非指针const常量初始化的情况：A b;&amp;nbsp;&lt;br /&gt;const A a = b;&lt;/p&gt;&lt;p&gt;2) 指针const常量初始化的情况：&lt;br /&gt;A* d = new A();&amp;nbsp;&lt;br /&gt;const A* c = d;&amp;nbsp;&lt;br /&gt;或者：const A* c = new A();&amp;nbsp;&lt;br /&gt;3）引用const常量初始化的情况：&amp;nbsp;&lt;br /&gt;A f;&amp;nbsp;&lt;br /&gt;const A&amp;amp; e = f;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 这样作e只能访问声明为const的函数，而不能访问一&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;般的成员函数；&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [思考1]： 以下的这种赋值方法正确吗？&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; const A* c=new A();&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; A* e = c;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [思考2]： 以下的这种赋值方法正确吗？&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; A* const c = new A();&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; A* b = c;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;5.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中，const 可以修饰函数的返回值，或某个参数；对于成员函数，还可以修饰是整个函数。有如下几种情况，以下会逐渐的说明用法：A&amp;amp; operator=(const A&amp;amp; a);&amp;nbsp;&lt;br /&gt;void fun0(const A* a );&amp;nbsp;&lt;br /&gt;void fun1( ) const; // fun1( ) 为类成员函数&amp;nbsp;&lt;br /&gt;const A fun2( );&lt;/p&gt;&lt;p&gt;&lt;br /&gt;1） 修饰参数的const，如 void fun0(const A* a ); void fun1(const A&amp;amp; a);&amp;nbsp;&lt;br /&gt;调用函数的时候，用相应的变量初始化const常量，则在函数体中，按照const所修饰的部分进行常量化，如形参为const A* a，则不能对传递进来的指针的内容进行改变，保护了原指针所指向的内容；如形参为const A&amp;amp; a，则不能对传递进来的引用对象进行改变，保护了原对象的属性。&amp;nbsp;&lt;br /&gt;[注意]：参数const通常用于参数为指针或引用的情况，且只能修饰输入参数;若输入参数采用&amp;ldquo;值传递&amp;rdquo;方式，由于函数将自动产生临时变量用于复制该参数，该参数本就不需要保护，所以不用const修饰。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;[总结]对于非内部数据类型的输入参数，因该将&amp;ldquo;值传递&amp;rdquo;的方式改为&amp;ldquo;const引用传递&amp;rdquo;，目的是为了提高效率。例如，将void Func(A a)改为void Func(const A &amp;amp;a)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于内部数据类型的输入参数，不要将&amp;ldquo;值传递&amp;rdquo;的方式改为&amp;ldquo;const引用传递&amp;rdquo;。否则既达不到提高效率的目的，又降低了函数的可理解性。例如void Func(int x)不应该改为void Func(const int &amp;amp;x)&lt;/p&gt;&lt;p&gt;&lt;br /&gt;2） 修饰返回值的const，如const A fun2( ); const A* fun3( );&amp;nbsp;&lt;br /&gt;这样声明了返回值后，const按照"修饰原则"进行修饰，起到相应的保护作用。const Rational operator*(const Rational&amp;amp; lhs, const Rational&amp;amp; rhs)&amp;nbsp;&lt;br /&gt;{&amp;nbsp;&lt;br /&gt;return Rational(lhs.numerator() * rhs.numerator(),&amp;nbsp;&lt;br /&gt;lhs.denominator() * rhs.denominator());&amp;nbsp;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;返回值用const修饰可以防止允许这样的操作发生:Rational a,b;&amp;nbsp;&lt;br /&gt;Radional c;&amp;nbsp;&lt;br /&gt;(a*b) = c;&lt;/p&gt;&lt;p&gt;一般用const修饰返回值为对象本身（非引用和指针）的情况多用于二目操作符重载函数并产生新对象的时候。 [总结]&lt;/p&gt;&lt;p&gt;&lt;br /&gt;1.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一般情况下，函数的返回值为某个对象时，如果将其声明为const时，多用于操作符的重载。通常，不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下：如果返回值为某个对象为const（const A test = A 实例）或某个对象的引用为const（const A&amp;amp; test = A实例） ，则返回值具有const属性，则返回实例只能访问类A中的公有（保护）数据成员和const成员函数，并且不允许对其进行赋值操作，这在一般情况下很少用到。&lt;/p&gt;&lt;p&gt;&lt;br /&gt;2.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果给采用&amp;ldquo;指针传递&amp;rdquo;方式的函数返回值加const修饰，那么函数返回值（即指针）的内容不能被修改，该返回值只能被赋给加const 修饰的同类型指针。如：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;const char * GetString(void);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;如下语句将出现编译错误：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;char *str=GetString();&lt;/p&gt;&lt;p&gt;&lt;br /&gt;正确的用法是：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;const char *str=GetString();&lt;/p&gt;&lt;p&gt;&lt;br /&gt;3.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 函数返回值采用&amp;ldquo;引用传递&amp;rdquo;的场合不多，这种方式一般只出现在类的赙值函数中，目的是为了实现链式表达。如：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;class A&lt;br /&gt;{&amp;hellip;&lt;br /&gt;A &amp;amp;operate = (const A &amp;amp;other); //负值函数&lt;br /&gt;}&lt;br /&gt;A a,b,c;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //a,b,c为A的对象&lt;br /&gt;&amp;hellip;&lt;br /&gt;a=b=c;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //正常&lt;br /&gt;(a=b)=c;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //不正常，但是合法&lt;/p&gt;&lt;p&gt;&lt;br /&gt;若负值函数的返回值加const修饰，那么该返回值的内容不允许修改，上例中a=b=c依然正确。(a=b)=c就不正确了。&lt;br /&gt;[思考3]： 这样定义赋值操作符重载函数可以吗？&amp;nbsp;&lt;br /&gt;const A&amp;amp; operator=(const A&amp;amp; a);&lt;/p&gt;&lt;p&gt;&lt;br /&gt;6.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 类成员函数中const的使用&amp;nbsp;&lt;br /&gt;一般放在函数体后，形如：void fun() const;&amp;nbsp;&lt;br /&gt;任何不会修改数据成员的函数都因该声明为const类型。如果在编写const成员函数时，不慎修改了数据成员，或者调用了其他非const成员函数，编译器将报错，这大大提高了程序的健壮性。如：&lt;/p&gt;&lt;p&gt;&lt;br /&gt;class Stack&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; void Push(int elem);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int Pop(void);&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int GetCount(void) const;&amp;nbsp;&amp;nbsp; //const 成员函数&lt;br /&gt;private:&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int m_num;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int m_data[100];&lt;br /&gt;};&lt;/p&gt;&lt;p&gt;&lt;br /&gt;int Stack::GetCount(void) const&lt;/p&gt;&lt;p&gt;{&lt;br /&gt;++m_num;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //编译错误，企图修改数据成员m_num&lt;br /&gt;Pop();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //编译错误，企图调用非const函数&lt;br /&gt;Return m_num;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;&lt;br /&gt;7.&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 使用const的一些建议&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1 要大胆的使用const，这将给你带来无尽的益处，但前提是你必须搞清楚原委；&amp;nbsp;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;2 要避免最一般的赋值操作错误，如将const变量赋值，具体可见思考题；&amp;nbsp;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;3 在参数中使用const应该使用引用或指针，而不是一般的对象实例，原因同上；&amp;nbsp;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;4 const在成员函数中的三种用法（参数、返回值、函数）要很好的使用；&amp;nbsp;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;5 不要轻易的将函数的返回值类型定为const;&amp;nbsp;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;6除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;[思考题答案]&amp;nbsp;&lt;br /&gt;1 这种方法不正确，因为声明指针的目的是为了对其指向的内容进行改变，而声明的指针e指向的是一个常量，所以不正确；&amp;nbsp;&lt;br /&gt;2 这种方法正确，因为声明指针所指向的内容可变；&amp;nbsp;&lt;br /&gt;3 这种做法不正确；&amp;nbsp;&lt;br /&gt;在const A::operator=(const A&amp;amp; a)中，参数列表中的const的用法正确，而当这样连续赋值的时侯，问题就出现了：&amp;nbsp;&lt;br /&gt;A a,b,c:&amp;nbsp;&lt;br /&gt;(a=b)=c;&amp;nbsp;&lt;br /&gt;因为a.operator=(b)的返回值是对a的const引用，不能再将c赋值给const常量。&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;C函数形参里面为何要用:const char *inputString&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;保护实参指针指向的数据不被意外改写。&amp;nbsp;&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;const char *inputString;//定义指向常量的指针&amp;nbsp;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;指针本身地址值可以增减，也可以给指针更换指向的常量，但是指向的内容有常量性质，指向的内容不能被更改&lt;/span&gt;&lt;/strong&gt;。即：&amp;nbsp;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;inputString++;//这是可以的。&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;(*inputString)++;//这是禁止的。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;强制类型转换的实质是告诉编译器,"这可行, 这行的通... 至于程序会不会出错, 编译器则不管, 因为你都说可行了."&lt;/p&gt;&lt;p&gt;&lt;strong&gt;两个操作数都是指向有限定符或无限定符的相容类型的指针，左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;正是这个条件，使得函数调用中实参char *能够与形参const char *匹配（在C标准库中，所有的字符串处理函数就是这样的）&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;函数那部分的时候，有一个很重要的概念是区别：&lt;strong&gt;值传递、指针传递、引用传值(&lt;/strong&gt;好像是这三种说法)。&lt;/p&gt;&lt;p&gt;我觉得要理解这部分知识点，首先应该知道&lt;strong&gt;不同种类的变量在内存中是如何分配存储的，它们的生命周期多长等这些问题&lt;/strong&gt;，然后在理解哪三种情况就好理解了。&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;函数的参数一般都&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;是在stack栈上分配的，所以它的生命周期就在它所属的函数内，函数执行完毕的时候，它的内存将被回收。&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;如果我们想在函数内对实际参数进行操作(不是对形式参数的副本)的话，一般会使用引用，即声明函数的形式参数为引用类型，比如char * fun(char * &amp;amp;p)，这样实参和形参为同&lt;/strong&gt;&lt;strong&gt;一个变量，我们在函数中操作形参p就等于直接在操作实参变量。&lt;/strong&gt;我看C++语法书的时候，书上说这样用还有一个好处是，&lt;strong&gt;在调用函数的时候，不用再为形式参数分配内存了&lt;/strong&gt;，这样执行效率会高一点儿。&lt;/p&gt;&lt;p&gt;下面是函数形参为指针的几种情况：&lt;/p&gt;&lt;p&gt;#include &amp;lt;iostream&amp;gt;&lt;br /&gt;using namespace std;&lt;/p&gt;&lt;p&gt;char* func1(char *p);&lt;br /&gt;void func2(char *p);&lt;br /&gt;void func3(char * &amp;amp;p);&lt;/p&gt;&lt;p&gt;char s1[]="原来的";&lt;br /&gt;char s2[]="指向我了吗";&lt;/p&gt;&lt;p&gt;int main()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char *ptr=s1;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; cout&amp;lt;&amp;lt;ptr&amp;lt;&amp;lt;endl;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ptr=func1(ptr);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //返回值改变ptr使它指向另一个地址&lt;br /&gt;//func2(ptr);&amp;nbsp;&amp;nbsp; //ptr的指向没有改变，func2函数中改变的只是它的副本(一个局部变量)&lt;br /&gt;//func3(ptr);&amp;nbsp;&amp;nbsp; //改变了ptr的指向，func3函数的形式参数为引用类型，实参和形参是同一个变量&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; cout&amp;lt;&amp;lt;ptr&amp;lt;&amp;lt;endl;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return 0;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;char* func1(char *p)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; p=s2;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return p;&lt;br /&gt;}&lt;br /&gt;void func2(char *p)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; p=s2;&lt;br /&gt;}&lt;br /&gt;void func3(char * &amp;amp;p)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; p=s2;&lt;br /&gt;}&lt;/p&gt;&lt;div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/chenleiustc/aggbug/2010647.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010647.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010034.html</id><title type="text">字符 数组 ，字符 指针 ，Sizeof总结</title><summary type="text">1.以字符串 形式出现的，编译器都会为该字符串自动添加一个0作为结束符，如在代码中写 "abc",那么编译器帮你存储的是"abc\0"2."abc" 是常量吗？答案是有时是，有时不是。不是常量的情 况："abc"作为字符数组初始值的时候就不是，如 char str[] = "abc";因为定义的是一个字符数组，所以就相当于定义了一些空间来存放"abc"，而又因为字符数组就是把字符一个一个地存放的，所以编译器把 这个语句解析为 char str[3] = {'a&amp;#39</summary><published>2011-04-08T16:15:00Z</published><updated>2011-04-08T16:15:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010034.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010034.html"/><content type="html">&lt;p&gt;&lt;span&gt;1.以&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;串 形式出现的，编译器都会为该&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;串自动添加一个0作为结束符，如在代码中写 "abc",那么编译器帮你存储的是"abc\0"&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;2."abc" 是常量吗？答案是有时是，有时不是。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;不是常量的情 况："abc"作为&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;初始值的时候就不是，如&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; char str[] = "abc";&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;因为&lt;strong&gt;定义&lt;/strong&gt;&amp;nbsp;的是一个&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;，所以就相当于&lt;strong&gt;定义&lt;/strong&gt;&amp;nbsp;了一些空间来存放"abc"，而又因为&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;就是把&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;一个一个地存放的，所以编译器把 这个语句解析为&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;char str[3] = {'a','b','c'};&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;又根据上面的 总结1，所以 char str[] = "abc";的最终结果是 char str[4] = {'a','b','c','\0'};做一下扩展，如果char str[] = "abc";是在函数内部写的话，那么这里 的"abc\0"因为不是常量，所以应该被放在栈上。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;是常量的情 况： 把"abc"赋给一个&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;指针&lt;/strong&gt;&amp;nbsp;变量时，如&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; char* ptr = "abc";&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; 因为&lt;strong&gt;定义&lt;/strong&gt;&amp;nbsp;的是一个普通&lt;strong&gt;指针&lt;/strong&gt;&amp;nbsp;，并没有&lt;strong&gt;定义&lt;/strong&gt;&amp;nbsp;空间来存放"abc"，所以编译器得帮我们找地方来放"abc"，显然，把这里的"abc"当成常量并把它放到程序 的常量区是编译器 最合适的选择。所以尽管ptr的类型不是const char*，并且ptr[0] = 'x';也能编译 通过，但是执行ptr[0] = 'x';就会发生运行时异常，因为这个语句试图去修改程序 常量区中的东西。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;记得哪本书中曾经说过char* ptr = "abc";这种写法原来在&lt;strong&gt;c++&lt;/strong&gt;&amp;nbsp;标准中是不允许的， 但是因为这种写法在c中实在是太多了，为了兼容c，不允许也得允许。虽然允许， 但是建议的写法应该是const char* ptr = "abc";这样如果后面写ptr[0] = 'x'的话编译器就不会让它编译通过，也就避免了上面说的运行时异常。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;又扩展一下，如果char* ptr = "abc";写在函数体内，那么虽然这里的"abc\0"被放在常量区中，但是ptr本身只是一个普通的&lt;strong&gt;指针&lt;/strong&gt;&amp;nbsp;变量，所以ptr是被放在栈上的， 只不过是它所指向的东西被放在常量区罢了。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;3.&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;的 类型是由该&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;所存放的东西的类型以及&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;本 身的大小决定的。 如char s1[3]和char s2[4]，s1的类型就是char[3]，s2的类型就是char[4]， 也就是说尽管s1和s2都是&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;，但两者的类型却是不同的。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;4.&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;串常量的类型可以理解为相应&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;常量&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;的类型， 如"abcdef"的类型就可以看成是const char[7]&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;5.sizeof 是用来求类型的字节数的。如int a;那么无论sizeof(int)或者是sizeof(a)都 是等于4，因为sizeof(a)其实就是sizeof(type of a)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;[转注：以下两点内容有待商榷]&lt;/strong&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;6.对于函数参数列表中的以&lt;strong&gt;数组&lt;/strong&gt;&amp;nbsp;类型书写的形式参 数，编译器把其解释为普通 的&lt;strong&gt;指针&lt;/strong&gt;&amp;nbsp;类型，如对于void func(char sa[100],int ia[20],char *p) 则sa的类型为char*，ia的类型为int*，p的类型为char*&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;7.根据上面的总结，来实战一下：&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; 对于char str[] = "abcdef";就有sizeof(str) == 7,因为str的类型是char[7]， 也有sizeof("abcdef") == 7，因为"abcdef"的类型是const char[7]。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;对于char *ptr = "abcdef";就有sizeof(ptr) == 4，因为ptr的类型是char*。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;对于char str2[10] = "abcdef";就有sizeof(str2) == 10，因为str2的类型是char[10]。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;对于void func(char sa[100],int ia[20],char *p); 就有sizeof(sa) == sizeof(ia) == sizeof(p) == 4， 因为sa的类型是char*，ia的类型是int*，p的类型是char*&lt;/span&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/chenleiustc/aggbug/2010034.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/chenleiustc/archive/2011/04/09/2010034.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2010001.html</id><title type="text">[ZZ]深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p</title><summary type="text">一、可能的组合： (1)const char*p (2)char const*p (3)char *const p (4)const char **p (5)char const**p (6)char *const *p (7)char **const p 当然还有在(5)、(6)、(7)中再插入一个const的若干情况，不过分析了以上7中，其他的就可类推了！二、理解助记法宝： 1。关键看const 修饰谁。 2。由于没有const *的运算，若出现const*的形式，则const实际上是修饰前面的。 比如：char const*p,由于没有const*运算，则const实际上是修饰前面的ch</summary><published>2011-04-08T15:32:00Z</published><updated>2011-04-08T15:32:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2010001.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2010001.html"/><content type="html">&lt;p&gt;&lt;strong&gt;一、可能的组合：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (1)const char*p&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (2)char const*p&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (3)char *const p&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (4)const char **p&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (5)char const**p&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (6)char *const *p&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (7)char **const p&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当然还有在(5)、(6)、(7)中再插入一个const的若干情况，不过分析了以上7中，其他的就可类推了！&lt;/p&gt;&lt;p&gt;&lt;strong&gt;二、理解助记法宝：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;&lt;span&gt;1。关键看const 修饰谁。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;&lt;strong&gt;2。&lt;span style="color: #ff0000;"&gt;由于没有const *的运算，若出现const*的形式，则const实际上是修饰前面的&lt;/span&gt;。&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 比如：char const*p,由于没有const*运算，则const实际上是修饰前面的char，因此&lt;strong&gt;char const*p等价于const char*p&lt;/strong&gt;。也就是说上面7种情况中，（1）和（2）等价。 同理，（4）和（5）等价。在（6）中，由于没有const*运算，const实际上修饰的是前面的char*,但不能在定义时转换写成 const(char *)*p，因为在定义是"()"是表示函数。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;三、深入理解7种组合&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (0)程序&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;在执行时为其开辟的空间皆在内存（RAM）中，而RAM里的内存单元是&lt;span&gt;可读可写&lt;/span&gt;&amp;nbsp;的；&lt;span&gt;指针只是用来指定或定位要操作的数据的工具，只是用来读写RAM里内存单元的工作指针&lt;/span&gt;&amp;nbsp;。若对指针不加任何限定，程序中一个指针可以指向RAM中的任意位置（除了系统敏感区，如操作系统内核所在区域）并对其指向的内存单元进行读和写操作（由RAM的可读可写属性决定）；RAM里内存单元的可读可写属性不会因为对工作指针的限定而变化（见下面的第4点），而&lt;span&gt;所有对指针的各种const限定说白了只是对该&lt;span&gt;指针&lt;/span&gt;&amp;nbsp;的&lt;/span&gt;&amp;nbsp;&lt;span&gt;&lt;span&gt;读写权限&lt;/span&gt;&amp;nbsp;（包括读写位置）进行了限定&lt;/span&gt;&amp;nbsp;。&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (1)char *p：p是一个工作指针，可以用来对&lt;strong&gt;&lt;span&gt;任意位置&lt;/span&gt;&amp;nbsp;&lt;/strong&gt;（非系统敏感区域）进 行&lt;strong&gt;&lt;span&gt;读操作和写操作&lt;/span&gt;&amp;nbsp;&lt;/strong&gt;，一次读写一个字节（char占一个字节）。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (2)const char*p或者char const *p（因为没有const*p运算，因此const修饰的还是前面的char）：可以对任意位置（非系统敏感区域）进行&lt;strong&gt;&lt;span&gt;&amp;ldquo;只读&amp;rdquo;&lt;/span&gt;&amp;nbsp;&lt;/strong&gt;操作。（&amp;ldquo;只读&amp;rdquo;是相对于char *p来说所限定的内容）&lt;br /&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (3)char *const p（const 修饰的是p）：只能对&lt;strong&gt;&lt;span&gt;&amp;ldquo;某个固定的位置&amp;rdquo;&lt;/span&gt;&amp;nbsp;&lt;/strong&gt;进 行读写操作，并且在定义p时就必须初始化（因为在后面不能执行&amp;ldquo;p=..&amp;rdquo;的操作，因此就不能在后面初始化，因此只能在定义时初始化）。（&amp;ldquo;某个固定的位 置&amp;rdquo;是相对于char *p来说所限定的内容）&lt;br /&gt;可以总结以上3点为：char *p中的指针p通常是&lt;strong&gt;&lt;span&gt;&amp;rdquo;万能&amp;rdquo;的工作指针&lt;/span&gt;&amp;nbsp;&lt;/strong&gt;，而（2）和（3）只是在（1）的基础上&lt;strong&gt;&lt;span&gt;加了些特定的限制&lt;/span&gt;&amp;nbsp;&lt;/strong&gt;，这些限制在程序中并不是必须的，只是为了防止程序员的粗心大意而产生事与愿违的错 误。&lt;br /&gt;另外，要明白&lt;strong&gt;&amp;ldquo;&lt;span&gt;每块内存空间都可有名字；每块内存空间内容皆可变（除非有所限）&lt;/span&gt;&amp;nbsp;&amp;rdquo;&lt;/strong&gt;&amp;nbsp;。比如函数里定义的char s[]="hello";事实上在进程的栈内存里开辟了6个变量共6个字节的空间，其中6个字符变量的名字分别为：s1[0]、s1[1]、 s1[2]、s1[3]、s1[4]、s1[5]（内容是'\0'）&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span&gt;{&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;&lt;strong&gt;待验证&lt;/strong&gt;&amp;nbsp;&lt;/span&gt;：&amp;nbsp;&lt;strong&gt;&lt;span&gt;还有一个4字节的指针变量s&lt;/span&gt;&amp;nbsp;&lt;/strong&gt;。不过s是&amp;ldquo;有所限制&amp;rdquo;的，属于char *const类型，也就是前面说的&amp;nbsp;&amp;nbsp; (3)这种情况,s一直指向s［0］, &amp;nbsp; &amp;nbsp;即（*s==s[0]=='h'）,可以通过*s='k'来改变s所指向的 s[0]的值，但不能执行（char *h＝&amp;ldquo;aaa&amp;rdquo;;s=h;）来对s另外赋值。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span&gt;}&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (4)上面的(2)和(3)只是对p进行限定，没有也不能对p所指向的空间进行限定，对于"char s[]="hello";const char*p=s;" 虽然不能通过*(p+i)='x'或者p[i]='x'来修改数组元素s[0]～s[4]的值，但可以通过*(s+i)='x'或者s[i]='x'来修改原数组元素的值－－&lt;strong&gt;RAM里内存单元的可读可写属性不因对工作指针的限定而改变，而只会因对其本身的限定而改变&lt;/strong&gt;。如const char c＝&amp;lsquo;A&amp;rsquo;,c是RAM里一个内存单元（8字节）的名字，该内存单元的内容只可读，不可写。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp; (5)const char **p或者char const**p ：涉及两个指针p和 *p。由于const修饰char ，对指针p没有任何限定，对指针*p进行了上面情况(2)的限定。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; (6)char *const *p：涉及两个指针p和 *p。由于const修饰前面的char*,也就是对p所指向的内容*p进行了限定(也属于前面的情况(2))。而对*p来说，由于不能通过"*p＝..."来进行另外赋值，因此属于前面的情况(3)的限定。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; (7)char **const p ： 涉及两个指针p和 *p，const修饰p，对p进行上面情况(3)的限定，而对*p,没有任何限定。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;四、关于char **p 、const char **p的类型相容性问题&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;1。问题&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; char *p1;const *p2=p1;//合法&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; char **p1;const&amp;nbsp; char**p2=p1;//不合法，会有警告warning: initialization from incompatible pointer type&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; char **p1;char const**p2=p1;//不合法，会有警告warning: initialization from incompatible pointer type&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; char**p1;char*const*p2=p1;//合法&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;&amp;nbsp;2。判断规则&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 明确const修饰的对象！对于指针p1，和p2，&lt;strong&gt;&lt;span&gt;若要使得p2=p1成立，则可读做&lt;/span&gt;&amp;nbsp;&lt;/strong&gt;：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;&lt;strong&gt;&amp;ldquo;p1是指向X类型的指针，p2是指向&amp;ldquo;带有const限定&amp;rdquo;的X类型的指针&lt;/strong&gt;&amp;nbsp;&lt;/span&gt;&lt;strong&gt;&lt;span&gt;&amp;ldquo;。&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; char *p1;const *p2=p1;//合法:p1是指向（char）类型的指针，p2是指向&amp;ldquo;带有const限定"的(char)类型的指针。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; char **p1;const&amp;nbsp; char**p2=p1;//不合法:p1是指向（char*）类型的指针,p2是指向 ((const char)*)类型的指针。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; char **p1;char const**p2=p1;//不合法;与上等价。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; char**p1;char*const*p2=p1;//合法:&amp;nbsp; p1是指向（char *）类型的指针，p2是指向&amp;ldquo;带有const限定"的(char*)类型的指针。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;五、其他&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 1。 含有const的单层或双层指针的统一读法：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;&lt;strong&gt;&amp;ldquo;p是一个指针，是一个［&amp;ldquo;带有const限定&amp;rdquo;的］指向［&amp;rdquo;带有const限定&amp;rdquo;的］X类型的指针&amp;rdquo;。&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 2。定义时const修饰的对象是确定的，但不能在定义时加括号，不然就和定义时用&amp;ldquo;（）&amp;rdquo;表示的函数类型相混淆了！因此定义时不能写(char *)const *p或者(const char) **p。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;六、问题探讨（由于博文后的留言有字符数目限制，将回复移到这里）&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&lt;span&gt;&lt;strong&gt;问题1&lt;/strong&gt;&amp;nbsp;&lt;/span&gt;（见博文后留言）：&lt;span&gt;讲解非常好，不过有个问题想探讨下： 例如： const char wang[]={"wang"}; char *p; p=wang; 是错误的。 所以char *p中的P不能指向常变量。 （1）需要补充纠正。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&lt;strong&gt;回复&lt;/strong&gt;&amp;nbsp;：&lt;/span&gt;&amp;nbsp;&lt;span&gt;你好！谢谢指正！我在ubuntu 10.04（gcc 4.4.3）下做了如下测试：&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; //test_const.c&lt;br /&gt;&amp;nbsp;&amp;nbsp; #include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; int main()&lt;br /&gt;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; const char wang[]={"wang"};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; char *p;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; p=wang;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; p[2]='c';&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf("p is %s\n",p);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return 0;&lt;br /&gt;&amp;nbsp; }&lt;br /&gt;&lt;strong&gt;编译&lt;/strong&gt;&amp;nbsp;：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; gcc -o test_const test_const.c&lt;br /&gt;&lt;strong&gt;输出如下&lt;/strong&gt;&amp;nbsp;：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; test_const.c: In function &amp;lsquo;main&amp;rsquo;:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; test_const.c:17: warning: assignment discards qualifiers from pointer target type&lt;br /&gt;&lt;strong&gt;执行：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ./test_const&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; p is wacg&lt;br /&gt;&lt;strong&gt;结论：&lt;/strong&gt;&amp;nbsp;&lt;span&gt;根据本博文中第四点－－相容性问题，将const型的wang赋值给p是不合法的。但编译器对其的处理只是警告，因此执行时通过p修改了只读区域的数据。这应该是该编译器处理不严所致后果，不知你用的什么编译器？&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 问题2&amp;nbsp;&amp;nbsp;&lt;/strong&gt;&lt;span&gt;回答&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;a href="http://www.linuxsir.org/bbs/showthread.php?t=239058"&gt;http://www.linuxsir.org/bbs/showthread.php?t=239058&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;&lt;span&gt;提到的问题&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;在c语言里&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;// test.c&lt;br /&gt;int main() {&lt;br /&gt;&amp;nbsp;&amp;nbsp; const char* s1 = "test";&lt;br /&gt;&amp;nbsp;&amp;nbsp; char *s2 = s1;&lt;br /&gt;&amp;nbsp;&amp;nbsp; s2 = "It's modified!";&lt;br /&gt;&amp;nbsp;&amp;nbsp; printf("%s\n",s1);&lt;br /&gt;}&lt;br /&gt;out: It's modified!;&lt;br /&gt;这样也可以吗? 照我的理解岂不是const限定符在c语言里只是摆设一个？&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 回复：&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;span&gt;(1)首先，以上代码编译时会出错warning: initialization discards qualifiers from pointer target type，&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因为char *s2 = s1和问题1提到的一样，不符合相容规则。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;(2)输出结果是正确的，代码分析如下：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;nbsp;&lt;span&gt;int main() {&lt;br /&gt;&amp;nbsp;&amp;nbsp; const char* s1 = "test";&amp;nbsp;&amp;nbsp;&lt;span&gt;//&amp;nbsp; 在只读数据区（objdump -h test后的.rodata区）开辟5字节存储"test",并用s1指向首字符&amp;lsquo;t&amp;rsquo;。&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; char *s2 = s1;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;//&amp;nbsp; s2也指向只读数据区中&amp;ldquo;test&amp;rdquo;的首字符't'。&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; s2 = "It's modified!";&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;&amp;nbsp;//&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;在只读数据区开辟15字节存储"It's modified!"，并将s2由指向't'转而指向"It's modified!"的首字符'I'。&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; printf("%s\n",s1);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;// 从s1所指的&amp;lsquo;t&amp;rsquo;开始输出字符串"test"。&lt;/span&gt;&amp;nbsp;&lt;br /&gt;}&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;br /&gt;(3)总结：提问者的误区在于，误以为s2 = "It's modified!"是对&amp;ldquo;test&amp;rdquo;所在区域的重新赋值，其实这里只是将&amp;ldquo;万能&amp;rdquo;工作指针s2指向另外一个新开辟的区域而已。比如若在char *s2 = s1后再执行s2[2]='a'则是对&amp;ldquo;test&amp;rdquo;的区域进行了写操作，执行时会出现段错误。但这个段错误其实与const没有关系，因为&amp;ldquo;test&amp;rdquo;这块区域本身就是只读的。为了防止理解出错，凡事对于对指针的赋值（比如&lt;/span&gt;&amp;nbsp;&lt;span&gt;s2 = "It's modified!"&lt;/span&gt;&amp;nbsp;&lt;span&gt;），则将其读做：将s2指向&amp;ldquo;&lt;/span&gt;&amp;nbsp;&lt;span&gt;It's modified!&lt;/span&gt;&amp;nbsp;&lt;span&gt;&amp;rdquo;所在区域的首字符。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;(4)额外收获：执行gcc -o test test.c后，&amp;ldquo;test&amp;rdquo;、&amp;ldquo;It's modified!&amp;rdquo;、"%s\n"都被作为字符串常量存储在二进制文件test的只读区&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 域 (.rodata)。事实上，一个程序从编译到运行，对变量空间分配的情况如下：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;A。赋值了的全局变量或static变量=&amp;gt;放在可执行文件的.data段。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;B。未赋值的全局变量或static变量＝&amp;gt;放在可执行文件的.bss段。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;C。代码中出现的字符串常量或加了const的A＝&amp;gt;放在可执行文件的.rodata段。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;D。一般的局部变量＝&amp;gt;在可执行文件中不占空间，在该二进制文件作为进程在内存中运行时才为它分配栈空间。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;E。代码中malloc或new出的变量＝&amp;gt;在可执行文件中不占空间，在该二进制文件作为进程在内存中运行时才为它分配堆空间。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;&lt;strong&gt;&amp;nbsp;问题3：（待进一步分析）&lt;/strong&gt;&amp;nbsp;&lt;/span&gt;验证博文中 三（3）提到的是否为s分配空间，初步分析结果为：不分配！文中的s只是s［0］的地址的代号而已。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;#include&amp;lt;stdio.h&amp;gt;&lt;br /&gt;int main() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int a=3;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char s1[] = "test";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; int b=4;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; char s2[] ="test2";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf("the address of a is %u\n",&amp;amp;a);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf("s1 is %u\n",s1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf("the address of s1 is %u\n",&amp;amp;s1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf("the address of b is %u\n",&amp;amp;b);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf("s2 is %u\n",s2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; printf("the address of s2 is %u\n",&amp;amp;s2);&lt;br /&gt;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;输出结果：&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;the address of a is 3213037836&lt;br /&gt;s1 is 3213037827&lt;br /&gt;the address of s1 is 3213037827&lt;br /&gt;the address of b is 3213037832&lt;br /&gt;s2 is 3213037821&lt;br /&gt;the address of s2 is 3213037821&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;由结果可以看出，编译器做了些优化。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;七、其他相关经典文章转载&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&lt;strong&gt;王海宁,&lt;/strong&gt;&lt;span class="STYLE2"&gt;&lt;a href="http://www.embedu.org/" target="_blank"&gt;华清远见嵌入式学院&lt;/a&gt;&lt;span&gt;讲师，&lt;strong&gt;对const关键字的理解&lt;/strong&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a href="http://www.embedu.org/Column/Column311.htm"&gt;http://www.embedu.org/Column/Column311.htm&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 目前在进行C语言补习时，发现很多的同学对于const这个关键字的理解存在很大的误解。现在总结下对这个关键字理解上的误区，希望在以后的编程中，能够灵活使用const这个关键字。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&lt;strong&gt;1、 const修饰的变量是常量还是变量&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于这个问题，很多同学认为const修饰的变量是不能改变，结果就误认为该变量变成了常量。那么对于const修饰的变量该如何理解那？&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;下面我们来看一个例子：&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;int main&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char buf[4];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const int a = 0;&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a = 10;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这个比较容易理解，编译器直接报错，原因在于&amp;ldquo;a = 10；&amp;rdquo;这句话，对const修饰的变量，后面进行赋值操作。这好像说明了const修饰的变量是不能被修改的，那究竟是不是那，那么下面我们把这个例子修改下：&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;int main&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char buf[4];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const int a = 0;&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;buf[4] = 97;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&amp;ldquo;the a is %d\n&amp;rdquo;,a);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;其中最后一句printf的目的是看下变量a的值是否改变，根据const的理解，如果const修饰的是变量是不能被修改的话，那么a的值一定不会改变，肯定还是0。但是在实际运行的结果中，我们发现a的值已经变为97了。这说明const修饰的变量a，已经被我们程序修改了。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;那综合这两个例子，我们来分析下，对于第二例子，修改的原因是buf[4]的赋值操作，我们知道buf[4]这个变量已经造成了buf这个数组变量的越界访问。buf数组的成员本身只有0,1,2,3，那么buf[4]访问的是谁那，根据局部变量的地址分配，可以知道buf[4]的地址和int a的地址是一样，那么buf[4]实际上就是访问了const int a；那么对buf[4]的修改，自然也修改了const int a的空间，这也是为什么我们在最后打印a的值的时候看到了97这个结果。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;那么我们现在可以知道了，const修饰的变量是不具备不允许修改的特性的，那么对于第一个例子的现象我们又如何解释那。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;第一个例子，错误是在程序编译的时候给出的，注意这里，这个时候并没有生成可执行文件，说明const修饰的变量可否修改是由编译器来帮我们保护了。而第二个例子里，变量的修改是在可执行程序执行的时候修改的，说明a还是一个变量。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;综上所述，我们可以得出一个结论，那就是const修饰的变量，其实质是告诉程序员或编译器该变量为只读，如果程序员在程序中显示的修改一个只读变量，编译器会毫不留情的给出一个error。而对于由于像数组溢出，隐式修改等程序不规范书写造成的运行过程中的修改，编译器是无能为力的，也说明const修饰的变量仍然是具备变量属性的。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&lt;strong&gt;2、 被const修饰的变量，会被操作系统保护，防止修改&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果对于第一个问题，有了理解的话，那么这个问题，就非常容易知道答案了。Const修饰的变量是不会被操作系统保护的。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;其原因是操作系统只保护常量，而不会保护变量的读写。那么什么是常量？比如&amp;ldquo;hello world&amp;rdquo;这个字符串就是被称为字符串常量。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;对于这个问题的另一种证明方法，可以看下面这个程序：&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;int main&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const int a;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char *buf = &amp;ldquo;hello world&amp;rdquo;;&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&amp;ldquo;the &amp;amp;a is %p, the buf is %p\n&amp;rdquo;,&amp;amp;a, buf);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;可以发现buf保存的地址是在0x08048000这个地址附近的，而a的地址是在0xbf000000这个地址附近的，而0x08048000附近的地址在我们linux操作系统上是代码段。这也说明了常量和变量是存放在不同区域的，自然操作系统是会保护常量的。&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;如果我们知道这个道理后，再看下面的题目：&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;int main&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char *buf = &amp;ldquo;hello&amp;rdquo;;&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;buf[0] = &amp;lsquo;a&amp;rsquo;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printf(&amp;ldquo;the buf is %s\n&amp;rdquo;,buf);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;/p&gt;&lt;p class="grey"&gt;&lt;span&gt;我们可以思考下，这个程序的运行结果会是什么呢？&lt;/span&gt;&lt;/p&gt;&lt;div&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/chenleiustc/aggbug/2010001.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2010001.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2009994.html</id><title type="text">c/c++里的 堆区 栈区 静态区 文字常量区 程序代码区</title><summary type="text">一个由C/C++编译的程序占用的内存分为以下几个部分 1、栈区（stack）— 由编译器自动分配释放 ，存放函数的参数值，局部变量的值等。其操作方式类似于数据结构中的栈。 堆(heap)：由malloc,new等分配的空间的地址,地址由低向高增长（程序员释放）。 2、堆区（heap） — 一般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事，分配方式倒是类似于链表。 栈(stack)：是自动分配变量,以及函数调用所使用的一些空间(所谓的局部变量),地址由高向低减少; 3、全局区（静态区）（static）— 全局变量和静态变量的存储是放在一块的，初.</summary><published>2011-04-08T15:25:00Z</published><updated>2011-04-08T15:25:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2009994.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2009994.html"/><content type="html">&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;div class="cnt" id="blog_text"&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp; 一个由C/C++编译的程序占用的内存分为以下几个部分&lt;/span&gt;&lt;/p&gt;&lt;div class="spctrl"&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;1、栈区（stack）&amp;mdash; &lt;span style="color: #ff0000;"&gt;&lt;strong&gt;由&lt;a target="_blank" href="http://baike.baidu.com/view/487018.htm"&gt;编译器&lt;/a&gt;自动分配释放 ，存放&lt;a target="_blank" href="http://baike.baidu.com/view/15061.htm"&gt;函数&lt;/a&gt;的参数值，&lt;a target="_blank" href="http://baike.baidu.com/view/552847.htm"&gt;局部变量&lt;/a&gt;的值等&lt;/strong&gt;&lt;/span&gt;。其操作方式类似于数据结构中的栈。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #ff0000; font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 堆(heap)：由malloc,new等分配的空间的地址,地址由低向高增长（程序员释放）。&lt;/span&gt;&lt;/p&gt;&lt;div class="spctrl"&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;2、堆区（heap） &amp;mdash; &lt;strong&gt;&lt;span style="color: #ff0000;"&gt;一般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收 &lt;/span&gt;&lt;/strong&gt;。注意它与数据结构中的堆是两回事，分配方式倒是类似于&lt;a target="_blank" href="http://baike.baidu.com/view/549479.htm"&gt;链表&lt;/a&gt;。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #ff0000; font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 栈(stack)：是自动分配变量,以及函数调用所使用的一些空间(所谓的局部变量),地址由高向低减少;&lt;/span&gt;&lt;/p&gt;&lt;div class="spctrl"&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;3、全局区（静态区）（static）&amp;mdash; 全局变量和&lt;a target="_blank" href="http://baike.baidu.com/view/675642.htm"&gt;静态变量&lt;/a&gt;的存储是放在一块的，&lt;strong&gt;初始化的&lt;a target="_blank" href="http://baike.baidu.com/view/261041.htm"&gt;全局变量&lt;/a&gt;和静态变量在一块区域， 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放&lt;/strong&gt;。--&amp;gt;分别是data区，bbs区&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div class="spctrl"&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;4、文字常量区 &amp;mdash; 常量字符串就是放在这里的，程序结束后由系统释放 。--&amp;gt;coment区&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div class="spctrl"&gt;&lt;/div&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;5、程序代码区 &amp;mdash; 存放函数体的二进制代码。--&amp;gt;code区&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;二、例子程序&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;这是一个前辈写的，非常详细&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;//main.cpp&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;int a = 0; 全局初始化区&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;char *p1; 全局未初始化区&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;main()&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;{&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;int b; 栈&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;char s[] = "abc"; 栈&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;char *p2; 栈&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;char *p3 = "123456"; 123456\0在常量区，p3在栈上。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;static int c =0； 全局（静态）初始化区&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;p1 = (char *)malloc(10);&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;p2 = (char *)malloc(20);&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;得来得10和20字节的区域就在堆区。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;strcpy(p1, "123456"); 123456\0放在常量区，编译器可能会将它与p3所指向的"123456"优化成一个地方。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;}&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 13px; background-color: #ffcc00;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;在函数体中定义的变量通常是在栈上，用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量，加了static修饰符后不管在哪里都存放在全局区（静态区）,在所有函数体外定义的static变量表示在该文件中有效，不能extern到别的文件用，在函数体内定义的static表示只在该函数体内有效。另外，函数中的 "adgfdf "这样的字符串存放在常量区。&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;还有就是&lt;span style="color: #ff0000;"&gt;函数调用时会在栈上有一系列的保留现场及传递参数的操作&lt;/span&gt;。栈的空间大小有限定，vc的缺省是2M。&lt;span style="color: #ff0000;"&gt;栈不够用的情况一般是程序中分配了大量数组和递归函数层次太深&lt;/span&gt;。有一点必须知道，当一个函数调用完返回后它会释放该函数中所有的栈空间。栈是由编译器自动管理的，不用你操心。&lt;span style="color: #ff0000;"&gt;堆是动态分配内存的，并且你可以分配使用很大的内存。但是用不好会产生内存泄漏。&lt;/span&gt;并且频繁地malloc和free会产生&lt;span style="color: #ff0000;"&gt;内存碎片&lt;/span&gt;（有点类似磁盘碎片），因为&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;c分配动态内存时是寻找匹配的内存的。而用栈则不会产生碎片。在栈上存取数据比通过指针在堆上存取数据快些&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000; font-size: small;" size="3" color="#FF0000"&gt;&lt;span style="font-size: 13px; line-height: 19px;"&gt;&lt;b&gt;&lt;span style="color: #000000; font-size: small;" size="3" color="#000000"&gt;&lt;span style="font-size: 12px; font-weight: normal; line-height: 15px;"&gt;&lt;span style="font-size: 13px;"&gt;堆(heap)和栈(stack)是C/C++编程不可避免会碰到的两个基本概念。首先，这两个概念都可以在讲数据结构的书中找到，他们都是基本的数据结构，虽然栈更为简单一些。在具体的C/C++编程框架中，这两个概念并不是并行的。对底层机器代码的研究可以揭示，&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;栈是机器系统提供的数据结构，而堆则是C/C++函数库提供的&lt;/strong&gt;&lt;/span&gt;。具体地说，现代计算机(串行执行机制)，都直接在代码底层支持栈的数据结构。这体现在，&lt;strong&gt;有专门的寄存器指向栈所在的地址，有专门的机器指令完成数据入栈出栈的操作&lt;/strong&gt;。这种机制的特点是效率高，支持的数据有限，一般是整数，指针，浮点数等系统直接支持的数据类型，并不直接支持其他的数据结构。因为栈的这种特点，对栈的使用在程序中是非常频繁的。&lt;strong&gt;对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把返回地址推入栈，然后跳转至子程序地址的操作，而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。&lt;/strong&gt;C/C++中的自动变量是直接利用栈的例子，这也就是为什么当函数返回时，该函数的自动变量自动失效的原因。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;和栈不同，堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的，而是由函数库提供的。&lt;strong&gt;基本的malloc/realloc/free函数维护了一套内部的堆数据结构。&lt;/strong&gt;当程序使用这些函数去获得新的内存空间时，这套函数首先试图从内部堆中寻找可用的内存空间，&lt;strong&gt;如果没有可以使用的内存空间，则试图利用系统调用来动态增加程序数据段的内存大小，新分配得到的空间首先被组织进内部堆中去，然后再以适当的形式返回给调用者。&lt;/strong&gt;当程序释放分配的内存空间时，这片内存空间被返回内部堆结构中，可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间)，以更适合下一次内存分配申请。这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache)，使用这套机制有如下若干原因：&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配)；这样的话对于大量的小内存分类来说会造成浪费。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color: #ff0000; font-size: small;" size="3" color="#FF0000"&gt;&lt;span style="font-size: 13px; line-height: 19px;"&gt;&lt;b&gt;&lt;span style="color: #000000; font-size: small;" size="3" color="#000000"&gt;&lt;span style="font-size: 12px; font-weight: normal; line-height: 15px;"&gt;&lt;span style="font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;三、堆和栈的理论知识&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2.1申请 方式&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;stack:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;由系统自动&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;。 例如，声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;heap:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;需要程序员自己申请，并指明大小，在c中malloc函数&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;如p1 = (char *)malloc(10);&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;在&lt;strong&gt;C++&lt;/strong&gt;&amp;nbsp;中用new运算符&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;如p2 = (char *)malloc(10);&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;但是注意 &lt;span style="color: #ff0000;"&gt;p1、p2本身是在栈中的&lt;/span&gt;。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2.2&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;申请后系统的响应&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; color: #ff0000;"&gt;栈：只要栈的剩余空间大于所申请空间，系统将为程序提供&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;，否则将报异常提示栈溢出。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;堆：首先应该知道&lt;span style="color: #ff0000;"&gt;操作系统有一个记录空闲&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;地址的链表&lt;/span&gt;，&lt;span style="color: #ff0000;"&gt;当系统 收到程序的申请时，会遍历该链表，寻找第一个空间大于所申请空间的堆结点，然后将该结点从空闲结点链表中删除，并将该结点的空间&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;给程序，另外，对于大多数系统，&lt;strong&gt;会在这块内存&amp;nbsp;空间中的首地址处记录 本次分配&amp;nbsp;的大小&lt;/strong&gt;，这样，代码中的delete语句才能正确的释放本&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;空间&lt;/span&gt;。另外，由于找到的堆结点的大小不一定正好等于申请的大小，系统会自动的将多余的那部分重新放入空闲链表 中。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2.3申请大小的限制&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;栈：在Windows下,栈是向低地址扩展的数据结构，是一块连续的内存&amp;nbsp;的区域&lt;/strong&gt;&lt;/span&gt;。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的，在WINDOWS下，栈的大小是 2M（也有的说是1M，总之是&lt;strong&gt;一个编译时就确定的常数&lt;/strong&gt;），如果申请的空间超过栈的剩余空间时，将提示overflow。因此，能从栈获得的空间较小。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;堆：堆是向高地址扩展的数据结构，是不连续的内存区域。&lt;/span&gt;&lt;/strong&gt;这是由于&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;系统是用链表来存储的空闲内存地址的，自然是不连续的，而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存&amp;nbsp;。由此可见，堆获得的空间比较灵活，也比较大。&amp;nbsp;&lt;/strong&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2.4申请效率的比较：&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; color: #ff0000;"&gt;栈由系统自动&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;，速度较快。但程序员是无法控制的。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px; color: #ff0000;"&gt;堆是由 new&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;的&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;，一般速度比较慢，而且容易产生&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;碎片,不过用起来最方便.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;另外，在 WINDOWS下，最好的方式是用VirtualAlloc&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;，他不是在堆，也不是在栈是直接在进程的地址空间中保留一快&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;，虽然用起来最不方便。但是速度快，也最灵活。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2.5堆和栈中的存储内容&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;栈： 在函数调用时，第一个进栈的是主函数中后的下一条指令（函数调用语句的下一条可执行语句）的地址，然后是函数的各个参数，在大多数的C编译器中，参数是由 右往左入栈的，然后是函数中的局部变量。&lt;strong&gt;注意静态变量是不入栈的。&amp;nbsp;&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;当本次函数 调用结束后，&lt;strong&gt;局部变量先出栈，然后是参数，最后栈顶指针&amp;nbsp;指向最开始存的地址&lt;/strong&gt;，也就是主函 数中的下一条指令，程序由该点继续运行。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&lt;strong&gt;堆：一般是在 堆的头部用一个字节存放堆的大小。&lt;/strong&gt;堆中的具体内容有程序员安排。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2.6存取 效率的比较&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;char s1[] = "aaaaaaaaaaaaaaa";&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;char *s2 = "bbbbbbbbbbbbbbbbb";&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;aaaaaaaaaaa 是在运行时刻赋值的；&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;而 bbbbbbbbbbb是在编译时就确定的；&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;但是，&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;在以后 的存取中，&lt;span style="color: #ff0000;"&gt;在栈&lt;/span&gt;上的数组&amp;nbsp;比指针&amp;nbsp;所指向的字符&amp;nbsp;串(例如堆)快。&amp;nbsp;&lt;/strong&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;比如：&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;＃i nclude&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;void main()&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;{&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;char a = 1;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;char c[] = "1234567890";&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;char *p ="1234567890";&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;a = c[1];&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;a = p[1];&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;return;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;}&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;对应的汇编代码&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;10: a = c[1];&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;0040106A 88 4D FC mov byte ptr [ebp-4],cl&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;11: a = p[1];&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;00401070 8A 42 01 mov al,byte ptr [edx+1]&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;00401073 88 45 FC mov byte ptr [ebp-4],al&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;第一种在读取 时直接就把&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;串中的元素读到寄存器cl中，而第二种则要先把&lt;strong&gt;指针&lt;/strong&gt;&amp;nbsp;值读到edx中，在根据edx读取&lt;strong&gt;字符&lt;/strong&gt;&amp;nbsp;， 显然慢了。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2.7小结：&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;堆和栈的区别可以用如下的比喻来看出：&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size: 13px;"&gt;使用栈就象我们去饭馆里吃饭，只管点菜（发出申请）、付钱、和吃（使 用），吃饱了就走，不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作，他的好处是快捷，但自由度小。&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size: 13px;"&gt;使用堆就象是自己动手做喜欢吃的菜肴，比较麻烦，但是比较符合自己的 口味，而且自由度大。&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;2.8 对比&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;从以上知识可知，&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;栈是系统提供的功能，特点是快速高效，缺点是有限制，数据不灵活；而栈是函数库提供的功能，特点是灵活方便，数据适应面广泛，但是效率有一定降低。&lt;/span&gt;&lt;/strong&gt;栈是系统数据结构，对于进程/线程是唯一的；堆是函数库内部数据结构，不一定唯一。不同堆分的内存无法互相操作。栈空间分静态分配和动态分配两种。静态分配是编译器完成的，比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配无需释放(是自动)，也就没有释放函数。为可移植的程序起见，栈的动态分配操作是不被鼓励的！堆空间的分配总是动态的，虽然程序结束时所有的数据空间都会被释放回系统，但是精确的申请内存/释放内存匹配是良好程序的基本要素。&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="font-size: small;" size="3"&gt;&lt;span style="font-size: 13px; line-height: 19px;"&gt;操作系统方面的堆和栈，如上面说的那些，不多说了。还有就是数据结构方面的堆和栈，这些都是不同的概念。这里的堆实际上指的就是（满足堆性质的）优先队列的一种数据结构，第1个元素有最高的优先权；栈实际上就是满足先进后出的性质的数学或数据结构。虽然堆栈，堆栈的说法是连起来叫，但是他们还是有很大区别的，连着叫只是由于历史的原因。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: small;" size="3"&gt;&lt;span style="font-size: 13px; line-height: 19px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;堆和栈的生长方向恰好相反&lt;/span&gt;&lt;/strong&gt;，&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;|--------------| 低地址&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;| 堆 |&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;|--------------|&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;| | |&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;| I |&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;| |&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;| ^ |&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;| 栈 | 高地址&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;-----------------&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;所以计算机中的堆和栈经常时放一块讲的&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;一般不是必要就不要动态创建，最讨厌把new出来的东西当局部变量用，用万了马上delete 的做法.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;理由&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size: 13px; color: #ff0000;"&gt;1.栈分配比堆快，只需要一条指令就呢给配所有的局部变量&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size: 13px; color: #ff0000;"&gt;2.栈不会出现内存碎片&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size: 13px; color: #ff0000;"&gt;3.栈对象好管理&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;当然，某些情况下也要那么写,比如&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;1.对象很大&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;2.对象需要在某个特定的时刻构造或析够&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;3.类只允许对象动态创建,比如VCL的大多数类&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;当然，必须用堆对象时也不能躲避&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size: 13px;"&gt;堆内存和栈内存各有什么作用？&lt;/span&gt;&lt;strong&gt;&lt;span style="font-size: 13px; color: #ff0000;"&gt;堆：顺序随意 &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/strong&gt;&lt;strong&gt;&lt;span style="font-size: 13px; color: #ff0000;"&gt;栈：先进后出&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;----------------------------------------------------------------------------------------------------------------------&amp;nbsp;&lt;/span&gt;&lt;span style="font-size: small;" size="3"&gt;&lt;span style="font-size: 13px; line-height: 19px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;为什么说在堆上&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;比在栈上&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;慢？堆空间的开辟需要用系统函数，栈上直接修改&lt;strong&gt;指针&lt;/strong&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;堆空间的&lt;a target="_self"&gt;&lt;strong&gt;管理&lt;/strong&gt;&amp;nbsp;&lt;/a&gt;需 要系统记帐，栈上的空间可以由编译器管理或是保存在某个处理器寄存器中。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;堆空间的释放需要系统管理，栈上的释放可以直接丢弃。堆空间需要通过栈上的&lt;strong&gt;指针&lt;/strong&gt;&amp;nbsp;间接引用，所以访问会慢&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;记得在apue2上面看到关于线程中有这样一段话,大致意思是,一个 线程有自己的堆栈,可以在堆栈上&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;,比如说一个结构体,如果这个线程调用了pthread_exit()返回这个结构体&lt;strong&gt;指针&lt;/strong&gt;&amp;nbsp;的时候之后要特别的小心,因为很有可能这个结构体里面的成员值发生改变,这个可以理解,因为同一个进程所 有线程的资源是共享的,当这个线程退出之后那部分以前用过的堆栈很可能被其它线程占用,但同时又说如果malloc就不会出现这样的问题,&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;比如，在栈上分一个int，只要esp-4就可以了，&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;在堆上系统要记录被&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;的信息，以便释放&amp;nbsp;&lt;/span&gt;&lt;span style="font-size: small;" size="3"&gt;&lt;span style="font-size: 13px; line-height: 19px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: 13px;"&gt;－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;方式有三种： &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp; 1.从静态存储区域&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;。&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;在程序编译的时候就已经&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;好，这块&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;在程序的整个运行期间都存在。例如全局变 量，static变量。 &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp; 2.在栈上创建。在执行函数时，函数内局部变量的存储&lt;strong&gt;单元&lt;/strong&gt;&amp;nbsp;都可以在栈上创建，函数执行结束时这些存储&lt;strong&gt;单元&lt;/strong&gt;&amp;nbsp;自动被 释放。栈&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;运算内置于处理器的指令集中，效率很 高，但是&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;的&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;容量有限。 &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp; 3.从堆上&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;，亦称动态&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;。程序在运行的时候用malloc或new申请任意多少的&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;，程 序员自己负责在何时用free或delete释放&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;。动态&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;的生存期由我们决定，使用非常灵活，但问题也最多。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;一般所说的堆栈（stack)往往是指栈，先进后出，&amp;nbsp;它是一块&lt;strong&gt;内存&lt;/strong&gt;&amp;nbsp;区。用以存放程序的局部变量，临时变量，函数的参数，返回地址等。在这块区域中的变量的&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;和释放由系统自动进行。不需要用户的参与。 &amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;&amp;nbsp;&amp;nbsp; 而在堆(heap,先进先出)&amp;nbsp;上的空间则是由用户进行&lt;strong&gt;分配&lt;/strong&gt;&amp;nbsp;，并由用 户负责释放。&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 13px;"&gt;===================================================================================&amp;nbsp;&lt;/span&gt;&lt;span style="font-size: small;" size="3"&gt;&lt;span style="font-size: 13px; line-height: 19px;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;div&gt;&lt;span style="font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;img src="http://www.cnblogs.com/chenleiustc/aggbug/2009994.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2009994.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2009888.html</id><title type="text">warning: deprecated conversion from string constant to &amp;quot;char *&amp;quot; 解决方案</title><summary type="text">Linux 环境下当GCC版本比较高时，编译代码可能出现的问题问题是这样产生的，先看这个函数原型：void someFunc(char *someStr);再看这个函数调用：someFunc("I'm a string!");把这两个东西组合起来，用最新的g++编译一下就会得到标题中的警告。为什么呢？原来char *背后的含义是：给我个字符串，我要修改它。而理论上，我们传给函数的字面常量是没法被修改的。所以说，比较和理的办法是把参数类型修改为const char *。这个类型说背后的含义是：给我个字符串，我只要读取它。很自然的延伸一下。 如果我既要传字面常量又要传字</summary><published>2011-04-08T12:38:00Z</published><updated>2011-04-08T12:38:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2009888.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2009888.html"/><content type="html">&lt;p&gt;&lt;span lang="en-US"&gt;Linux &lt;/span&gt;&lt;span lang="zh-CN"&gt;环境下当&lt;/span&gt;&lt;span lang="en-US"&gt;GCC&lt;/span&gt;&lt;span lang="zh-CN"&gt;版本比较高时，编译代码可能出现的问题&lt;/span&gt;&lt;/p&gt;&lt;p&gt;问题是这样产生的，先看这个函数原型：&lt;/p&gt;&lt;p&gt;&lt;span&gt;void someFunc(char *someStr);&lt;/span&gt;&lt;/p&gt;&lt;p&gt;再看这个函数调用：&lt;/p&gt;&lt;p&gt;&lt;span&gt;someFunc("I'm a string!");&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;把这两个东西组合起来，用最新的&lt;/span&gt;&lt;span&gt;g++&lt;/span&gt;&lt;span&gt;编译一下就会得到标题中的警告。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;为什么呢？原来&lt;/span&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;char *背后的含义是：给我个字符串，我要修改它。&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;而理论上，我们&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;传给函数的字面常量是没法被修改的&lt;/span&gt;&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;&lt;span&gt;所以说，比较和理的办法是&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;把参数类型修改为&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;const char *&lt;/span&gt;。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;这个类型说背后的含义是：&lt;strong&gt;给我个字符串，我只要读取它。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;很自然的延伸一下。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;如果我既要传字面常量又要传字符串变量怎么办呢？&lt;/span&gt;&lt;span&gt;......&lt;/span&gt;&lt;span&gt;重载&lt;/span&gt;&lt;/p&gt;&lt;p&gt;实验：&lt;/p&gt;&lt;p&gt;对deprecated conversion from string constant to 'char *'此类警告的详细解释&lt;br /&gt;&lt;br /&gt;假定你想使用一个char*类型的变量，有时指向一个字符串，有时指向另外一个字符串。开始的代码就像这样：&lt;br /&gt;char *msg;&lt;br /&gt;msg = "hello";&lt;br /&gt;msg = "good-bye";&lt;br /&gt;&lt;br /&gt;编译器会对这段代码给出两段警示，说&amp;rdquo;deprecated conversion from string constant to 'char *'"，意思就是说你没有能力修改字符串的内容。如果将代码写成这样，如：&lt;br /&gt;char *msg = "hello";&lt;br /&gt;*msg = 'j';&lt;br /&gt;printf( "%s\n", "hello" );&lt;br /&gt;&lt;br /&gt;编译器会通过编译，实际上会将msg指向的内容从"hello"转变为"jello", 正确的解决方法是将&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;msg声明为一个指向不变字符串的指针&lt;/strong&gt;&lt;/span&gt;：&lt;br /&gt;const char&amp;nbsp; *msg;&lt;br /&gt;msg = "hello";&lt;br /&gt;msg = "good-bye";&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #ff0000;"&gt;这段代码可以成功编译，并且将msg指向的值如愿改变，但如果&lt;strong&gt;你将指针指向的指进行赋值&lt;/strong&gt;：&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;*msg = 'j';&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;将会产生一个错误，不能修改一个字符串常量&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;注意如下的代码，此代码编译时不会出现警告也不会出现任何错误：&lt;br /&gt;const char&amp;nbsp; *msg;&lt;br /&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;char&amp;nbsp;&amp;nbsp; buf[ 10 ]; &amp;nbsp; //注意不能使用char *buf；&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;sprintf( buf, "%03d\n", 7 );&lt;br /&gt;msg = buf;&lt;br /&gt;&lt;br /&gt;改变buf的内容是可以的，因为它并没有被声明为常量。在这种情况下，msg将指向一个字符串，"007\n". 像这种语句&lt;br /&gt;*buf = 'x';&lt;br /&gt;将会正确编译执行，但像&lt;br /&gt;&lt;strong&gt;*msg = 'x';&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;将会产生一个警告，因为msg指向的内容不允许改变&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;还有一种方法是&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;使用强制转换&lt;/strong&gt;&lt;/span&gt;，使用强制转换意味着你清楚会出现什么情况，不需要编译器为你做出判断，例如下面的代码将不会产生警告：&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;char&amp;nbsp; *msg;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color: #ff0000;"&gt;msg = (char *) "hello";&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;但一旦你使用强制转换，编译器对如下语句进行编译时，也不会出现错误或警告&lt;br /&gt;*msg = 'j';&lt;br /&gt;&lt;br /&gt;这个错误将一直存在，但并不会被发现，直到运行时。那时再找出错点就相当麻烦了，比编译器提醒你，麻烦多了。所以，最好不要对字符串使用强制转换。&lt;br /&gt;&lt;br /&gt;Constant 指针&lt;/p&gt;&lt;p&gt;根据constant的位置不同，可以有以下四种情况：&lt;br /&gt;&lt;strong&gt;const char* const msg_0;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;const char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *msg_1;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;char* const msg_2;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; *msg_3;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;其中，msg_0是一个constant指针指向一个const字符串。这个声明编译器会给出一个警告，因为msg_0的指向没有被初始化，而且之后的语句也无法对mg_0进行赋值，如&lt;br /&gt;const char const *msg_0 = "hello";&lt;br /&gt;会编译成功，但&lt;br /&gt;*msg_0 = 'j';或者&lt;br /&gt;msg_0 = "good-bye"; 将会产生错误&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;msg_1既可以指向一个const字符串，也可以指向一个可变的字符串，但是不能修改它所指向的字符串的内容。&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;编译msg_2这条语句，会出现和编译msg_0一样的错误。&lt;strong&gt;因为指针是一个常量，所以它应该首先被赋值&lt;/strong&gt;。&lt;strong&gt;如果刚开始已经赋值，那么它可以对指向的字符串内容进行修改&lt;/strong&gt;，如：&lt;br /&gt;char&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; buf[ 10 ];&lt;br /&gt;char * const msg_2 = buf;&lt;br /&gt;这段代码里，msg_2指向buf[0],并且永远指向这个地址，不会改变；&lt;br /&gt;&lt;br /&gt;对于msg_3，就没太多可以说的。你可以改变指针，也可以改变指针指向的内容；&lt;/p&gt;&lt;img src="http://www.cnblogs.com/chenleiustc/aggbug/2009888.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/chenleiustc/archive/2011/04/08/2009888.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2009/12/07/1618280.html</id><title type="text">ORACLE实例与数据库的区别</title><summary type="text">刚接触ORACLE的人肯定会对实例和数据库感到困惑，实例到底代表些什么？为什么会有这个概念的出现？ORACLE实例 = 进程 + 进程所使用的内存(SGA)实例是一个临时性的东西，你也可以认为它代表了数据库某一时刻的状态！数据库 = 重做文件 + 控制文件 + 数据文件 + 临时文件数据库是永久的，是一个文件的集合。ORACLE实例和数据库之间的关系1.临时性和永久性2.实例可以在没有数据文件的情...</summary><published>2009-12-06T16:34:00Z</published><updated>2009-12-06T16:34:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2009/12/07/1618280.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2009/12/07/1618280.html"/><content type="text">刚接触ORACLE的人肯定会对实例和数据库感到困惑，实例到底代表些什么？为什么会有这个概念的出现？ORACLE实例 = 进程 + 进程所使用的内存(SGA)实例是一个临时性的东西，你也可以认为它代表了数据库某一时刻的状态！数据库 = 重做文件 + 控制文件 + 数据文件 + 临时文件数据库是永久的，是一个文件的集合。ORACLE实例和数据库之间的关系1.临时性和永久性2.实例可以在没有数据文件的情...</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2009/11/11/1601369.html</id><title type="text">[原]Linux网络编程学习笔记</title><summary type="text">我们要讨论的第一个结构类型是：struct sockaddr，该类型是用来保存socket信息的： struct sockaddr { unsigned short sa_family; /* 地址族， AF_xxx */ char sa_data[14]; /* 14 字节的协议地址 */ }; sa_family一般为AF_INET；sa_data则包含该socket的IP地址和端口号。 另外...</summary><published>2009-11-11T14:37:00Z</published><updated>2009-11-11T14:37:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2009/11/11/1601369.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2009/11/11/1601369.html"/><content type="text">我们要讨论的第一个结构类型是：struct sockaddr，该类型是用来保存socket信息的： struct sockaddr { unsigned short sa_family; /* 地址族， AF_xxx */ char sa_data[14]; /* 14 字节的协议地址 */ }; sa_family一般为AF_INET；sa_data则包含该socket的IP地址和端口号。 另外...</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2009/11/11/1601205.html</id><title type="text">[zz]Vim 的标签页功能</title><summary type="text">虽然一直在使用 Vim 写程序与文档，但是一直没有深究其标签页 (Tab Page) 功能的具体使用方法。现在开始重视这个问题，毕竟使用 Vim 的时间越来越少了，当然这只是针对我的所有生命而言。1 建立新的标签页使用 Vim 在当前工作目录中新建或打开某一文件并开启新标签页显示文件内容，操作如下：:tabnew filename使用 tabf 指令基于正则表达式递归遍历当前工作目录查找名称匹配的...</summary><published>2009-11-11T09:52:00Z</published><updated>2009-11-11T09:52:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2009/11/11/1601205.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2009/11/11/1601205.html"/><content type="text">虽然一直在使用 Vim 写程序与文档，但是一直没有深究其标签页 (Tab Page) 功能的具体使用方法。现在开始重视这个问题，毕竟使用 Vim 的时间越来越少了，当然这只是针对我的所有生命而言。1 建立新的标签页使用 Vim 在当前工作目录中新建或打开某一文件并开启新标签页显示文件内容，操作如下：:tabnew filename使用 tabf 指令基于正则表达式递归遍历当前工作目录查找名称匹配的...</content></entry><entry><id>http://www.cnblogs.com/chenleiustc/archive/2009/10/29/1592540.html</id><title type="text">［zz］安装mysql 获得 mysql.h 建立C接口</title><summary type="text">先安装mysql代码:sudo apt-get install mysql-server mysql-client再装开发包代码:sudo apt-get install libmysqlclient15-dev安装完以后，C代码里添加头文件代码:#include &lt;mysql.h&gt;编译方法：代码:gcc $(mysql_config --cflags) xxx.c -o xxx $(...</summary><published>2009-10-29T12:44:00Z</published><updated>2009-10-29T12:44:00Z</updated><author><name>心之镇</name><uri>http://www.cnblogs.com/chenleiustc/</uri></author><link rel="alternate" href="http://www.cnblogs.com/chenleiustc/archive/2009/10/29/1592540.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/chenleiustc/archive/2009/10/29/1592540.html"/><content type="text">先安装mysql代码:sudo apt-get install mysql-server mysql-client再装开发包代码:sudo apt-get install libmysqlclient15-dev安装完以后，C代码里添加头文件代码:#include &lt;mysql.h&gt;编译方法：代码:gcc $(mysql_config --cflags) xxx.c -o xxx $(...</content></entry></feed>
