From 1b943cfd1a8346c03b58347c10f9db10b16a710b Mon Sep 17 00:00:00 2001 From: He Junqiu Date: Tue, 25 Aug 2015 20:16:34 +0800 Subject: [PATCH 1/2] =?UTF-8?q?changes:update=20to=20'=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E6=8C=87=E9=92=88=E6=8C=87=E5=90=91=E7=9A=84=E5=86=85=E5=AD=98?= =?UTF-8?q?'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reference.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/reference.md b/reference.md index d838832..ff6c8fa 100644 --- a/reference.md +++ b/reference.md @@ -23,9 +23,7 @@ int num; double which = 0.0; int *poniter = new int(0); ``` -上面的`num`、`which`、`pointer`所在空间存在于栈上,为什么呢?因为你的代码已经表明了我要用 -这三个变量,系统就会在栈上开辟一定等值大小的空间供你使用。也许你会问了,这儿有一个指针 -指针难道不是堆上面的吗?嗯,我承认你问的很好,但是`int *pointer`pointer却是在栈上,只不过它存 +上面的`num`、`which`、`pointer`所在空间存在于栈上,为什么呢?因为你的代码已经表明了我要用这三个变量,系统就会在栈上开辟一定等值大小的空间供你使用。也许你会问了,这儿有一个指针,指针难道不是堆上面的吗?嗯,我承认你问的很好,但是`int *pointer`pointer却是在栈上,只不过它存 的值是一个地址数值,这个数值指向堆空间,所以`new int(0)`申请到的内存才是堆空间的。 ## 指针初始化 @@ -77,18 +75,19 @@ int main() >x86 pointer-address:00F6F730 -第一行输出了指针`pointer`所在栈空间的地址,第二行输出了`pointer`所指向堆空间的首地址。 +第一行输出了`pointer`所指向堆空间的首地址,第二行输出了指针`pointer`所在栈空间的地址。 可以看出他们是不同的哦,请结合第二节的`内存分配`。 希望你不要搞混了。输出的数据是16进制的,关于进制请参考[编码](./encoding.md),这里简要说明一下16进制,16进制的1位数字代表了 -二进制的4位,所以这里总共是32位。 +二进制的4位(bit),所以这里总共是32位。 -在上面的代码中,我提到了`x86`,嗯,x86是一种架构,一般指32位的操作系统。那么,你想不想知道在`x64`系统上,这一段代码将会输出什么呢?我的机器是`x64`的 +在上面的代码中,我提到了`x86`,嗯,x86是一种架构,一般指32位的操作系统。那么,你想不想知道在`x64`系统上,这一段代码将会输出什么呢?我的机器是`x64`的 在VS2015中,切换到`x64`编译模式下(其它编译器也可以,但得你自己去搜索切换的方法,VS系列的都是一种切换方法)。 >值得一提的是,如果你的操作系统是`x86`的,那么无法切换到`x64`编译模式下。 好了,现在它输出了这些: >x86 pointer:000000EEFA8E96E0 + >x86 pointer-address:000000EEFA89F748 从输出可以看出,值是64位的。 @@ -189,6 +188,7 @@ int *dynamicArray = new int[9]; } ``` 这样的代码,编译不会报错,但运行期的时候,就会令程序崩溃。超出数组界限,即越界。 + ### 数组索引与取值 C++中的数组,和C是一样的,如果你有C语言的基础,那么此节可以不用看。不过,作点了解还是可以的。 @@ -201,9 +201,80 @@ cout << intArray[0]; >底层是这样实现的,intArray[i] = intArray + i \* 4;如果是从1开始,则变成了 >intArray[i] = intArray + (i-1) \* 4。这样就会多了一次计算,对于追求高效率的C/C++来说,是不能容忍的。 +>值得注意的是,索引的数值只能是整数。 + ### 二维数组初始化 +在前面小节中,提到的是一维数组。一维数组可以理解成是一条数轴上整数点,数组的下标即整数点。二维数组呢,你可以理解成是[直角坐标系](http://baike.baidu.com/view/1539320.htm)。 + +在编程语言中,形如array[5][6]来表示数学上的直角坐标系,也被称作『二维数组』,第一个方括号表示数学上的x轴,即编程语言中的二维,第二个表示数学上的y轴(当然,你也可以反向理解,将xy的位置交换都是可以的),即编程语言中的一维。 +一维就可以和一维数组联系起来了,二维就是表示有多少个一维数组。 + +*code segment 1* +```C++ +int array[4][5];// 直接定义在栈上的静态二维数组。未初始化。 +int arrayInit[3][2] = {{0, 1}, {2, 3}, {4, 5}};// 静态二维数组的初始化。mark - 1 +``` +请注意`mark - 1`那一行的代码,外层的`{}`控制了二维的大小,内层的3个`{}`则表明了一维数组的个数。我们在初始化的时候也是不能超出 +界限的,比如这样写是会报错的`int arrayInit[3][2] = {{0, 1}, {2, 3}, {4, 5, 6}};`最后的`{4, 5, 6}`超过了我们的设定值『2』的范围。 +可以少初始化,但不能多初始化! + +*code segment 2* +```C++ +int **array = new int*[5];// 指定了二维的大小为5。 mark - 2 +// 循环申请一维数组,一维数组大小为3 +for (int i = 0; i < 5; ++i) { + array[i] = new int[3]; +} +``` +也许你会在`mark - 2`那行这样写动态二维数组的内存申请`int **array = new int[5][3]`,但是很遗憾,这样写,编译器根本不认! +这是为什么呢?原来编译器无法确定二维数组的一维内存在哪儿,也许你的一维要指向栈空间呢(这是有可能,我们在后面的小节将会讲,请留心)。 +所以需要我们对每一个一维手动指定内存。 + ### 指针数组 +在C语言和C++语言中,数组元素全为指针的数组称为指针数组。一维指针数组的定义形式为:“类型名 *数组标识符[数组长度]”。 +例如,一个一维指针数组的定义:int *ptr_array[10]。via [百度百科](http://baike.baidu.com/view/2072881.htm) + ### 数组指针 +数组名的指针,即数组首元素地址的指针。即是指向数组的指针。 +例:int (*p)[10]; p即为指向数组的指针,又称数组指针。 via [百度百科](http://baike.baidu.com/view/3026322.htm) + +指针数组和数组指针,读者只需要记住一件事情,就是它们都是建立在二维数组的基础上的。在这里,对它们就不做过多讲解,前面讲的二维数组的知识要点恰好可以通过这两节来提升对指针和数组的认识。 + +>学有余力的读者,可以点击链接查看更深的内容。 + +## 释放指针指向的内存 +有申请内存空间,在必要的时候就得把内存返回给操作系统。在C++中,我们用delete关键字来释放内存。 +delete可以用来释放普通指针和数组,但是表达形式上不一样,到达的效果也不一样。 +```C++ +int *ptr = new int(2); +delete ptr; +ptr = nullptr; +``` + +回收普通指针用delete,记得在delete后将指针赋值为[nullptr](http://baike.baidu.com/item/nullptr) + +delete只负责把指针指向的内存回收掉,但不会帮我们把指针的内容改变。//TODO:vs截图查看delete后指针的值 + +```C++ +int *ptrArray = new int[12]; +delete[] ptrArray; +``` + +回收数组用delete[],对数组一定要采取这种做法,一定要采取这种做法,一定要采取这种做法,重要的事情说三遍。 +很初学者,甚至学了几年的编程都会犯这个错。 + +为什么会有`delete`和`delete[]`区别呢。其实,在内部实现上,它们两个是不同的。对于基础数据类型,使用其中之一都是可以的,但是一旦涉及到对象指针后,就不可乱用了, +`delete`会调用对象的析构函数(编译器自动完成,细节请参考其它章节),而`delete[]`会负责把数组里的每一个对象的析构函数都调用一遍。所以,使用`delete`去释放 +存有对象的数组,不仅引起内存泄露,还有可能会造成程序崩溃(比如:Heap error) + +例如: +```C++ +// 假设我们有一个类叫作「Human」 +Human *objects = new Human[25]; +delete objects;// 可能会引起程序错误! +delete []objects;// 正确的释放方式 +``` + ## 值传递 ## 指针传递 ## 指针和结构体 From 8b1b8f5479b42ae26283851b6f233700758265f7 Mon Sep 17 00:00:00 2001 From: "CHE.He.Junqiu" Date: Wed, 26 Aug 2015 11:56:30 +0800 Subject: [PATCH 2/2] Update reference.md --- reference.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/reference.md b/reference.md index ff6c8fa..5531d1e 100644 --- a/reference.md +++ b/reference.md @@ -178,14 +178,14 @@ dynamicArray = new int[4];// compiler OK! ### 数组初始化 ```c++ int intArray[5] = { 0, 1 };// 将数组的前两个的值初始化为0和1,后面的值不定,看编译器决定。VS2015是默认后面的为0。 -int *dynamicArray = new int[9]{ 1,2,3,4,5,6,7,8,9 }; +int *dynamicArray = new int[9]{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; ``` 初始化的时候,不能超过数组大小,超过就会编译报错。不过,请看下面的代码: ```C++ int *dynamicArray = new int[9]; - for (int i = 0;i < 10;++i) { - dynamicArray[i] = i; - } +for (int i = 0;i < 10; ++i) { + dynamicArray[i] = i; +} ``` 这样的代码,编译不会报错,但运行期的时候,就会令程序崩溃。超出数组界限,即越界。 @@ -194,7 +194,7 @@ C++中的数组,和C是一样的,如果你有C语言的基础,那么此节 数组通过下标操作`operator[]`操作,来取得数组的对应元素的值,索引是从0开始计算的。代码为先: ```c++ -int intArray[5] = { 0, 1, 2,3 ,4 }; +int intArray[5] = { 0, 1, 2, 3, 4 }; cout << intArray[0]; ``` >你是否想问,为什么从0开始算下标。这其实是为了方便计算。例如,以上面代码为例,想取值intArray[i]