1. 什么是指针变量?
指针变量是专门有一个变量来存放指针。
int main(int argc, char *argv[])
{
int a = 10;
int *p = &a; //通过取地址符号 & 把 a 变量的地址传给指针变量 p
std::cout << "p is " << p << std::endl; // p is 0x7fffe740301c
std::cout << "&a is " << &a << std::endl; //&a is 0x7fffe740301c
std::cout << "*p=" << *p << std::endl; // *p=10
return 0;
}
通过符号 *p
获取指针变量 p
指向的地址的存放的值。我们可以理解为通过变量 p
我们拿到抽屉的钥匙(地址)打开抽屉取出数据。
int main()
{
int i = 3;
int *iptr = &i;
int **iptrptr = &iptr; //iptr 也是变量,也能够获取它的地址
cout << "iptr=" << iptr <<endl; //输出 iptr 存储的内容,即 i 在内存中的地址
cout << "*iptr=" << *iptr <<endl; //输出 iptr 所指向的变量
cout << "iptrptr=" << iptrptr <<endl; //输出 iptr 在内存中的地址
cout << "*iptrptr=" << *iptrptr <<endl; //输出 iptrptr 所指向的变量,即 iptr
*iptr = 2 + *iptr; //*iptr 可以作左值
cout << "*iptr=" << *iptr <<endl;
return 0;
}
输出结果:
iptr=0x7ffd0e00e2b4
*iptr=3
iptrptr=0x7ffd0e00e2b8
*iptrptr=0x7ffd0e00e2b4
*iptr=5
2. 数组和指针
int main(int argc, char *argv[])
{
int a[3] = {1,2,3};
int *p = a;
std::cout << "p is " << p << std::endl;
std::cout << "&a is " << &a << std::endl;
std::cout << "a is " << a << std::endl;
std::cout << "&a[0] is " << &a[0] << std::endl;
std::cout << "*p=" << *p << std::endl;
std::cout << "*(p+1)=" << *(p+1) << std::endl;
std::cout << "a[1]=" << a[1] << std::endl;
return 0;
}
执行输出结果:
p is 0x7fff7b96be40
&a is 0x7fff7b96be40
a is 0x7fff7b96be40
&a[0] is 0x7fff7b96be40
*p=1
*(p+1)=2
a[1]=2
a
的值就是数组 a
第一个元素的地址,与 &a[0]
和 &a
等价。
而 p+1 表示的是数组 a 的第二个元素的地址,以此类推。
获取数组的第二个元素可以是 a[1],也可以是 *(p+1)。
3. 指针数组
表示数组内存放的是指针类型的数据。
定义方式
int *p[]:
示例:
int main(int argc, char *argv[])
{
int a[3] = {1,2,3};
int *p[3] = {a,a+1,a+2};
std::cout << "a= " << a << std::endl;
std::cout << "a+1= " << a+1 << std::endl;
std::cout << "a+2= " << a+2 << std::endl;
std::cout << "*p=" << *p << std::endl;
std::cout << "*(p+1)=" << *(p+1) << std::endl;
std::cout << "p[1]=" << p[1] << std::endl;
std::cout << "**p=" << **p<< std::endl;
std::cout << "**(p+1)=" << **(p+1) << std::endl;
return 0;
}
输出:
a= 0x7ffd747a1b70
a+1= 0x7ffd747a1b74
a+2= 0x7ffd747a1b78
*p=0x7ffd747a1b70
*(p+1)=0x7ffd747a1b74
p[1]=0x7ffd747a1b74
**p=1
**(p+1)=2
a
、a+1
、a+2
分别表示数组 a 第一个、第二个、第三个元素的地址。
p+1
指的是指针数组 p
的第二个元素的地址,而 *(p+1)
(等价于 p[1]
)指的就是第二个元素地址存放的值,也就是 a+1
。
a+1
与 *(p+1)
值是相等的。
4. 数组指针
表示指针是数组类型的,就像 int
类型指针,char
类型指针一样。
定义方式:
int (*p)[n]
()
优先级高,首先说明 p
是一个指针,指向一个整型的一维数组,这个一维数组的长度是 n,也可以说是 p 的步长。
示例:
int main(int argc, char *argv[])
{
int a[3] = {1,2,3};
int (*p)[3] = &a; //定义一个指向长度为 3 的 int 数组的指针
std::cout << "a = " << a << std::endl;
std::cout << "a+1= " << a+1 << std::endl;
std::cout << "p+1= " << p+1 << std::endl;
std::cout << "a+2= " << a+2 << std::endl;
std::cout << "&a= " << &a << std::endl;
std::cout << " p= " << p << std::endl;
std::cout << "*p= " << *p << std::endl;
return 0;
}
输出:
a = 0x7ffd63226b30
a+1= 0x7ffd63226b34
p+1= 0x7ffd63226b3c
a+2= 0x7ffd63226b38
&a= 0x7ffd63226b30
p= 0x7ffd63226b30
*p= 0x7ffd63226b30
&a
与 a
、p
值是相等的,都是指数组 a
的第一个元素的地址。
仔细看两个值,可以发现 p+1
(0x7ffd63226b3c)是 p
(0x7ffd63226b30)再加 12,也就是数组 a
所有元素加起来的长度(长度 3*int 类型的 4 个字节),所以数组指针 p
再加 1,这里的 1 指的就是数组 a
所有元素加起来的长度,而不是数组元素的长度。
p
与 *p
两个值是一样的。*p
的值是数组 a
第一个元素的地址,而 p
虽然也是数组 a
第一个元素的地址,但是 p
指的是整个数组的地址,只是用了数组 a
的第一个元素地址来替代,而 *p
是指这个数组的地址对应的值,就是数组本身,
也就是 a
,而 a
的值也就是数组 a
第一个元素的地址,所以 *p
等于 a
。
int a[3] = {1,2,3};
int (*p)[3] = &a;
cout<<**p<<endl;
cout<<*(*p+1)<<endl;
cout<<(*p)[1]<<endl;
输出:
1
2
2
*p 表示数组第一个元素地址,**p 表示数组第一个元素地址存放的值也就是 1;p+1 表示数组第二元素地址,(*p+1) 表示数组第二个元素地址存放的值也就是 2。(*p)[1] 与 *(*p+1) 值一样,都是表示数组第二个元素值,即 a[1]。
如果改成下面:
int main(int argc, char *argv[])
{
int a[3] = {1,2,3};
int (*p)[3] = a;
return 0;
}
会编译报错:
cannot convert ‘int*’ to ‘int (*)[3]’ in initialization
因为 a
这里是一维数组,a
代表的是数组 a
首个元素地址,而不是整个数组地址。
假设 a 数组是二维数组,如下定义:
int main(int argc, char *argv[])
{
int a[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3] = &a;
return 0;
}
会编译报错:
cannot convert ‘int (*)[2][3]’ to ‘int (*)[3]’ in initialization
因为这里 a
是二维数组,而 &a
表示整个二维数组的地址,是包含 2*3=6 个元素的数组。那要怎么改才可以呢?
可以改成:
int (*p)[3] = a; // 这里 a 理解为二维数组 a 第一维 a[0][]数组的地址。
或者改为:
int (*p)[2][3] = &a; // 表示2行3列的二维数组地址
完整代码:
int main(int argc, char *argv[])
{
int a[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3] = a;
std::cout << "a= " << a << std::endl;
std::cout << "a+1= " << a+1 << std::endl;
std::cout << "a+2= " << a+2 << std::endl;
std::cout << "&a=" << &a << std::endl;
std::cout << "p=" << p << std::endl;
std::cout << "*(p+1)=" << *(p+1) << std::endl;
std::cout << "p[1]=" << p[1] << std::endl;
std::cout << "**p=" << **p<< std::endl;
std::cout << "**(p+1)=" << **(p+1) << std::endl;
return 0;
}
输出结果:
a= 0x7ffe52d671f0
a+1= 0x7ffe52d671fc
a+2= 0x7ffe52d67208
&a=0x7ffe52d671f0
p=0x7ffe52d671f0
*(p+1)=0x7ffe52d671fc
p[1]=0x7ffe52d671fc
**p=1
**(p+1)=4
5. 指针常量与常量指针
int main()
{
int a = 42;
const int b = 84;
const int *c_a_ptr = &a; //常量指针
int * const a_c_ptr = &a; //指针常量
int *b_ptr = &b; //错误,不能把常量的地址给指针变量
const int *c_b_prt = &b; //把常量的地址给常量指针是允许的
*c_a_ptr = 68; //错误,间接引用常量指针不可修改内存中的数据
*a_c_ptr = 68; //间接引用指针常量可以修改内存中的数据
c_a_ptr = &b; //常量指针可以指向其他变量
a_c_ptr = &b; //错误,指针常量不能指向别的变量
const int * const c_c_a_ptr = &a; //常量指针常量,既不能间接引用修改数据,也不能指向别的变量或常量
*c_c_a_ptr = 68; //错误,不能间接引用修改数据
c_c_a_ptr = &b; //错误,不能指向别的常量或变量
return 0;
}