今天继续对指针进行学习,正式正入主题:
字符指针:
对于上面的理论,下面用程序进行论证:
输出结果:
所以论证了:
str1是字符数组变量,str1保存了字符串"cppcourse"
str2是字符指针变量,str2并不保存字符串“cppcourse”本身,而是保存常量"cppcourse"的地址
编译失败:
所说又论证了“由于str2指向的是字符串常量“cppcourse” ,常量不能被更改,因而str2[0] = 'C';将引发错误。”
字符数组与字符指针的区别:
下面分别从以下几个方面来解析:
存储分配
读写权限
这个上面已经论证过了:
字符数组 是可读可写的
字符指针是只读的
初始化
字符数组初始化是直接把字符串保存到字符数组所分配的这6个字节的空间当中;
字符指针仅仅只是把常量的地址保存到了字符指针这4个字节的空间当中而已;
执行效率
字符数组是直接访问;而字符指针,需通过字符常量地址去间接访问字符串,所以字符数组比字符指针的效率更高。
字符串拷贝:
来看一下下面程序:
编译:
需要用到strcpy库函数来赋值:
这时就正常执行了:
接下来,我们用自己的方式来实现库函数strcpy同样的功能,来加深对字符串指针的使用:
第一种实现:
运行结果:
下面来解释一下图中标的①、②:
①:
while ((*dest++ = *src++) != ' ');等价于:
*dest = *src;
dest++;
src++;
所以,这样的话程序就比较好理解了,为了对于这种复杂表示式有一个更清楚的理解,做下面一些实验,理解上面这样的写法就不难了:
实验一:
输出:
实验二:
输出结果:
实验三:
输出结果:
输出结果:
所以,通过上面的几个实验就能很轻松地理解上面的这种实现字符串拷贝的代码啦。
注意:&、*运算的优先级要大于算数运算,所以*dest++,首先是先算*desc,再算++。
②:Assert断言,类似于junit中的一样,也就是如果断言失败,则整个程序会终止,并给出提示,拿它来容错比较好:
输出结果:
第二种实现:
输出:
下面来对其实现代码进行解析:
思路:算出目标地址与原地址之间间隔数,利用这个间隔数是恒定的原则,来达到数组拷贝的目的,下面用一个图来说明其实现原理:
通过上面的简单流程,就可以很清楚地理解这种实现方式了,总结一下这种实现方式:
效率要比第一种要高,因为只操作了一个指针,指针属于间接方式,性能没有直接使用变量高。
指针与const:
先回顾一下const的用法:
定义常量时,一般需要进行初始化,如果不初始化,在C语言中会有警告,而在C++则会编译出错:
编译:
这时运行,它的值是不确定的:
当常量被定义之后,就不能再次赋值了【这也是常量的意义】:
编译会出错:
但是,真的常量定义了之后,就不能更改了么?答案是:否,请看下面的程序:
运行结果:
但是,是不是说“通过指针都可以改变被初始化的常量值呢?”,答案也不并是,请看下面这种场景:
编译运行:
这时因为:
编译:
另外const int*p = int const *p,const都出现在*的左边
编译:
只能直接赋值:
编译运行:
但是,*p它并非是个常量:
运行:
但是对于下面这种情况,就都不合法了:
编译:
编译:
指针的使用原则:
野指针:
也就是p目前指向的地址是不确定的,乱指的,可能指向的地址是不能写的,所以就会出错:
这次就是正常编译运行了:
编译运行:
这是为什么呢?
free()函数是释放在堆中申请的内存空间,而不能释放在栈上分配的内存,而上图中的p是在栈上分配的,所以运行就出错了。
这时,编译运行就正常了,因为free正常用释放堆中的内存:
说明:malloc在堆中分配的内存是不会自动回收的,对于服务器端程序,动态分配的内存,如果不用了并且没有释放,日积月累,就会浪费很多内存,这就叫内存泄露。
注意;是不是堆中分配的内存,只有调用free了才会释放么?并非如此,当程序结束运行时,既使没有调用free,堆中申请的内存也会进行回收的,这一点需要注意,一般人都会有这个误区!如我们的这段程序:
空悬指针:
编译,运行:
实际上上面能够正常运行,也是一种偶然的情况,其实是存在安全隐患的,待我来解析一下:
所以,有很多时候,是程序运行了一段时间,才会出运行时异常,其中空悬指针就容易造成这种情况的发生,那怎么解决空悬指针的问题呢?
好了,今天的内容有点多,需好好消化,下次再见!!