1. 初步了解——指针与地址
指针变量存储的内容是内存中某个字节的地址,指针变量占用得内存字节数随系统的不同有所不同,在普通的win32平台上,指针变量占4个字节,但在其他一些内存模型中,指针变量可能占用两个字节,简单点说,指针变量占用的字节多少取决于程序内存空间有多大。
#include <iostream>
using namespace std;
int main()
{
double num=3;
double *pNum;
pNum=#
cout<<"PNum在内存中得地址是: "<<&pNum<<endl;
cout<<"PNum的值为:"<<pNum<<endl;
cout<<"num在内存中的地址为:"<<&num<<endl;
cout<<"通过指针访问num:"<<*pNum<<endl;
cout<<"pNum(指针类型)在内存中的字节数:"<<sizeof(pNum)<<endl;
cout<<"num(double型)在内存中的字节数:"<<sizeof(num)<<endl;
return 0;
}
Mac上的数据:
win上的数据:
哎呀,竟然不一样哎,所以应了指针变量占用得内存字节数随系统的不同有所不同这句话。
下图地址可能不一样,但是原理一样。
对同一个系统,指向不同类型的指针变量占用的内存字节数一般是相同的(但也有例外)编译器通过指针变量的声明来确定其所指数据的类型和长度,从深层次讲指针指向的是内存中的一个字节(编译器根据该字节和声明语句中指针变量的类型来决定对那几个字节来进行操作)这对所有类型的指针变量都是等价的。
指向不同类型的指针变量占用相同的内存字节数:
#include <iostream>
using namespace std;
int main()
{
double num=0;
double *pNum;
pNum=#
short *pSnum,sNum=0;
pSnum=&sNum;
char *pChar,chr='A';
pChar=&chr;
cout<<"指向double型的指针变量pNum占用的内存字节数:"<<sizeof(pNum)<<endl;
cout<<"指向short型的指针变量pNum占用的内存字节数:"<<sizeof(pSnum)<<endl;
cout<<"指向char型的指针变量pNum占用的内存字节数:"<<sizeof(pChar)<<endl;
return 0;
}
(2)
#include<stdio.h>
int main()
{
int a = 1;
int *p;
printf("%d\n",*p); //未初始化的指针,野指针。
p = &a; //初始化指针
printf("a的地址是 %p\n",&a);
printf("指针p所指的地址是 %p\n",p);
return 0;
}
上面int *p定义了一个指向int类型指针p,并初始化p使其指向int类型的变量a,这里&a的&是取地址操作符,当&作用于一个对象上时,它返回了该对象的地址。通过测试可以知道指针p中存的地址就是a的地址所以说指针p指向了a.
因为指针相当于直接对a进行操作,所以可以通过指针来改变变量的值,这个即有好处,也有弊端,当我们需要的时候可以通过指针来改变变量的值,效率更高,但是如果利用不当,可能在我们不注意的地方悄悄修改变量的值。怎么办呢,这个在后面会讲用const来修饰。
小结:
double num=3;
double *pNum;
pNum=&num
num:double类型的变量。
pNum:指向double类型的指针变量,其值是num的地址。
&num:变量num的地址,与pNum等价。
*pNum:pNum所指的变量,间接访问方式,与num等价。
&(*pNum):与&num(即pNum)等价,num的地址。
*(&num):与*pNum(即num)等价,变量num。
*与&是可以互相抵消当同时出现*与&是可以直接将它们去掉。
2. 如何使用指针?——解引用与指针赋值
#include<stdio.h>
int main()
{
int a = 1;
int *p=&a;
*p=2;
printf("%d\n",a); //2
return 0;
}
当然,我们也可以给指针p赋值,使其指向另外一个地址。
#include<stdio.h>
int main()
{
int a = 10, b = 20;
int *p = &a;
printf("%d\n", *p);//此时p指向了a,所以输出了a的值.
p = &b;
printf("%d\n", *p);//此时p指向了b,所以输出了b的值.
return 0;
}
不同类型的指针间的赋值:
#include <iostream>
using namespace std;
int main()
{
short num1=100,*pNum1=&num1;
long *pNum2=(long *)pNum1;
cout<<"pNum1的地址:"<<&pNum1<<endl;
cout<<"pNum2的地址:"<<&pNum2<<endl;
cout<<"pNum1的内容:"<<pNum1<<endl;
cout<<"pNum2的内容:"<<pNum2<<endl;
cout<<"pNum2指向的值为:"<<*pNum2<<endl;
return 0;
}
short型变量num占用两个内存字节,当把short型指针pNum1强行赋给long型指针pNum2时,通过pNum2间接访问的是以0X7FFF5FBFF89A为基址的四个内存字节其值已经不是100了
下图原理示例:
简单地把整数赋给指针是错误的。
int *pNum=0x0012FF7c;//错误
如果实在有必要对某个内存地址进行访问,可以通过强制类型转化来完成。
int *pNum=(int *)0x0012FF7c;
3. 引用(引用概念在C++中才有,C中并没有)
#include<cstdio>
int main()
{
int a = 7, b = 999;
int &refa = a, &refb = b; ///引用必须要初始化,使其绑定到一个变量上
///修改引用的值将改变其所绑定的变量的值
refa = -12;
printf("%d %d\n", a, refa);//-12,refa的值和a一样
///将引用b赋值给引用a将改变引用a所绑定的变量的值,
///引用一但初始化(绑定),将始终绑定到同一个特定对象上,无法绑定到另一个对象上
refa = refb;
printf("%d %d\n", a, refa);//999
return 0;
}
从某种意义上来说,引用完全有别于上面说介绍的内容:
关键:作用在引用上的所有操作事实上都是作用在该引用所绑定的对象上(相当于直接对此变量进行相关操作)。
可以用int *&refp = p;的方式将一个指针引用和一个指针绑定起来。
PS:如果还是不理解,可以把引用认为是一个对象的别名(外号、简称等等)。(类比#define ...,typedef ...)
#include<cstdio>
int main()
{
int a=10,b=20;
int *p1=&a;
int *p2=&b;
int *&p3=p1;
printf("%d %d\n",*p1,*p3);
p3=p2; //可以改变指向
printf("%d\n",*p3);
return 0;
}
使用引用有何优点?
在传参的时候,使用指针传参,编译器需要给指针另行分配存储单元,存储一个该指针的副本,在函数中对这个副本进行操作;而使用引用传参,编译器就不需要分配存储空间和保存副本了,函数将直接对实参进行操作。所以使用引用使得程序的运行速度更快,执行效率更高。但是得注意安全。