int p; //这是一个普通的整型变量
int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针
int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.
int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针
int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
1、变量的地址
电脑内存中每个字节都有一个唯一编号(内存地址),从 0 字节开始递 增,1、2、…,直到 M-1,其中 M 是内存的最大容量, 例如 8G。
变量存储在内存中。变量所在内存字节的编号称为变量的地址。如果一个 变量占用的空间超过一个字节(例如 int 占 4 个字节),则在内存连续地址存放,并以较小的内存字节编号作为变量的地址。
就像我们前面学习到的一维数组与二维数组在动态定义的时候便使用了指针
int** a = new int* [m];
int *a = new int[n];
#include<iostream>
using namespace std;
int main()
{
//定义 m 行 n 列的二维数组
int m, n;
cin >> m >> n;
int** a = new int* [m];
for (int i = 0; i < m; i++)
{
a[i] = new int[n];//一维数组定义:int *a = new int[n];
}
//为数组各元素赋值
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (i == 0 || j == 0)
{
a[i][j] = 1;
}
else
{
a[i][j] = a[i - 1][j] + a[i][j - 1];
}
}
}
//输出
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
cout << a[i][j];
if (j == n - 1)
{
cout << endl;
}
else
{
cout << " ";
}
}
}
//回收空间!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
for (int i = 0; i < m; i++)
{
delete[]a[i];//delete 先释放j再释放i
}
delete[]a;
return 0;
}
【例 1】计算变量的地址和占用空间
#include <iostream>
using namespace std;
int main()
{
int x = 10;
cout << "变量 x 的值为:" << x << endl;
cout << "变量 x 占用内存大小:" << sizeof(x) << " bytes" << endl;
cout << "变量 x 的地址为:" << &x << endl;
return 0;
}
【显示结果】
变量 x 的值为:10
变量 x 占用内存大小:4 bytes
变量 x 的地址为:0012FF7C
注意:
变量的地址每次运行程序的显示结果都不同,这是正常现象。
不同类型变量占用的内存空间大小:
char,
1 字节=8 个二进制位,能存-128~127 范围内的整数,输入输出时的表现 形式为字符。
int,
4 字节=32 个二进制位,能存-2147483648~2147483647 范围内的整数。
double,
8 字节=64 个二进制位,能存-1.79*10308~1.79*10308范围内的浮点数。
2、指针
变量的地址用指针保存。指针也是一种变量,和 int、char 等变量一样存放在内存中。
为了区别指针与普通变量,我们称之为指针或指针变量。
【例 2:指针变量的定义和使用】
#include<iostream>
using namespace std;
int main()
{
int* p; //定义了一个指针变量 p,类型是 int*
int a = 123;
p = &a; //p 保存 a 的地址,称为 p 指向 a。用&获取一个变量的地址
cout << p << endl; //输出 int*类型的指针变量,显示的是 p 保存的地址
cout << *p << endl; //输出*指针变量,显示的是 p 指向的变量 a 的值
return 0;
}
【显示结果】
00AFF948
123
注意:
指针的定义和指针的使用
中*含义的区别 int* p; p = &a;
这两句话可以合成一句话: int* p = &a
在上面的例子中,
int a=123; int *p=&a;
假设系统
给 a 分配的内存地址为 0x013CFB90,
给 p 分配的内存地址为 0x013CFB9C。
则内存中,指针 p 存储的值 是 a 的地址 0x013CFB90。
在一个 32 位的程序中,任何类型的指针变量(int*、 char*、double*、...)占用的内存空间都是 4 个字节(32 位)。在 64 位的程序 中,指针变量占用的内存空间是 8 个字节。
【例 3:指针变量之间的赋值】
#include<iostream>
using namespace std;
int main()
{
int x = 10;
int* p, * q;
p = &x;
q = p;
cout << *p << endl;
cout << *q << endl;
return 0;
}
【例 4:double * 类型的指针变量】
#include<iostream>
using namespace std;
int main()
{
double x = 2.5;
double* p;
p = &x;
cout << p << endl;
cout << *p << endl;
return 0;
}
【显示结果】
00AFF948 2.5
注意:
指针变量的类型要和指向的变量类型相对应,int*指向 int,double*指向 double。
【例 5:通过指针访问或修改变量】
#include<iostream>
using namespace std;
int main()
{
int x = 10;
int* p;
p = &x;
*p = 12;
cout << x << ' ' << *p;
return 0;
}
【显示结果】
12 12
注意:
当 p=&x 时,*p 等价于 x 的值
3、不能对未赋值的指针使用*运算符。
如果指针不指向任何数据,称为空指针(nullptr)。
int *p=nullptr; 定义后未赋值的指针是空指针,不能对空指针使用*运算符。
以下用法是错的:
int* p; *p=10;
正确的用法:
int a=10; int* p; p=&a; *p=11;
或者以下也是正确的:
int* p=new int(); *p=10;
【例 6】利用指针交换变量的值
#include<iostream>
using namespace std;
void swap(int* x, int* y)
{
int t;
t = *x; *x = *y; *y = t;
}
int main()
{
int a = 10, b = 50;
cout << "交换前 a,b 为" << a << "\t" << b << endl;
swap(&a, &b);
cout << "交换后 a,b 为" << a << "\t" << b << endl;
return 0;
}
【运行结果】
交换前 a,b 为 10 50
交换后 a,b 为 50 10
【代码导读】
swap(&a, &b)中,
&a 表示 a 的地址,&b 表示 b 的地址,这两个值赋给 x 和 y。
t=*x; *x=*y;*y=t;
通过 x 和 y 两个指针变量,完成了 a 和 b 变量值的交换。
【例 7】利用指针对 3 个数字进行排序
#include<iostream>
using namespace std;
void swap(int* x, int* y)
{
int t;
t = *x; *x = *y; *y = t;
}
void sort(int* x1, int* x2, int* x3)
{
if (*x1 > *x2) swap(x1, x2);
if (*x1 > *x3) swap(x1, x3);
if (*x2 > *x3) swap(x2, x3);
}
int main()
{
int a, b, c;
cin >> a >> b >> c;
sort(&a, &b, &c);
cout << a << '\t' << b << '\t' << c << endl;
return 0;
}
void swap1(int a, int b)
{
int p;
p = a; a = b; b = p;
}
void swap2(int* a, int* b)
{
int p;
p = *a; *a = *b; *b = p;
}
void swap3(int& a, int& b)
{
int p;
p = a; a = b; b = p;
}
void swap4(int* a, int* b)
{
int* p;
*p = *a; *a = *b; *b = *p;
}
void swap1(int a, int b)
{
int p;
p = a; a = b; b = p;//不可以
}
void swap2(int* a, int* b)
{
int p;
p = *a; *a = *b; *b = p;//可以
}
void swap3(int& a, int& b)
{
int p;
p = a; a = b; b = p;//不可以
}
void swap4(int* a, int* b)
{
int* p;
*p = *a; *a = *b; *b = *p;
}
4、利用指针动态申请一个变量的内存空间
(1)动态申请
int* p; p = new int(); *p = 10;
或者
int* p = new int(10);
p 为整数型指针,分配内存空间为 sizeof(int),即 4 字节
(2)动态回收
用于释放 new 语句动态创建指针的内存,回收后的指针不可使用 delete p; 语句执行后,p 的值不是空指针
例
int* p = new int(10);
delete p;
5、指针的加减运算
int a=1; int b=2;
int *p=&a; int *q=&b;
假设
p 的值是 0x001ffdbc,
q 的值是 0x001ffdb0
p+3 的值:001ffdbc + 3*sizeof(int) = 001ffdc8
p-3 的值:001ffdbc - 3*sizeof(int) = 001ffdb0
p-q 的值:(001ffdbc - 001ffdb0) / sizeof(int) = 3
指针+指针、数字-指针为非法运算。
#include<iostream>
using namespace std;
int main()
{
int x = 0x12345678;
int* p = &x;
char* q = (char*)p;
int y = *q;
cout << y;
return 0;
}
#include<iostream>
using namespace std;
int main()
{
int a = 3;
int& b = a; //左值引用,a 和 b 相当于同一个变量
a = 4;
cout << b << endl;
cout << &a << endl;
cout << &b << endl;
return 0;
}
下面是错误的写法,原因是左值引用时,等号右边必须也是左值。
int a = 3;
int &b = a*a;
但下面操作是允许的,右值可以赋给 const 左值引用:
int a = 3;
const int &b = a*a;
不过不管 a 以后怎么变,b 都是 9。
8、扩展知识:右值引用
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int&& ra = a + 1;//右值引用
cout << ra;
return 0;
}
#include<iostream>
#include<string>
using namespace std;
int f(int n)
{
return 2 * n;
}
int main()
{
int&& a = f(4); //a 的地址变成了 f(4)返回值临时内存存放地址
cout << a; //8
return 0;
}