且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

(五十七)指针、数组、指针算数

更新时间:2021-11-05 02:37:31

对指针加1,加的是指针类型的字节数。

例如:

int*a[5];

a+1;

这个时候,由于int是4字节,因此实际上是地址的位置加4,也就是向右移动一个int字节的宽度。

减一刚好相反,是向左。

 

而*a+1; 是指针a所指的地址的变量+1,但是指针位置是不变的。


#include<iostream>

int main()
{
	using namespace std;
	int *a;
	int b[4] = { 1,2,3,4 };	//声明数组b,数值分别为1~10
	cout << "int b[4] = { 1,2,3,4 }" << endl << endl;
	cout << "b[0]= " << b[0] << endl;	//输出数组b的第一个元素
	//输出数组b的地址 b 和 &b 时,二者输出是一样的
	cout << endl;
	a = &b[0];	//将指针和数组的第一个元素的地址对齐
	cout << "a = &b[0];" << endl;
	cout << "*a = " << *a << endl;	//输出指针a的变量
	cout << "指针a的地址为:" << a << endl;	//输出指针a的值
	a = a + 1;	//指针a+1
	cout << "a = a+1;\n*a = " << *a << endl;	//指针a+1后,指针a的变量
	cout << "指针a的地址为:" << a << endl;	//输出指针a的值
	cout << "*(a+1) = " << *(a + 1) << endl;	//指针a+1后,输出(a+1)的变量,注意,这里只是输出a+1,但并没有a=a+1
	cout << "*a = " << *a << endl;	//说明,没有a=a+1时,指针a的位置是不变的。输出的结果依然是2,也是数组b的第二个元素
	cout << "指针a的地址为:" << a << endl;	//输出指针a的值
	a = a - 1;	//指针a-1
	cout << endl << "a = a - 1;" << endl;
	cout << "*a = " << *a << endl;	//指针a+1后再次a-1,指针a的变量
	cout << "指针a的地址为:" << a << endl;	//输出指针a的值
	cout << endl << "以下对数组b的地址 b 和 &b 进行操作" << endl;
	cout << "数组b的地址,\nb      = " << b << endl;	//输出数组b的地址
	cout << "&b     = " << &b << endl;	//&b也是数组b的地址
	cout << "b + 1  = " << b + 1 << endl;	//b+1是加的数组中一个元素的宽度,地址偏移数组一个元素的类型(int)的距离
	cout << "&b + 1 = " << &b + 1 << endl;	//&b+1是地址偏离了整个数组长度的宽度的距离
	system("pause");
	return 0;
}

输出:


int b[4] = { 1,2,3,4 }

b[0]= 1

a = &b[0];
*a = 1
指针a的地址为:0040FBA8
a = a+1;
*a = 2
指针a的地址为:0040FBAC
*(a+1) = 3
*a = 2
指针a的地址为:0040FBAC

a = a - 1;
*a = 1
指针a的地址为:0040FBA8

以下对数组b的地址 b 和 &b 进行操作
数组b的地址,
b      = 0040FBA8
&b     = 0040FBA8
b + 1  = 0040FBAC
&b + 1 = 0040FBB8
请按任意键继续. . .

备注:

①int a[4];后,a是一个指针,是4字节(int)的指针;而&a是面向数组a全部元素的一个指针(4字节*4个元素);

 

 

 

指针算数:

两个指针可以相减,如代码:

#include<iostream>

int main()
{
	using namespace std;
	int a[10];
	int *b, *c;
	b = &a[0];
	c = &a[5];
	cout << "指针c为&a[5],指针b为&a[0]。指针c-指针b: c - b = " << c - b << endl;
	system("pause");
	return 0;
}

输出结果为:


5

结论:

①指针相减的结果,相当于两个元素之间指针需要偏移的距离(5个int)。

 

 

对指针解除引用:

对指针解除引用,实际上就是显示指针(所指向)的值,运算符是*。

例如int *p; p是指针,*p就是对指针p解除引用。

 

另外一种对指针解除引用的方法就是数组表示法,即p[0],[0]表示指针当前地址的值。

 

不要对没有被初始化的指针解除引用。

 

 

delete指针:

指针初始化后可以delete 指针。例如

int *a = new int;

delete a;

这样是ok的。也可以给指针赋值后,删除指针。

 

 

数组和指针:

上代码:

#include<iostream>
#include<cstring>

int main()
{
	using namespace std;

	char *a, b[10] = { 'a','b' };	//声明指针a。再声明数组b为字符串ab
	a = b;	//将字符串b赋值给指针a

	cout <<"a = "<< a << endl;	//指针a输出字符串
	cout <<"b = "<< b << endl;	//数组b输出字符串
	cout <<"&a= "<< &a << endl;	//输出指针a的地址,这是储存指针a的内存地址
	cout <<"&b[0] ="<< &b[0] << endl;	//这行无法输出数组b的地址,不加&输出的是数组b的第一个元素,
		//但加了&,cout会从第一个字符输出到空字符为止——(但是不知道为什么)
	cout <<"&b= "<< &b << endl;	//输出数组b的地址,注意,和指针a的地址是不同的
	cout << (int*)a << endl;	//只有改为(int*)a才能输出指针a所输出的地址,如果是(char*)a将依然输出字符组
	cout << endl;
	a = "ffff";

	cout << "a = " << a << endl;	//指针a输出字符串
	cout << "b = " << b << endl;	//数组b输出字符串
	cout << "&a= " << &a << endl;	//输出指针a的地址,这是储存指针a的内存地址
	cout << "&b[0] =" << &b[0] << endl;	//这行无法输出数组b的地址,不加&输出的是数组b的第一个元素,
										//但加了&,cout会从第一个字符输出到空字符为止——(但是不知道为什么)
	cout << "&b= " << &b << endl;	//输出数组b的地址,注意,和指针a的地址是不同的

	cout << endl;

	int *c, d;
	d = 123;
	c = &d;
	cout <<"c = "<< c << endl;
	cout <<"d = "<< d << endl;
	cout <<"&c= "<< &c << endl;
	cout <<"&d= "<< &d << endl;
	cout << endl << "*c = 5" << endl;
	*c = 5;
	cout << "c = " << c << endl;
	cout << "d = " << d << endl;
	cout << "&c= " << &c << endl;
	cout << "&d= " << &d << endl;

	cout << endl;

	system("pause");
	return 0;
}

输出:


a = ab
b = ab
&a= 0029F790
&b[0] =ab
&b= 0029F77C
0029F77C

a = ffff
b = ab
&a= 0029F790
&b[0] =ab
&b= 0029F77C

c = 0029F764
d = 123
&c= 0029F770
&d= 0029F764

*c = 5
c = 0029F764
d = 5
&c= 0029F770
&d= 0029F764

请按任意键继续. . .

①当给指针赋值数组的时候,如果输出指针,那么实际输出的是数组(但数组如果有数字可能乱码,字符串不会)

 

②如果要输出地址,则需要给数组前面加&,或者给指针前面加&,才能输出地址。但两个输出的地址不同,&a是储存指针a的地址,&b是数组b的地址。a由于输出的是字符串,所以无法输出地址。只有加了(int*)或者其他(类型名*)才能将其强制转换为内存地址,若是(char*)将依然输出字符串

 

③储存指针的内存地址和指针所表示的内存地址,是不同的,前者的内存地址储存的值 是 指针的内存地址。

 

④当指针表示字符串的时候,改变指针的值,只改变指针的值,不像指针表示内存地址时,将影响指针指向内存地址的值。具体见a="ffff";后的语句。

 

⑤字符串/数组的第一个元素的内存地址,将表示数组的内存地址。但不能用(int*)数组名[0]这样的,把(int*)改成(char*)也不行,会输出地址,但不是预期想要的地址。至于为什么,不知道。只能用(int*)数组名,或者&数组名,才能显示数组的地址。

 

⑥char*a;是指针,当被赋值为字符串时,输出a的时候输出的就是字符串。

前者:(一般是char*指针情况下)

	char a[] = "abc";
	char *b;
	b = a;
	cout << a << endl;

输出的为:abc

后者:


	string a = "abc";
	string *b;
	b = &a;
	cout << b << endl;

输出的为:0043FDFC是一个地址。


 

 

 

指针和字符串:

数组和指针的特殊关系,是可以扩展到C-风格的字符串的。

例如:

char a[10] = "abc";
cout << a << endl;

其中在第二行语句中,字符串a实际上是被cout认为是字符串a的地址,而cout的效果,实际上是读取内存地址,然后按顺序输出字符,直到遇见空字符(\0)为止,另外,空格\回车等并非空字符。——这也是为什么字符串最后要是空字符的原因。

 

当使用指针的时候,如在上面代码后面加上这两句代码:

	char *b;
	b = a;
<span style="white-space:pre">	</span>cout << b << endl;

b是一个指针,又因为a实际上也是一个地址,因此a才能被赋值给b,此时b被认为是字符串a的地址。

然而,当cout遇见b的时候,因为cout发现两件事①b是地址②b是char类型的指针。于是,输出的是字符串,至于原因,要看下一行。

 

在cout和C++的多数表达式中,char 数组名、char 指针以及用括号引起的字符串常量(比如"abc"),都被解释为字符串的第一个字符的地址。而输出的是字符串。只有当在之前加上 & 之后,才能显示出内存地址。

 

比较典型的如以下代码:


#include<iostream>
#include<cstring>

int main()
{
	using namespace std;

	char animal[20] = "bear";	//声明字符串animale为"bear"
	const char * bird = "wren";		//常量字符串bird为wren
	char * ps;	//对于char*类型的指针,cout是根据指针地址输出字符串,而不像比如int*类型或者string*类型的指针那样输出地址
	

	cout << animal << " and " << bird << endl;	//输出字符串animal和常量字符串bird
	cout << "输入一种动物:";
	cin >> animal;	//让用户输入字符串animal,覆写之前的bear

	ps = animal;	//由于ps是一个可变指针,animal是字符串,所以ps是字符串
	cout << "ps = : " << ps << "!\n";	//输出ps(ps是字符串)

	cout << "在使用strcpy():之前——strcpy(目标字符串,源字符串)是将字符串从一个位置复制到另外一个位置\n";
	cout << animal << " at " << (int*)animal << endl;	//第一个animal是输出字符串,第二个加了(int*)是强制转换,输出的是指针
	cout << ps << " at " << (int*)ps << endl;	//第一个ps是指针,第二个ps加(int*)是输出储存指针的指针

	ps = new char[strlen(animal) +1]; //new 一个char字符串,宽度是animal宽度+1
	
	//strcpy(ps, animal);	//这行命令编译出错,正常来说,应该是将animal的字符串复制到ps新的内存地址处
	ps = "ab";	//用自行赋值替代
	cout << "在使用strcpy()之后\n";
	cout << animal << " at " << (int*)animal << endl;		//animal的值和地址不变
	cout << ps << " at " << (int*)ps << endl;	//ps获得新值和新的地址
	//delete[] ps; 这行代码应该是要删除指针ps,但实际上出错了,不知道为什么?

	system("pause");
	return 0;
}

 

①在以上这个代码之中,对于char*类型的指针,cout输出的为字符串。只有之前加上 & 之后,输出的才不是字符串,而是地址——只不过是储存指针的地址,而非指针指向的地址。

 

②对于字符串而言,cout输出的是字符串(直到遇见空字符\0)为止。只有在字符串(数组)之前加上&,显示的才是字符串的地址。

 

③对于int*类型,或者其他类似的指针来说,cout输出的指针是地址,但char*类型的指针例外——似乎也只有char*类型的例外

 

④在vs2015里面,strcpy(ps,animal)这行语句会提示不安全。——但是我也不知道怎么改

 

⑤因为ps是指针,因此才能ps=new char[],这是给指针a一个新的内存地址,这个时候,指针a的地址为该地址,但是&a为储存指针a的内存地址,只有用强制转换如(int*)a,才能得出新地址是在哪里。

 

⑥char*,数组,字符串,以及字符串常量(比如char a[5]="abc";这样。字符串abc为字符串常量,被储存在内存的某个专门的区域),cout都理解为内存地址,输出的是字符串,而不是地址。只有加了&后,才根据具体情况输出地址,但不一定是字符串所在地址——比如指针指向该字符串,指针加&输出的是储存该指针的内存地址(而非指针指向的内存地址)。

 

⑦因为char*类型,一般被cout认为是地址但输出的字符串,因此如果想输出地址的话,需要用强制转换,比如转换为(int*)类型的指针,才能输出地址。

 

⑧因为某个不知道的原因,删除指针delete[]ps;时,提示出错了,不知道为什么。

 

⑨假如char*a="abc"; cout <<a<<endl;这个时候,指针a指向了字符串abc所在的内存地址,输出的是abc。假如之后加上代码a="def";cout<<a<<endl;这个时候,改变a并没有将abc所在内存地址的值改变,而是改变了指针a指向的内存地址,为字符串"def"所在的内存地址。

 

想改变char *a所指向内存地址的值的话,需要通过*a='d';*(a+1)='e';*(a+2)='f';

这样,把字符串“abc”所在内存地址的值,改为字符串def。

或者通过

cin>>a;

这样,将用户输入的值,直接覆写到指针a所指向内存地址的之上。

但是不能通过比如*a="def";这种形式,企图一次写多个字符到字符串之中。

不过或许可以通过strncpy(a,"字符串",字符串长度);这样的形式将字符串写入,但问题在于,我的vs2015提示这是不安全的,无法编译。