指针探幽2
再补充一些关于指针的注意点.
一.野指针及C++指针使用注意点
避免野指针的产生
“野指针”的成因主要有:
1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
1 | char *p; //此时p为野指针 |
2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针.
1 | char *p=new char[10]; //指向堆中分配的内存首地址,p存储在栈区 |
3)指针操作超越了变量的作用范围。
1 | char *p=new char[10]; //指向堆中分配的内存首地址 |
指针的注意点:
a.指针指向常量存储区对象
1 | char *p="abc"; |
此时p指向的是一个字符串常量,不能对*p的内容进行写操作,如srtcpy(p,s)是错误的,因为p的内容为“abc”字符串常量,该数据存储在常量存储区,但可以对指针p进行操作,让其指向其他的内存空间。
b.资源泄漏
问题:
1 |
|
结果:报错
改进:
1 |
|
c.内存越界
1 | 1 char *p=new char[3]; //分配三个字符空间,p指向该内存空间 |
d.返回值是指针
问题:数组p[]中的内容为“hello world”,存储在栈区,函数结束时内容被清除,p变为野指针,可能导致乱码
1 |
|
有的编译器会报错,有的会执行但是出现乱码.
改进:
1.加static限定,延长数组生存期
1 |
|
2.定义成指针型数组
1 |
|
e.指针做形参
即所谓的地址传递,我们都知道地址传递的方式,形参的改变会导致实参的改变,但要注意的是,这里的改变是指指针所指内容的改变,而不是指针值的改变。因此,当形参改变会导致实参改变时,指针所指的内容是非const类型的,否则会出错。
1.改变指针内容:
1 | void swap(int *a,int *b) //交换的是*a,*b,即指针的内容,而不是指针a,b |
2.改变指针值:
1 |
|
上面的代码会报错,不能将 “const char *” 类型的值分配到 “char *” 类型的实体,所以改为:
1 |
|
这样的代码是失败的,因为VS告诉我们,Parameter ‘p’ is only assigned but neveraccessed.直接把第五行删了也没问题.
二.指针和数组
说到指针和数组,这里直接说三个最基本的规则,这三个规则基本上能够概括指针和数组的常见关系了.
- 一看到数组,就要知道数组名可以当做这个数组的第一个地址。(
a=&a[0]
) - 对于指向数组的指针,指针+n表示往后移动n个位置
- 指针也可以像数组那样用
p_a[n]
这种形式直接取元素,本质是*(p_a+n)
三.指针和字符串
指针和字符串相信是很多人一直弄混淆的地方了.其中确实有些地方需要理一理. 指针和字符串主要是指针用于c风格的字符串.
这里首先把重要的几个规则列出来,这几个规则足够弄清楚指针和字符串之间的关系了.
- 已经知道,c风格的字符串本质就是一个字符数组,所以,数组名就是第一个元素的地址。同样,一个指向char的指针变量也能够实现字符串的一些东西.
- C++中,对于引号引起来的字符串,也代表第一个元素的地址。
cout
对象认为,char的地址是字符串的地址,因此它打印该处的地址处的字符, 然后继续打印后面的字符。直到遇到空字符’\0’
之后才停止。
这里举一个例子来详细说明.
1 |
|
- 永远应该记住,双引号引起来的C风格的字符串常量,字符数组,字符指针变量这几个是相通等价的。
- 注意
const char* bird="wren"
;要是不使用const会有警告,因为不能够用可变的指针来修改常量. - 当cout后面是其他的指针的时候,会输出该指针的值也就是一个地址,但是cout遇到char* 的时候,就会输出当前char*指向的值,同时也会输出后面的值,直到遇到空字符为止。
- 所以在上面程序的20和21行有一个(
int*
)强制转化为整形指针,不然就没得输出了。