C++笔记(?):指针
一、指针是什么
在定义一个普通变量时,程序为其分配内存,内存位置有一个地址,可通过&
运算符访问值的地址.
指针也是一种变量,其储存的是一个值的地址,而不是值本身.
#include<iostream>
using namespace std;
int main()
{
int a = 10;
cout << "a的地址:" << &a << endl;
return 0;
}
a的地址:0x6ffe1c
显示地址时,常用十六进制表示法来描述.
- 声明和初始化指针
int *p = &a;
int*
的类型是指向int的指针,则p是一个指针,储存地址;
*p
则是一个int
.
初始化时,被初始化的是指针p
,而不是指针指向的值,因此初始化时赋值为一个地址&a
.
a 和 *p 相同,均是一个值;
&a 和 p 相同,均是一个地址;
一个例子:
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int *p = &a;
cout << "Value of a : " << a << endl;
cout << "Address of a : " << &a << endl;
cout << "Value of *p : " << *p << endl;
cout << "Value of p : " << p << endl;
return 0;
}
Value of a : 10
Address of a : 0x6ffe04
Value of *p : 10
Value of p : 0x6ffe04
指针可以指向其他数据类型
double *p1;
char *p2;
float *p3;
p1, p2, p3均是指针,只是指向不同的类型,但其储存的都是一个十六进制地址.
二、指针与数组
用指针指向数组时,不使用&
运算符,数组名称a
即表示数组的地址;
int a[3] = {1, 2, 3};
int *p;
p = a;
- 运用指针访问数组的值
初始化指针p后,p指向数组的第一个元素,则*p
是第一个元素的值.
访问数组中的其他元素,可以把指针当作数组名使用,即使用p[1]
访问第二个元素.
#include<iostream>
using namespace std;
int main()
{
int a[3] = {1, 2, 3};
int *p;
p = a;
cout << "*p = " << *p << endl;
cout << "p[1] = " << p[1] << endl;
return 0;
}
*p = 1
p[1] = 2
另一种访问不同元素的方式:
#include<iostream>
using namespace std;
int main()
{
int a[3] = {1, 2, 3};
int *p;
p = a;
cout << "p[0] = " << p[0] << endl;
cout << "p[1] = " << p[1] << endl;
p++;
cout << "new p[1] = " << p[1] << endl;
return 0;
}
p[0] = 1
p[1] = 2
new p[1] = 3
p是地址,p++
不会修改指针指向的数组的值,但指针是变量,此次修改试指针指向下一个元素.
数组名既然也表示数组的地址,则可以像使用指针一样使用数组名.
同理,array
初始时也指向数组array的第一个元素,(array + 1)
则指向下一个元素.
但数组名与指针也有不同,array = array + 1
是不合法的.
int array[3] = {1, 2, 3};
cout << "*array = " << *array << endl;
cout << "*(array + 1) = " << *(array + 1) << endl;
return 0;
*array = 1
*(array + 1) = 2
三、通过指针修改变量
对指针p
进行加减,改变的是地址,对*p
进行四则运算,则变量a
中的值也被改变.
#include<iostream>
using namespace std;
int main()
{
int a = 1;
int *p = &a;
cout << "*p = " << *p << endl;
*p = *p + 1;
cout << "new *p = " << *p << endl;
cout << "new a = " << a << endl;
return 0;
}
*p = 1
new *p = 2
new a = 2
四、new运算符
new运算符可以在程序运行阶段分配未命名的内存,这样的内存只能通过指针来访问.
- 在运行时”新建”一个int
new会为指定的数据类型找到一个长度正确的内存块,并返回该内存块的地址,赋值给一个指针.
int *pn = new int;
new在得知需要一个int后,找到int需要的字节数的内存,把地址返回赋给指针pn,*pn则是一个新的int.
double *pd = new double;
*pd = 1.41;
cout << *pd << endl;
new分配的内存块与变量声明分配的内存块不同,变量的值被储存在称为栈
的内存区域中,而new从被称为堆
或自由存储区
的内存区域中分配内存.
- delete运算符
new可以请求内存,当内存不再需要使用时,可以用delete运算符把内存释放,释放的内存可供程序其他部分使用.
使用delete时,后面为指向内存块的指针(只能是用new分配的).
int *pn = new int;
…
delete pn;
一些不建议的行为:
int a = 5;
int *p1 = &a;
delete p1; //不要用delete释放不是new分配的内存;
int *p2 = new int;
delete p2; //OK
delete p2; //不那么OK
//不要重复用detele释放同一内存
指针的内存被释放后,指针可继续使用,让其指向变量或用new请求新的内存块.
int *pn = new int;
*pn = 1;
cout << "NO.1 *pn = " << *pn << endl;
delete pn;
int a = 5;
pn = &a;
cout << "NO.2 *pn = " << *pn << endl;
- 动态数组
对于小型变量,用new来节省内存没有太大意义,但对于大型数组,正是new大显身手之处.
通过声明来创建数组,则程序在编译时就给其分配内存空间,在运行时,这部分空间无论是否有被使用,都被占据着;
静态联编:在编译时给数组分配内存.
使用new来创建数组,在运行阶段,需要时创建,不需要就不创建,同时可以在运行时选择数组的长度;
动态联编:在运行时给数组分配内存.
使用静态联编时,编写程序时必须确定数组长度;使用动态联编时,可以运行时再确定数组长度,节省内存,这种数组叫动态数组.
创建动态数组:
int *p = new int[10];
new运算符找到了可储存包含10个int的数组的内存块,并把第一个元素的地址返回给指针p.
释放整个数组:
delete []p;
动态数组方括号[]中的长度可以不是常量,即变量也OK.
#include<iostream>
using namespace std;
int main()
{
int a;
scanf("%d", &a);
int *p = new int[a];
for(int i=1; i<=a; i++)
{
*p = i;
printf("p[%d] = %d \n", i-1, *p);
p++;
}
delete []p;
return 0;
}
五、const关键字与指针
const关键字的使用将无法通过指针修改被指向的对象.
- 常量指针1
int a = 10;
const int *p = &a;
*p
的数据类型为const int,即p指向一个常量,不能使用*p来修改值.
但是a仍然是一个变量,可以直接通过a来修改a的值.
a = a + 1; //OK
*p = 11; //不怎么OK
- 常量指针2
const int a = 10;
const int *p = &a;
把const变量的地址赋给const指针,则既不可以通过a来修改值,也不可以通过指针p来修改a的值.
把const变量的地址赋给普通指针是不可行的.
const int a = 10;
int *p = &a; //不怎么OK
- 还有一件事
int a = 10;
const int *p = &a;
int b = 12;
p = &b; //OK
const指针可以防止指针修改指向的值,但是不可以阻止指针自身的值(指向的地址)被改变.
另一种const可以解决这个问题:
int a = 10;
int *const pp = &a;
*pp = 12; //OK
pp = &b; //不怎么OK
把const换一个位置,此时指针pp指向的地址不可被改变,但是可以通过指针pp修改变量a的值.
把两种const同时使用:
const int *const po = &a;
po = &b; //不怎么OK
*po = 12; //不怎么OK
此时指针po指向的地址不可改变,也不可通过po改变变量a的值.