零、引入
最近在复习C++指针,看到这样一个简单指针小程序片段,说有隐患问题,一时间竟然没看出来有啥隐患:
long * fellow;
*fellow = 233333;
long * fellow
不就是声明一个fellow指针指向long类型么?
* fellow = 233333
不就是将fellow指向的内存区域填上233333这个数据嘛?
有啥问题呢?
灵光一闪想到了,绝不要对未被初始化为适当地址的指针解引用
因为!fellow没有被初始化(就是说这个fellow指针要指向哪块内存都不知道;内存方面解释,就是我在一个内存地址声明一个指向long类型的指针,名为fellow,但是我就不是不初始化,也就是不往这个内存里填值),你怎么能对它进行解引用呢?它可能有任何值(注意是值,不是这个指针本身的内存地址)。不管值是什么,程序都将它解释成存储为233333的地址,这个指向的地址因为随便放,说不定就放在了系统程序上的某个地方,可能后面就被覆盖掉了,导致隐晦的bug。
最后用个形象的比喻:我让你去找一个叫fellow的地方放东西,但是就不跟你说地址在哪儿,你也没有其他办法知道在哪儿,不就只能乱放了么~
解决方式:
long x = 233333;
long * fellow = &x;
或者:
long * fellow = new int;
*fellow = 233333;
一、使用指针的注意事项:
- 有
new
必有delete
- 不要用
delete
来释放不是new
分配的内存 - 如果用
new
为一个实体内存分配内存,应使用delete
(没有方括号)来释放 - 使用
new
创建动态数组时,格式int * arr = new int [10]
; 使用delete
free掉该指针时,格式delete [] arr
delete
的指针可以不是new
出来的那个,但是必须指向new
出来那个指针指向的原始地址。
比如long * fellow = new int; long * x = fellow; delete x;
这里fellow是new
出来的,但是可以用delete
x 来将那一块分配的内存块free掉。- 不要用
delete
释放同一个内存块两次 - 对空指针用
delete
是安全的
二、指针和数字
指针不是整型,虽然计算机通常把地址当做整数来处理,但是从概念上看,指针和整数是截然不同的类型。
- 整数是可以执行加减乘除等运算的数字
- 指针描述的时为止,将两个地址相乘没有任何意义
所以你看下面这段程序就会出错:
int * pt;
pt = 0xB8000000;
因为这段程序没有告诉程序,这个数字就是一个地址,编译器将显示error:类型不匹配
那么既然不能简单地将整数赋给指针,如何将数字值作为地址来用呢??
应该通过强制转换将数字转换为适当的地址类型:
int * pt;
pt = (int *)0xB8000000;
这样,赋值语句的两边都是整数的地址,有效。
注意,pt是int值的地址,并不意味着pt本身的类型时int,例如,在有些平台中,int是2字节,地址却是4个字节。
三、数组名与指针的共同点与区别
1.共同点
-
都可以对数组进行操作,不管是
arr[i]
方式,还是arr+i
方式(arr此处不管是数组名还是指向数组头的指针都可以) -
在C++中,数组名多数情况下就是被解释为地址
2.区别
- 对数组用sizeof得到的是数组的长度,对指针用sizeof得到的是这个指针的长度(一般是4个字节),因为在这种情况下,C++不会将数组名解释为地址。
- 数组名是常量,是不可以修改的,但是指针可以,甚至可以像下面这样用p指针遍历完arr数组(长度为len):
int * p = arr;
while (len--)
{
//printf(...);
p += 1;
}
其中arr作为数组名是不可以修改的,而p可以。
如果你说这是因为arr数组创建与p无关,所以这样才可以。那我们看看下面这个用 p指针和 new 关键字的:
#include<iostream>
using namespace std;
int main()
{
double * p = new double[3]; // 开辟空间
p[0] = 0.2;
p[1] = 0.5;
p[2] = 0.8;
cout << "让我瞧瞧p指向哪里" << p << endl;
cout << "现在p[0]值为:" << p[0] << endl;
p = p + 1;
cout << "让我瞧瞧p指向哪里" << p << endl;
cout << "现在p[0]值为:" << p[0] << endl;
// 注意, new之后一定要delete!而且一定要在正确的原位置delete!
p = p - 1; // 指向最初的位置
cout << "让我瞧瞧p指向哪里" << p << endl;
delete[] p;
system("pause");
}
四、指针算数
- 指针和整数相加——
arr+i
都是同样的含义。如果是int型数组,arr+i
实际上是arr+4*i个byte
;如果是double型数组,arr+i
实际上是arr+8*i个byte
; - 指针和指针相减——仅当两个指针指向同一个数组(也可以是指向超出结尾的一个位置)时,这种运算才有意义:得到两个元素的间隔(不是字节数的差值),比如对于a数组,&a[9] - a = 9,中间间隔了9个元素。
五、数组的静态联编和动态联编
静态联编很简单,比如数组创建时,规定了数组的长度,那么数组长度在编译时就设置了:
int ttt[10];
C语言里面难以实现的动态数组,C++用new
轻易实现。
int size;
cin >> size;
int * pz = new int [size];
....
delete[] pz;
六、指针和字符串
这段稍微长一点,看官请进传送门