内存地址
计算机的内存储器被划分成一个个的存储单元,这些存储单元按一定的规则编号,这个编号就是存储单元的地址,也就是内存地址。
地址编码的最基本单位是字节,每个字节由8位二进制组成,因此最基本内存单元大小就是一字节。每个存储单元都有唯一的地址,在这个存储单元可以存放指定的数据。
变量地址
系统分配给变量的内存空间的起始单元地址,称为该变量的地址。
变量的指针与指针变量
一个变量的地址就称为变量的指针。
专门用于存储其他变量地址(指针)的变量,称为指针变量。
指针运算符
&取地址运算符,获取变量的地址;*解引用运算符,用于获取地址中的内容
#include <iostream>
using namespace std;
int main()
{
// 定义整型变量
int i = 0;
// 定义浮点型变量
float f = 1.0;
// 定义整型指针变量
int* ip1;
// 定义浮点型指针变量
float* fp2;
// &取地址运算符,获取变量的地址
ip1 = &i;
cout << "ip1: " << ip1 << endl;
// *解引用运算符,用于获取地址中的内容
*ip1 = 100;
cout << "i: " << i << " ip1:" << ip1 << " &ip1:" << &ip1 << endl;
// 存放变量地址的指针类型应该与变量类型相同
fp2 = &f;
char c = 'a';
char* cp = &c;
//cout输出字符指针时,会把字符指针作为指针字符串输出。输出字符指针的地址时,需要转换指针类型。
cout << "(void *)cp: " << (void *)cp << " (int*)cp: " << (int*)cp << endl;
char c1[] = "Hello";
cp = &c1[0];
cout << "cp: " << cp << endl;
cp = c1;
cout << "cp: " << cp << endl;
cout << "(void *)c1: " << (void*)c1 << " (int*)c1: " << (int*)c1 << endl;
// 从大到小输出数字
int a = 10, b = 20;
int* p, * p1, * p2;
p1 = &a;
p2 = &b;
cout << "&a: " << &a << " &b: " << &b << " p1:" << p1 << " p2:" << p2 << endl;
if (*p1 < *p2)
{
p = p1;
p1 = p2;
p2 = p;
}
cout << "&a: " << &a << " &b: " << &b << " p1:" << p1 << " p2:" << p2 << endl;
cout << "max:" << *p1 << " min: " << *p2 << endl;
return 0;
}
算术运算符,指针可以与整数进行加减运算,运算结果与指针类型有关。
int a[5] = {1,2, 3, 4, 5};
int* p1 = &a[0];
// 指针加1,移动到下一个同数据类型的元素;指针减一,移动到前一个同类型的元素。
cout << "p1: " << p1 << " p1 + 1" << p1 + 1 << endl;
for (int i = 0; i < 5; i++)
{
cout << *(p1 + i) << ", "; // 算数运算符
}
cout << endl;
p1 = &a[4];
for (int i = 4; i >= 0; i--)
{
cout << *(p1 - i) << ", ";
}
cout << endl;
p1 = &a[0];
for (int i = 0; i < 5; i++)
{
cout << *(p1++) << ", "; // 自增
}
cout << endl;
p1 -= 1; // 复合运算符
for (int i = 0; i < 5; i++)
{
cout << *(p1--) << ", "; // 自减
}
cout << endl;
关系运算符,指针类型的数据可以进行关系运算,>、>=、==、!=、<、<=。
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* p1 = &a[2], * p2 = &a[9];
// NULL表示空指针。为了避免没有指向的指针,建议在定义指针变量时,可以将其初始化为NULL
int* p3 = NULL;
// 指针关系运算符比较两个指针地址的大小
cout << "p1: " << p1 << " p2: " << p2 << endl;
cout << "p1 > p2: " << (p1 > p2) << endl;
int a1 = 10, a2 = 10;
p1 = &a1;
p2 = &a2;
/*
p1,p2指向不同的变量,p1 == p2 false;
a1的值和a2的值相同,p1解引用后的结果和p2解引用后的结果相同,*p1 == *p2 true
*/
cout << "p1 == p2: " << (p1 == p2) << " *p1 == *p2: " << (*p1 == *p2) << endl;
// 关系运算符可以和NULL进行运算。
p1 = NULL;
p2 = NULL;
cout << "(p1 == p2): " << (p1 == p2) << endl;
cout << "(p1 == 0): " << (p1 == 0) << endl;
cout << "(p1 == NULL): " << (p1 == NULL) << endl;
cout << "(p1 != p2): " << (p1 != p2) << endl;
多级指针
用于保存另一个指针变量的地址的指针变量称之为指向指针的指针,即多级指针。
int n = 10;
// 定义指针
int* p1 = &n;
// 定义二级指针
int** p2 = &p1;
cout << "**p2: " << **p2 << " *p2:" << *p2 << " p2:" << p2 << " &p1:" << &p1 << " p1:" << p1 << endl;
/*
指针的大小和编译器的选项有关。
x86选项,指针大小为4个字节;x64选项指针大小为8个字节。
*/
cout << "sizeof(p1): " << sizeof(p1) << " sizeof(p2): " << sizeof(p2) << endl;
double d = 10.1;
double* p3 = &d, ** p4 = &p3;
cout << "sizeof(p3): " << sizeof(p3) << " sizeof(p4): " << sizeof(p4) << endl;
指针数组
数组元素是指针的数组称为指针数组。
int a = 10, b = 20, c = 30;
// 定义长度为3的整型指针数组,指针数组初始化的默认值是NULL
int* p[3] = {&a, &b};
cout << (p[2] == NULL) << endl;
p[2] = &c;
// 计算a,b,c三个变量的和
int sum = 0;
for (int i = 0; i < 3; i++)
{
sum += *p[i];
}
cout << "sum: " << sum << endl;
指针访问数组
数组名是数组的第一个元素的地址。
可以通过 解引用 和 [ ] 两种方式访问数组元素。
int a1[5] = { 1, 2, 3, 4, 5 };
// 数组名是数组的第一个元素的地址
cout << "a1: " << a1 << " &a[0]: " << &a1[0] << endl;
int* p1 = a1;
// 通过p1访问数组第二个元素
cout << "*(p1 + 1): " << * (p1 + 1) << endl; // 解引用的方式通过地址访问数组元素
cout << "p1[1]: " << p1[1] << endl; // []下标的方式通过p1访问数组元素
cout << "*(a1 + 1): " << *(a1 + 1) << endl;
// 修改p1指向数组第二个元素
p1++;
// 通过p1访问数组第三个元素
cout << "*(p1 + 1): " << *(p1 + 1) << endl; // 解引用的方式通过地址访问数组元素
cout << "p1[1]: " << p1[1] << endl; // []下标的方式通过p1访问数组元素
指针数组可以使用二级指针访问。
int a = 10, b = 20, c = 30;
int* p[3] = { &a, &b, &c };
// 指针数组的数组名是一个二级指针
int** p1 = p;
// 指针访问指针数组需要两次解引用才能获取指针数组元素指向的数据
for (int i = 0; i < 3; i++)
{
cout << *(p1 + i) << ", ";
}
cout << endl;
for (int i = 0; i < 3; i++)
{
cout << **(p1 + i) << ", ";
}
cout << endl;
二维数组可以通过指针数组访问。
int a[3][5] =
{
{1, 2, 3, 4, 5},
{11, 12, 13, 14},
{21, 22, 23, 24}
};
// 二维数组的数组名是第一个元素的地址
cout << "a: " << a << " &a[0][0]:" << & a[0][0] << endl;
// 二维数组的 数组名[行下标] 是该行第一个元素的地址
cout << a[1] << endl;
int* p[3] = { a[0], a[1], a[2] };
/*
p是数组p的地址;
p[0]是数组p第一个元素的值(数组a的地址);
p[0][0],相当于*(p[0] + 0)即*p[0], 是p[0]是数组p第一个元素的值指向的数据,
&p[0][0],是a[0][0]的地址,也就是a的地址。
*/
cout << "p: " << p << " p[0]: " << p[0] << " &p[0][0]:" << &p[0][0] << endl;
// 通过指针访问二维数组元素
cout << "*(*(p+1) + 2):" << *(*(p + 1) + 2) << endl; // 取地址和运算的方式
cout << "p[1][2]: " << p[1][2] << endl; // []方式
int** p1 = p;
cout << "*(*(p1+1) + 2):" << *(*(p1 + 1) + 2) << endl; // 取地址和运算的方式
cout << "p1[1][2]: " << p1[1][2] << endl; // []方式
常量指针与指针常量
常量指针
只有常量指针才能指向常量;
不能通过常量指针修改其指向的内容;
/*
常量指针
*/
int a = 10;
const int b = 20;
// 定义常量指针
const int* p1 = NULL;
int* p2 = NULL;
p2 = &a;
// p2 = &b; 只有常量指针才能指向常量
p1 = &b;
p1 = &a;
// *p1 = 20; 不能通过常量指针修改变量的值
指针常量
指针常量定义后,指向不能发生改变(不能通过赋值存储其他指针)
可以通过指针常量修改其指向的内容
/*
指针常量
*/
int m = 10, n = 20;
// 定义指针常量,定义指针常量时必须初始化
int* const p3 = &m;
// p3 = &n; 指针常量定义后,指向不能发生改变(不能通过赋值存储其他指针)
*p3 = 100; // 指针常量指向的内容可以修改
cout << "m: " << m << endl;
const int * const p4 = &m;
// *p4 = 10;
// p4 = p3;
// 常量指针才能指向常量
const char* p5 = "Hello!";
cout << p5 << endl;
cout << strlen(p5) << endl;