#996. 指针入门

指针入门

指针

指针类型算是 C/C++ 中相对难理解的概念之一,是根本上区别于其它语种的内容之一,是可以直接操作内存强大且风险较高的工具之一。


概念一定要清晰:指针是一种用于存储内存地址的数据类型。


需要明确一点,程序运行时的临时数据,都是存储在内存当中。而需要控制,操作该片内存之前,得先找到这片内存在哪,因此内存地址相当于是一片内存的标识


int a;
a = 1;
cout << a << endl;

int a; 表示声明了一个整数类型的变量,名称为 a。这仅仅表示 a 这个变量名可以帮助程序操控一个 4 字节大小的内存。

a = 1; 表示将数字 1 存入 a 所表示的那片内存中。

因此 cout << a << endl; 可以输出 1 ,表示通过 a 变量查看那片内存中的数值。


int a;
int *p;
p = &a;
*p = 1;
cout << a << endl;

& 在这里被称为取地址符号,简称取址符,能够计算一个变量的内存地址。

int *p; 表示声明了一个 int 类型 的指针,名称为 p 。

在声明时,* 仅表示 p 是一个指针类型的变量。

注意这里明确了 p 是 int 类型 的指针,它只能用于存放 int 类型 变量的内存地址,不能够存储其它类型变量的内存地址。(除非使用强制类型转换)

在使用指针变量 p 时:

  • p 变量存储的是内存地址
  • *p 表示这片内存,此刻 * 表示取值

int a = 1;
int *p = &a;
cout << &a << " " << a << endl; // a 的地址和 a 存的数值
cout << p << " " << *p << endl;  // 输出数值 1 和与 &a 一样的地址

注意修改变量数值的本质,是修改内存中的数值。


指针基础运用

  • 数组
  • 函数
  • sort

指针与数组

int a[10]; 表示声明了一个整数类型的数组,名称为 a,共 10 个元素,分别是 a[0], a[1], ... a[9]。

数组名称 a 有两层含义:表示这整个数组,也表明整个数组在内存中的首地址

示例

int a[10];
cout << "首地址:" << &a[0] << endl;
cout << "数组名:" << a << endl;


可以观察到两个输出结果一致,表示名称 a 是一个int 类型的地址常量

有如下用法:

int a[10] = {1, 3, 2, 4, 0, 9, 5, 6, 7, 8};
int *p = a; // 指针 p 存储数组 a 首地址,称之为 p 指向 a 的首地址

cout << "朴素数组运用" << endl;
for (int i = 0; i <= 9; i ++) {
	cout << a[i] << " ";
}
cout << endl;

cout << "利用首地址偏移" << endl;
for (int i = 0; i <= 9; i ++) {
	cout << *(a + i) << " ";
}
cout << endl;

cout << "利用指针变量偏移,这种遍历结束后,p 指针依然指向 a 数组的首地址" << endl;
for (int i = 0; i <= 9; i ++) {
	cout << *(p + i) << " ";
}
cout << endl;

cout << "利用指针变量直接偏移,这种遍历结束后,p 指针不再指向数组 a" << endl;
for (int i = 0; i <= 9; i ++, p ++) { 
    cout << *p << " ";
}
cout << endl;

指针与函数

  • 数值传递:void foo(int x, int y); 无法改变外界变量
  • 引用传递:void foo(int &x, int &y); 可以改变外界变量,根本因素是传递了整片内存
  • 地址传递 (指针传递):void foo(int *px, int *py); 直接传递内存地址,因为直接改变的是内存中的数据,因此只要是管理同片内存的存储单元都会被改变。

参考代码

void foo(int x, int y) {
    int t;
    t = x, x = y, y = t; // 外界变量不会交换
}

void foo(int &x, int &y) {
    int t;
    t = x, x = y, y = t; // 外界变量会被交换
}

void foo(int *px, int *py) {
    int t;
    t = *px, *px = *py, *py = t; // 外界变量会被交换
}

// 特别的,由于指针可以作为数组的首地址,因此函数传参数组可以有以下两种,且数组元素值若在函数内被改变,则外界数组必然被改变。

void foo(int a[], int n) { 
    // 当然 a 数组的下标范围要超过 n
    for (int i = 1 ; i <= n; i ++) {
        cout << a[i] << " ";
    }
    cout << endl;
}

void foo(int *a, int n) {
    for (int i = 1; i <= n; i ++) {
        cout << a[i] << " ";
    }
    cout << endl;
}

sort

对于sort 的参数,都是地址参数。具体参考 👉排序II



简单练习

  1. 以下对代码描述正确的是

    int* p, q;
    

{{ select(1) }}

  • p 和 q 都是指针
  • p 和 q 都不是指针
  • p 是指针,q 不是指针
  • p 不是指针,q 是指针

  1. 以下代码是否正确
    double b;
    int *p = &b;
    

{{ select(2) }}

  • 不对