C/C++指针的坑

零、引入

最近在复习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;

六、指针和字符串

这段稍微长一点,看官请进传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值