C++指针总结(不包括智能指针)

一、指针的类型

1.int* p;

类似于这种形式的指针,可指向一个对应类型的变量,比如

int *p;
int a;
p=&a;

可以让指针p指向整型变量a,int型的指针变量p里面放的是int型变量a的地址。

2.int* p[3];

表示p是一个数组,数组里面有3个元素,每一个元素都是一个可指向int型的指针变量

#include<iostream>
using namespace std;


int main()
{
	int* p[3];
	int a = 10;
	int b = 20;
	int c = 30;
	p[0] = &a;
	p[1] = &b;
	p[2] = &c;
	cout << *p[0] << endl;
	cout << *p[1] << endl;
	cout << *p[2] << endl;
	return 0;
}

最后输出为:

10

20

30

3.int (*p)[3];

这表示指针p可指向一个类型为int,大小为3的数组

#include<iostream>
using namespace std;


int main()
{
	int(*p)[3];
	int a[3] = { 1,2,3 };
	p = &a;
	cout << (*p)[0] << endl;
	cout << (*p)[1] << endl;
	cout << (*p)[2] << endl;
	return 0;
}

p=&a,代表用数组a的地址给指针p赋值,至于为什么用&a,而不是a,为什么用(*p)[0]这种形式来取得数组元素,我们在后面的“数组名退化成指针”里面讨论。

这里程序会输出:

1

2

3

4.const int *p、int const *p以及其他const用以修饰参数类型的情况;

const关键字会优先和左边的关键字或者运算符结合,所以这两种写法都可以可以使指针p能够指向一个常量,或者一个变量;这样的指针有一个名字叫常量指针,但我更喜欢叫它指向常量的指针

const int *p;
const int a = 10;
int b = 20;

对于一般的指针,比如int *p1来说,就不能让他指向a这个常量,如果指向,他就会报错,所以常量指针的指向范围比一般的指针要宽一些

5.int* const p以及其他const用来修饰*的情况;

根据const关键字先和左边的关键字或者运算符结合的特性,这里的const是用来修饰*的,所以这里的指针本身是一个常量,并且具有常量的性质。实际上,这里的p被叫做指针常量,意为类型为指针的常量,既然是常量,那值就不能改变,所以,在我们定义这个指针的时候,一定要给他赋值,否则编译器就会报错。

 

并且,在没有const运算符修饰int的情况下,他只能指向一个非常量,并且一旦指向一个变量,就不能再指向另一份变量,因为这个指针本身具有常量的性质,即值不能改变;

改变指针指向对象时会报错:

指向一个常量时也会报错:

6.int* func();

这个没什么好说的,有个名字叫指针函数,意为返回值是指针的函数,该例子返回一个int型指针

7.double (*func)(int a,int b);

他也有个名字叫函数指针,意为指向函数的指针,比如该例子中的这个函数指针,它可以指向一个接收两个int型参数,并且返回一个double类型值的函数,也就是说,它所指向的函数,除了函数名以外(在该例子中也就是(*func))其他都要和它相同,当然,它是一个指针,没有函数体,自然不要求函数体相同;

#include<iostream>
using namespace std;

double(*fun)(int a, int b);
double sum(int x, int y)
{
	return x + y;//int自动向上转换为double
}
int main()
{
	fun = sum;
	int a = 10;
	int b = 20;
	double c = fun(a, b);
	cout << c << endl;
	return 0;
}

这里输出30

二、指针的大小

指针是存储地址的工具,所以他的大小应该和寻址的范围有关,32位计算机的地址需要32位二进制数才能完全表示,所以在32位操作系统下,指针的大小为4字节(32位2进制的大小),同理,64位操作系统下,指针的大小为8字节。

三、数组名退化成指针

数组名只有在3种情况下,才代表数组本身:

1.对数组名使用sizeof关键字的时候

#include<iostream>
using namespace std;

int main()
{
	int a[] = { 1,2,3,4,5 };
	cout << sizeof(a) << endl;
	return 0;
}

这段代码输出20,代表数组的大小为20字节

2.对数组名取地址的时候

#include<iostream>
using namespace std;


struct Te
{
	int a[5] = { 1,2,3,4,5 };
	int b = 10;
}te;
int main()
{
	int(*p)[5] = &te.a;
	int *p2 = te.a;
	++p;
	++p2;
	cout << (*p)[0] << endl;
	cout << *p2 << endl;
	return 0;
}

这段代码输出:

10

2

因为指针p的类型是int*[5],所以让他自增1的话,他就会越过整个数组(4*5=20字节),指向数组最后一个元素的后一位,在这里p就指向了b的地址;

而我们使用括号将*和p括起来的原因是[]运算符的优先级在*之上,如果不使用括号,这里的p就会先和[]结合进行运算,然后再用运算的结果来和*进行运算(当然对于(*p)[0]加不加括号并不会对结果造成影响,毕竟p[0]代表在p的地址上加上0个该类型指针的大小,比如p[1],就代表在p的基础上加上20个字节,详见下文,指针的运算);

而p2是一个普通的int类型的指针,所以他自增后,越过的地址只有一个int型变量的大小,即4字节。

3.给整个数组初始化的时候

int a[]={1,2,3,4,5};
char c[]={'1','2','3','4','5','\0'};

在其他情况下,数组名均会退化成普通指针,不在具有数组名的性质。

四、指针的运算

两个指针相加没有什么意义,但是两个指针相减通常可以用来表示他们之间在内存上的距离

指针自增,代表地址加上指针所指类型的大小,对指针使用[]运算符,代表在当前指针所指的地址上加上指针所指类型的大小*[]里的数字,来看个例子:

#include<iostream>
using namespace std;

int main()
{
	char c[][3] = { {'a','b','c'},{'d','e','f'},{'g','h','i'},{'j','k','l'} };
	char(*pc1)[3] = &c[0];
	cout << (*pc1)[0] << endl;
	cout << (*pc1)[1] << endl;
	cout << (*pc1)[2] << endl;
	++pc1;
	cout << (*pc1)[0] << endl;
	cout << (*pc1)[1] << endl;
	cout << (*pc1)[2] << endl;
	cout << "-----------------------------------------------" << endl;
	cout << pc1[0][0] << endl;
	cout << pc1[1][0] << endl;
	cout << pc1[2][0] << endl;
	
	int *pi = (int*)&c[0][0];
	cout << (char)*pi << endl;
	++pi;
	cout << (char)*pi << endl;
	return 0;
}

这一段代码的输出如下:

首先我们用pc1指针指向了c数组的第一个一维数组,然后我们先对pc1进行解引用操作,得到c[0],这时的c[0]不满足保持数组名的三种情况之一,退化成一个普通的char型指针,指向c数组的第一维数组的第一个元素,也就是'a',所以,我们对它分别进行[0],[1],[2]操作后,分别代表,目前地址加0字节、1字节、2字节(1个char型变量占一字节),所以前三条输出语句输出a,b,c;

之后我们让pc1自增1,所谓的自增1,其实也就是让pc1加上一个单位的自己所指类型的大小,pc1的类型是char*[3],大小为3个字节,于是pc1自增1,其实就是在原有的基础上加上3个字节;

所以我们可以看到,后面再执行同样的三条语句时,输出的是def,而不是bcd;

再然后,我们尝试对pc1进行[]操作,pc1的类型是char*[3],我们进行[0],[1],[2]操作时,等同于在pc1原有值的基础上加上0*3字节,1*3字节,2*3字节,然后,分别找到了c[1],c[2],c[3],他们代表c数组里面第2、3、 4个一维数组的首地址,也可以看做是指向第2、3、 4个一维数组的首地址的类型为char的指针,所以我们最后的结果是dgj,分别是第2、3、 4个一维数组的首元素;

我们之后再用一个int*类型的指针pi指向数组c的首地址,即c[0][0]的地址,然后再让pi自增,根据前面的规则,pi的地址要加上一个int型变量的大小,也就是4字节,所以本来指向c[0][0]的指针现在指向了c数组的第5个元素,即c[1][1],即字母'e'。

暂时完结