阅读笔记–《C++实践之路》
第三章–指针
可能看不了图片,我用的atom写的,传图片伤时间,可以到github上面看有图片的,点击这个就行了
指针与引用
谈到指针,就不得提一提它的兄弟引用(不得不说他们是很像,以前刚
开始是傻傻分不清的)下面来探讨一下:
指针和引用都一个相同的作用:让同一个定义的变量指向不同的对象
上面这个应该不难理解,但有一些细节还是需要我们注意一下的,让我
们来看看下面这三行代码中的道道:
int * pValue;
int TheValue = 10;
pValue = &TheValue;//嘿嘿,对于初学者来说道道就在这了
//上面的 & 符号取地址的作用,即把 TheValue 的内存地址给 pValue
//接下来我们来看看输出地址的结果
std::cout << pValue;
//输出的结果如下:
![1](./photo/chrpter three/1.jpg)
//上面输出的便是这个变量的值的存储地址了是十六进制的
//如果在 pValue 前面加上 * 号--*pValue, 输出的便是变量的值了
//接下来我们把第三个赋值语句改成下面这个
pValue = TheValue;//这样是不能通过编译的,因为左边是整型指针,右边是整型,类型不同
//想到还有强制类型转换,用强制类型转换试试,再改成下面的
pValue = (int *) TheValue;//嘿嘿,可以通过编译,让我们来看下结果
![2](./photo/chrpter three/2.jpg)
//可以看到这个地址明显和上面的输出不一样,这个时候呢是很容易出事的!
//让我们来输出pValue的值来看看
std::cout << *pValue << std::endl;
![3](./photo/chrpter three/3.jpg)
/*看看,出事了.因为我从开始到现在基本是用Java(这个没指针),对指针的出事概率没有深刻
的体会,但看到很多人对指针还是很难过的(我也走上了这条不归路了!马的)还有就是要记得初
始化指针,不要没初始化就使用,这样也会出现(VS现在能提示未初始化的错误,还是可以的)。*/
指针有什么优势(为什么要使用指针)
下面讲述的是自己感觉的,查的资料不全:
指针存在的目的是–提高内存的使用率:看下面的代码讲述我的想法
#include <iostream>
class Test //定义一个类来进行测试,名字乱起的
{
public:
Test(int value) { i = value; };
//下面的析构函数是测试的关键,我们让这个对象销毁的时候打印 dead,以便观察对象是否销毁
~Test() { std::cout << "dead" << std::endl; };
const int getValue() { return i; }
private:
int i = 8;
};
int main()
{
//初始化定义对象 t 并且它的值为9
Test t(9);
//使对象 t 重新指向值为3的,可以知道值为9的没有用,看看会不会自己销毁
Test t = Test(3);
std::cout << t.getValue << std::endl;
system("pause");
return 0;
}
运行的结果如下:
![4](./photo/chrpter three/4.jpg)
从上面可以看出,值为9的并没有被销毁,它一直在内存中。我们知道局部变量在相应的运行阶段完毕以后会被系统自动销毁,而在main函数中定义的变量将存在到程序运行结束。
还有一种情况是在函数中定义的变量占内存比较大,虽然在函数结束时内存会自己释放,但内存占用过多会对运行的效果有影响(就行电脑卡阿卡的情况)。
像上面的这两种情况会存在许多需要释放的内存占用,而使用指针可以及时的释放不需要的内存占用,提高内存的使用效率。像下面的这段代码:
#include <iostream>
class Test
{
public:
Test(int value) { i = value; };
~Test() { std::cout << "dead" << std::endl; };
const int getValue() { return i; }
private:
int i = 8;
};
int main()
{
Test t = new Test(9);
delete(t);//此时不需要这个值为9的了,就释放它的内存
/*再指向新的值3*/
t = new Test(3);
std::cout << t->getValue() << std::endl;
system("pause");
return 0;
}
相关的知识点
数组
让我们来看看下面一段简单代码的输入:
#include <iostream>
int main()
{
int array[5];
for (int i = 0; i < 5; i++) {
std::cout << &array[i] << std::endl;
}
system("pause");
return 0;
}
结果:
![5](./photo/chrpter three/5.jpg)
我们可以看到上面每个数组的地址之间相差为5,联系以前学的变量的存储空间的知识点–int的存储空间是4个字节。哈哈,是不是挺有趣的。从这我们就可以知道数组其实是一段连续的内存地址的相同变量组合,遍历的时候可以用地址递增的方法,如下:
#include <iostream>
int main()
{
char array[5] = { 'a', 'b', 'c', 'd', 'e' };
char * p;
p = array;
while (*p != '\0' ) {//字符数组是以‘\0’结尾的
std::cout << *p << std::endl;
++p;//为啥加一就行,别问我,我其实也不知道
}
system("pause");
return 0;
}
结果如下:
![6](./photo/chrpter three/6.jpg)
疑问
这里要注意的是字符数组的结尾标志是‘\0’,但好像整型和其他就没有了!我能想到的是把整型转为字符型,而在转为字符型的时候也要判断结束标志啊!我就想知道C++中的sizeOf()这个函数是怎么实现的?我觉得是系统自己加的结束标志.
main 函数的命令行调用
main 函数有如下形式:
int main (int argCount, char * argumints [])
其中,第一个参数 argCount 经包含保存在第二个参数数组 argumints中的字符串数量;第一个字符串 argumints[0] 包含调用程序的名字,余下的包含调用程序的命令行参数。例如程序名为 testing.exe, 你可以通过三个参数从命令行调用它:
testing con two three
令人头晕的变量定义
看如下变量定义:
class Link;
...........
Link const * pLink;//指向常量的指针
Link * const pLink = pInitPr;//常量指针
Link const * const pLink = pInitPtr;//指向常量的常量指针
怎么样,晕不晕,哈哈。
其中指向常量的指针表示不能通过它进行操作,想改变它的值之类的;常量指针是不能移动的指针,即不能够再指向其他的变量了(它只能一辈子不离不弃的跟着第一个爱上的它,而那个被爱上是它也只能让它跟着,摆脱不了,但其他的也能爱上它爱上的它,好幸福啊!)就像古代的一夫多妻,但是那可以修理它(修改它的值);最后一个指向常量的常量指针也是古代的一夫多妻,但修理不了它(不能修改它的值)。