第四章总结(三)指针初探

指针类型

下面通过代码进行讲解

#include<iostream>
using namespace std;


int main()
{
	int* pt = new int;
	double* ptt = new double;
	cout << pt << endl;
	cout << ptt << endl;
	
	*pt = 100.00;
	*ptt = 12.1234;

	cout << *pt << endl;
	cout << *ptt << endl;
	return 0;
}

在这里插入图片描述
这里可以看到,我们通过new申请得到的两块地址的长度是一致的,但是为什么在cout的时候,编译器知道该输出int还是输出double呢?这就与指针的类型有关了
我们在new int的时候,new找到一个长度为四字节的内存块,把起始地址放在pt中,ptt也是同理,,由于地址在计算机中的表示形式都是一样的,最起码在同一台计算机中是这样的,所以pt和ptt存储的内容长度都一致,但是由于pt在定义的时候就定义成了int *类型的变量,所以在cout<<*pt的时候,编译器就知道,从pt存储的那块地址开始的四个字节按照int的方式进行解释

delete

delete用来释放new申请得到的空间,注意 是new 得到的空间,另外new和delete应该成对出现,然后不要对同一个指针,因为第一次delete已经把指针所保存的空间归还给操作系统了,连续两次delete虽然不会报错,但是会返回错误的结束码.
对于数组的申请
int *pt = new int[5]; delete [] pt;

指针和数组

基本操作

直接讲解书中代码,进行引出

#include<iostream>
using namespace std;


int main()
{	
	double wages[3]{ 1000.0, 2000.0, 3000.0 };
	short stacks[3]{ 3,2,1 };

	double* pw = wages;
	short* ps = &stacks[0];
	short(* ps_2)[3] = &stacks;
	
	cout << "pw= " << pw << " *pw=" << *pw << endl;
	pw = pw + 1;
	cout << "after add 1 to pw\n";
	cout << "pw= " << pw << " *pw=" << *pw << endl;
	cout << "ps= " << ps << " *ps=" << *ps << endl;
	ps = ps + 1;
	cout << "after add 1 to ps\n";
	cout << "ps= " << ps << " *ps=" << *ps << endl;
	cout << "*(stacks+1)=" << *(stacks + 1) << endl;

	cout << "sizeof(wages):" << sizeof(wages) << endl;
	cout << "sizeof(ps):" << sizeof(ps) << endl;

	cout << "&stacks[2]=" << &stacks[2] << endl;
	cout << "ps_2+1=" << ps_2 + 1 << endl;
	return 0;
}

运行结果
pw= 010FFAB4 *pw=1000
after add 1 to pw
pw= 010FFABC *pw=2000
ps= 010FFAA4 *ps=3
after add 1 to ps
ps= 010FFAA6 *ps=2
*(stacks+1)=2
sizeof(wages):24
sizeof(ps):4
&stacks[2]=010FFAA8
ps_2+1=010FFAAA

对结果进行解释,由于C++一般情况下将数组名结束为数组的第一个元素的地址(特殊情况下面会讲),所以pw的值也就是&wages[0],所以pw也就是wages[0],然后pw=pw+1,对指针变量加1后,其增加的值等于指向的类型占用的字节数,这点很重要!!! 所以这里pw的值和pw都编程了wages[1]的了
指针ps的结果和pw一致,然后对于 *(stacks + 1) ,可以看出这种写法与stacks[1]的结果一致,所以大多数情况下,可以用相同的方式使用指针名和数组名,所以ps[0]这种写法也可以得到stacks[0]的值

最后的两个sizeof的结果,如果是sizeof(wages),得到的结果就是元素的大小*数组的长度,如果是sizeof(指针)的话,得到的结果与编译器或操作系统的位数有关,在vs2019中,如果最上面设置的是x86,则指针大小得到4,如果是x64,则得到8

不解释为首元素地址的特殊情况

  1. 对数组取地址
    同时也是上面这段代码最后一个需要注意的地方,就是short(* ps_2)[3] = &stacks;,我们这里对数组进行了取地址,这时就会得到整个数组的地址,由于整个数组是一个有着3个short元素的数组,虽然ps_2和&stacks[0](也就是stack)的值是一样的,但是从概念上说,&stacks[0]是一个4字节的内存块的地址,而ps_2是一个12字节的内存块的地址,因此最后将ps_2+1得到的地址比&stacks[2]还大了四个字节

指针数组和数组指针的简单理解

所以这里把ps_2声明成了 short (*)[3],这是一个数组指针,这个怎么理解呢,由于括号的优先级,所以ps_2先和星号结合,成为一个指针,然后指向的元素是有着三个元素的short数组(数组指针,首先是个指针,指向一个数组,所以叫数组指针)
然后如果我们去掉括号的话,就成了 short *ps_2[3],ps_2将先和[3]结合,所以就解释为了,ps_2是一个数组,数组的元素是指向short类型的指针,先是一个数组,再是一个指针,所以叫它指针数组

指针和字符串

同样也是讲解书中代码,然后引出相关知识

#include<iostream>
using namespace std;


int main()
{	
	char animal[20]{ "bear" };
	const char* bird = "wren";
	char* ps;
	cout << animal << " and " << bird << endl;
	//cout << ps << endl; 编译错误,使用了未初始化的局部变量ps
	//cout << ps << endl; 如果把ps初始化为nullptr,则运行不会报错,但是会返回错误的结束码

	cout << "Enter a kind of animal:";
	cin >> animal;

	ps = animal;
	cout << ps << "!\n";
	cout << "Before using strcpy():\n";
	cout << animal << " at " << (int*)animal << endl;
	cout << ps << " at " << (int*)ps << endl;

	ps = new char[strlen(animal) + 1];
	strncpy_s(ps,strlen(animal) + 1,animal,strlen(animal));
	cout << "after use:\n";
	cout << animal << " at " << (int*)animal << endl;
	cout << ps << " at " << (int*)ps << endl;
	delete[] ps;
	return 0;
}

bear and wren
Enter a kind of animal:tigger
tigger!
Before using strcpy():
tigger at 00BFFE4C
tigger at 00BFFE4C
after use:
tigger at 00BFFE4C
tigger at 00EC4968
  1. 为什么"wren"一定要通过const char *去存储?
    因为"wren"实际表示的是字符串的地址,因此将地址只能赋给指针,而且由于字符串字面值是常量,所以要是const,表示 bird 是一个指针,指向一个const char的数据,不能对指针指向的数据进行修改
  2. 一般来说,提供给cout一个指针,他将打印地址,但如果指针类型为char *,则cout将显示指向的字符串,但如果要显示地址得话,就需要想上面一样转换一下再输出
  3. 如果要进行字符串副本的话,不能直接用 = 因为这样实际上是让两个指针指向了一个地址,就像上面 ps =animal之后的结果,两个的地址一样, 这样并不安全,通常的做法是 先给ps开辟空间,然后用strcpy函数进行拷贝
    当然,如果使用string的话,就避免了这些问题,这些就不用我们考虑了,之前有介绍string的自动扩容
  4. C++不保证字符串字面值被唯一的存储,也就是说如果在程序中多次使用了 “bear” 这个字符串常量,则编译器可能存储该字符串的多个副本,也可能只存储一个副本,这种与编译器有关的我们就不做深究了,我在vs2019和DEV C++上测试,都是只存储了一个

堆,栈和内存泄漏

#include<iostream>
using namespace std;


int main()
{	
	{
		int* a = new int;
	}
	delete a; //直接报错,因为a的作用域已经结束了
	return 0;
}

书上说的就是这么两汉代码,我们在{}中 new 了一个空间给a,但是花括号结束也就代表着出了a的作用域,在括号外就无法通过a去delete了

二级指针初探

#include<iostream>
using namespace std;

struct mytest {
	int test;
};
int main()
{	
	mytest t1, t2, t3;
	t1.test = 1996;
	t2.test = 1997;
	t3.test = 1998;
	mytest * arp[3] = { &t1, &t2, &t3 };
	mytest* *pd = arp;
	cout << pd[0]->test << endl;//1996
	cout << (*(pd + 0))->test << endl;//1996
	cout << (*pd)[0].test << endl;//1996
	cout << pd[0]->test << endl;//1996
	
	cout << arp[0]->test << "--" << (*(arp + 0))->test << "--" << (*(*(arp + 0))).test << endl;//1996--1996--1996
}

由于arp是一个指针数组,所以他的每一个元素都是指针,所以arp[0]就需要用间接成员运算符也就是箭头去访问成员,由于前面介绍过 arp[0] 和 (arp+0)是一回事,但是最后那个((*(arp + 0)))就直接拿到的是这个结构体,所以可以使用 . 去访问元素

然后arp由于是一个数组的名称,因此它是第一个元素的地址,但其第一个元素为指针,所以pd就是一个指针变量,它指向一个mytest 的指针,所以*pd就是结构体指针,需要用箭头去访问,然后(*pd)[0].test相当于做了两次 * 号操作,所以可以直接用 . 去访问

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巴塞罗那的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值