指针和引用

C++中我们可以使用兼容C的指针特性,此外C++还引入了引用的概念,这篇笔记我们介绍指针和引用的用法。

指针

指针是C++中的C兼容语法。指针本身也是一个变量,但它存储的是另一个变量的地址。指针类型使用数据类型和星号定义,例如int *char *等,对一个变量使用取地址符&即可得到它的指针,对于指针则可以使用解引用操作符*获取指针指向的值,下面是一个例子。

#include <iostream>

int main() {
  int a = 10;
  int *p = &a;
  // 打印指针p的值
  std::cout << p << std::endl;
  // 打印p指向的值
  std::cout << *p << std::endl;
  return 0;
}

代码中,我们尝试打印指针的值,它其实是一个内存地址。

指针所占内存空间

对于32位程序,指针占4字节,对于64位程序,指针占8字节。我们可以使用sizeof操作观察指针所占的字节数。

#include <iostream>

int main() {
  int a = 10;
  int *p = &a;
  std::cout << sizeof(p) << std::endl;
  return 0;
}

空指针

空指针是指向地址为0的指针,通常在指针变量定义但我们暂时还不能给它设置值时,我们习惯将其设置为空(C++中NULL0的宏定义)。0这个内存地址本身不可能被访问,我们将其设置为0的目的在于后续用于判断该指针是否已被初始化。

int *p = NULL;

野指针

C++中野指针是一种错误,它表示指针指向了非法的内存地址。下面例子中,0x100是我们随意指定的不合法地址,此时p就是一个野指针。

int *p = (int *)0x100;

指针和const修饰符

指针和const组合使用时,根据const的位置不同可以表达常量指针、指向常量的指针或是指向常量的常量指针,C++初学者对这些情况经常发生混淆,其实它很简单,我们倒着观察即可。

const int *p倒着观察,p是一个指针,指向int类型的常量。

const int a = 1;
const int *p = &a;

int const *p倒着观察,p是一个指针,它是常量的(即常量指针),指向int类型的变量。

int a = 1;
int const *p = &a;

const int const *p同理,倒着观察可以解释为指向常量的常量指针。

const int a = 1;
const int const *p = &a;

指针和数组

数组名其实可以被视为一个常量指针,我们基于这个指针可以访问数组元素或是遍历数组,下面是一个例子。

#include <iostream>

int main() {
    int arr[] = {1, 2, 3};
    for (int i = 0; i < std::size(arr); i++) {
        std::cout << *(arr + i) << std::endl;
    }
    return 0;
}

指针作函数参数

指针作函数参数时,它虽然仍是值传递,但我们传入的是变量的地址,因此变量本身仍可被修改。下面例子中,变量a经过func()函数操作后,它的值会被修改为10

#include <iostream>

void func(int *a) { *a = 10; }

int main() {
  int a = 0;
  func(&a);
  std::cout << a << std::endl;
  return 0;
}

引用

引用和指针有很多容易让初学者混淆的相似点,但它和指针的本质不同。指针是一个内存中确实存在的存储内存地址的变量,引用则是C++语言层面的概念,它是变量的别名。下面例子代码定义了一个变量a的引用b

#include <iostream>

int main() {
  int a = 0;
  int &b = a;
  std::cout << b << std::endl;

  // 变量a被改变,使用b访问时得到的也是新的值
  a = 1;
  std::cout << b << std::endl;
  return 0;
}

引用的使用有一些注意事项:

  1. 引用声明时必须初始化,即引用必须绑定某个变量。和NULL空指针不同,没有“空”引用的概念。
  2. 引用在初始化后不可以概念,类似常量指针,绑定某个变量的引用不可以修改为绑定其它变量

引用作函数参数

引用作函数参数意味着参数是引用传递的,下面例子中,变量a引用传递到函数func()中,函数对其值的修改会反映到函数作用域的外部。

#include <iostream>

void func(int &a) { a = 10; }

int main() {
  int a = 0;
  func(a);
  std::cout << a << std::endl;
  return 0;
}

常量引用

有时我们需要引用传递避免值传递造成拷贝开销,但又不希望函数在函数内修改参数,这可以使用常量引用。常量引用其实就是使用const修改函数的形参,这种写法主要用于告知编译器该参数在函数内是不可变的,防止代码出现Bug造成误修改。

#include <iostream>

struct Data {
  int value;
};

void printData(const Data &data) {
  std::cout << "Data value: " << data.value << std::endl;
}

int main() {
  Data data;
  data.value = 42;
  printData(data);
  return 0;
}
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap