当前位置: 首页 > news >正文

C++编程基础(七):指针

文章目录

  • 一、指针概述
    • 1. 什么是指针?
    • 2. 指针的定义与使用
  • 二、特殊指针类型
      • 1. 空指针 (`nullptr`)
    • 2. void 指针 (万能指针)
  • 三、 指针与 `const` (关键)
    • 1. 指向 `const` 的指针 (常量指针)
    • 2. `const` 指针 (指针常量)
    • 3. 指向 `const` 的 `const` 指针
  • 四、 指针的应用
    • 1. 指针与数组
    • 2. 指针与C风格字符串
    • 3. 指针与函数
      • 3.1. 指针作为函数参数(传地址)
      • 3.2. 指针作为函数返回值
    • 4. 多级指针(指针的指针)
  • 五、 指针与引用的区别

一、指针概述

1. 什么是指针?

在计算机中,所有数据都存储在内存中。内存被划分为一个个的字节,每个字节都有一个唯一的编号,这个编号就称为地址(Address)指针(Pointer)

所谓指针变量,就是一个用来保存内存地址的变量。

2. 指针的定义与使用

C++中使用 * 来声明指针变量,使用 & (取地址运算符) 来获取一个变量的内存地址。

// 语法:定义与初始化
数据类型 *指针变量名 = &某个变量;
  • &varName取地址。获取变量 varName 在内存中的地址。
  • *ptrName解引用。访问指针 ptrName 所指向的地址上存储的值。
  1. 示例
#include <iostream>int main() {int num = 100; // 定义一个整型变量 num// 定义一个整型指针 p,并将其初始化为 num 的地址int *p = &num;// 打印 num 的值和地址std::cout << "变量 num 的值: " << num << std::endl;std::cout << "变量 num 的地址: " << &num << std::endl;// 打印指针 p 中存储的地址,以及该地址上std::cout << "指针 p 存储的地址: " << p << std::endl;std::cout << "指针 p 指向的值: " << *p << std::endl; // 解引用// 通过指针修改变量的值*p = 200; // *p 就等价于 numstd::cout << "通过指针修改后,num 的值: " << num << std::endl;return 0;
}

输出:

变量 num 的值: 100
变量 num 的地址: 0x... (某个内存地址)
指针 p 存储的地址: 0x... (与上一行相同)
指针 p 指向的值: 100
通过指针修改后,num 的值: 200

二、特殊指针类型

1. 空指针 (nullptr)

一个指针不指向任何数据,称之为空指针。

在过去(C语言或旧的C++),空指针用 NULL0 来表示。

// 旧的方式 (不推荐)
int *p1 = NULL;
int *p2 = 0;

NULL 本质上是一个宏,通常被定义为 0。这会带来歧义,例如在函数重载时:

void func(int);
void func(char*);func(NULL); // 编译器会调用 func(int),而不是 func(char*)!

为了解决这个问题,C++11 引入了 nullptr 关键字,它是一个类型安全的空指针常量,只能被转换为空指针。

在现代 C++ 中,应始终使用 nullptr 初始化空指针。

// 推荐的方式
int *p = nullptr;

注意: 未经初始化的指针(如 int *p;)是一个野指针,它指向一个未知的内存地址。对其解引用是极其危险的,会导致未定义行为。

2. void 指针 (万能指针)

void* 是一种特殊的指针类型,被称为“无类型指针”或“通用指针”。它可以指向任何类型的数据,无需进行强制类型转换,但有以下限制:

  1. void 指针可以接收任何类型变量的地址。
  2. 不能直接解引用 void 指针,因为它不包含类型信息(编译器不知道该读取几个字节)。
  3. 在使用前,必须将其强制类型转换为具体的数据类型指针。
int num = 100;
void *p = &num; // void* 可以持有 int*std::cout << "地址: " << p << std::endl;
// std::cout << *p << std::endl; // 编译错误!不能直接解引用// 必须强制转换回原始类型
std::cout << "值: " << *((int*)p) << std::endl;

三、 指针与 const (关键)

const* 的相对位置决定了到底什么是“常量”。

一个简单的区分方法: 从右向左阅读声明。

1. 指向 const 的指针 (常量指针)

  • 声明: const int *p; (或者 int const *p;)
  • 解读: p 是一个指针,它指向一个 const int (常量整数)。
  • 含义:
    • 不能通过 *p 修改所指向的值。
    • 可以改变 p 自己的指向。
int num = 100, num2 = 200;
const int *p = &num;// *p = 111; // 错误!不能通过 p 修改值
p = &num2;  // 正确!p 的指向可以改变

2. const 指针 (指针常量)

  • 声明: int * const p = &num;
  • 解读: p 是一个 const (常量) 指针,它指向一个 int (整数)。
  • 含义:
    • 可以通过 *p 修改所指向的值。
    • 不能改变 p 自己的指向(p 必须在定义时初始化)。
int num = 100, num2 = 200;
int * const p = &num; // 必须在定义时初始化*p = 111;   // 正确!可以修改值
// p = &num2; // 错误!p 的指向不能改变

3. 指向 constconst 指针

  • 声明: const int * const p = &num;
  • 解读: p 是一个 const (常量) 指针,它指向一个 const int (常量整数)。
  • 含义:
    • 不能通过 *p 修改所指向的值。
    • 不能改变 p 自己的指向。
int num = 100, num2 = 200;
const int * const p = &num;// *p = 222;  // 错误!
// p = &num2; // 错误!

四、 指针的应用

1. 指针与数组

数组名在C++中可以被看作一个指向数组首元素(第0个元素)的常量指针

int arr[3] = {1, 2, 3};
int *p = arr; // 将指针 p 指向数组首地址 (等价于 &arr[0])

注意: 数组名 arr 和指针 p 并不完全等价。arr 是一个常量,不能被修改(arr++ 是非法的),而 p 是一个变量,可以移动。

使用指针遍历数组(指针算术):
对指针进行加减运算(如 p+1),指针会移动 sizeof(数据类型) 个字节,从而精确指向下一个元素。

int arr[3] = {1, 2, 3};
int *p = arr;// 方法一:使用 *(p + i)
std::cout << "方法一:" << std::endl;
for (int i = 0; i < 3; i++) {std::cout << "arr[" << i << "] = " << *(p + i) << std::endl;
}// 方法二:移动指针 p (p++ 会修改 p 的值)
std::cout << "方法二:" << std::endl;
for (int i = 0; i < 3; i++) {std::cout << "arr[" << i << "] = " << *p << std::endl;p++; // 将 p 指向下一个元素
}
// 此时 p 不再指向 arr[0]

2. 指针与C风格字符串

C风格字符串本质上是 char 数组。

// 方式一:字符数组 (在栈上,内容可修改)
char str1[] = "hello world";
str1[0] = 'H'; // 正确
char *p1 = str1;
*(p1 + 1) = 'E'; // 正确// 方式二:指向字符串字面量 (在常量区,内容不可修改)
char *str2 = "hello world"; 
// *str2 = 'H'; // 编译通过,但运行时会崩溃!(未定义行为)// 
// 现代C++的正确写法:
const char *str3 = "hello world";
// *str3 = 'H'; // 编译错误!(const 阻止了修改)

注意:
char *varName = "字符串"; 是一种已废弃且极其危险的写法。它试图用一个可修改的指针指向一块不可修改的内存(字符串字面量存储在常量区)。应始终使用 const char* 来指向字符串字面量。

3. 指针与函数

3.1. 指针作为函数参数(传地址)

使用指针作为函数参数,可以在函数内部修改函数外部的变量(类似于引用)。

案例: 交换两个变量的值。

// 传入指针,函数内部通过解引用 *p 来修改实参
void change(int* p1, int* p2) {int tmp = *p1;*p1 = *p2; // 修正:这里是 *p2 (取值),而不是 p2 (取地址)*p2 = tmp;
}int main() {int num1 = 100, num2 = 200;std::cout << "交换前: num1=" << num1 << ", num2=" << num2 << std::endl;// 传入变量的地址change(&num1, &num2);std::cout << "交换后: num1=" << num1 << ", num2=" << num2 << std::endl;return 0;
}

3.2. 指针作为函数返回值

函数可以返回一个指针。

注意:
绝不可以返回函数内部局部变量的地址。函数结束后,局部变量被销毁(其内存被释放),返回的指针将成为悬垂指针(Dangling Pointer),指向一块不再有效的内存。

// 错误示例:返回局部变量的地址
int* func_bad() {int num = 100;return &num; // 危险!num 在函数返回时被销毁
}// 正确示例:返回静态变量的地址
int* func_good() {// static 变量的生命周期是整个程序static int num = 100;return &num;
}int main() {int *p1 = func_bad();// cout << *p1 << endl; // 极度危险,可能崩溃,或输出垃圾值int *p2 = func_good();std::cout << *p2 << std::endl; // 正确,输出 100*p2 = 200;std::cout << *p2 << std::endl; // 正确,输出 200return 0;
}

(注:返回堆内存 (new) 也是一种方法,但需要调用者 delete,暂不讨论。)

4. 多级指针(指针的指针)

多级指针就是指向指针的指针。二级指针存储的是一级指针的地址。

int num = 100;// 一级指针
int *pnum = &num;// 二级指针
int **ppn = &pnum; // ppn 存储了 pnum 的地址// 访问
std::cout << "num = " << num << std::endl;
std::cout << "*pnum = " << *pnum << std::endl;
std::cout << "**ppn = " << **ppn << std::endl; // 解引用两次

理论上,指针的级数没有限制(int ******pa6;),但实际应用中,超过二级指针就很少见了。

五、 指针与引用的区别

引用(Reference) 是 C++ 中的一个重要概念,它是一个变量的别名

特性指针 (Pointer)引用 (Reference)
本质存储变量的地址变量的别名
初始化可以不初始化(不推荐,会成为野指针)。必须在定义时初始化。
空值可以为 nullptr不能有 “空引用”。
可变性可以改变指向(指向另一个变量)。一旦绑定,不能再更改为其他变量的别名。
操作需要 * (解引用) 来访问值。像普通变量一样直接使用。
内存指针变量本身占用内存空间。不(或说“概念上不”)占用额外内存。
多级可以有二级、三级…指针 (int **p)。只能有一级 (int &&r 有不同含义)。

简单总结:

  • 指针更强大、更灵活,但也更危险(空指针、野指针、内存泄漏)。
  • 引用更安全、更易用(没有空引用、自动解引用)。

关于常量引用 (const int&):

在函数传参时,如果不想修改参数,又想避免复制(特别是对于大对象),使用常量引用是 C++ 中最高效、最安全的做法。
void printObject(const MyObject& obj);

http://www.dtcms.com/a/611346.html

相关文章:

  • 怎么给网站做spm下载站推广
  • 一个网站包括新闻门户网站源码
  • pvc模板多少钱一平方seo博客教程
  • 美容院玩转小程序,实现预约、宣传、在线商城等功能
  • 针对 Windows 常见端口的渗透思路
  • 用户态与内核态(deepseek问答)
  • 宜宾网站建设工作室多用户商城思维导图
  • 公司网站开发报价徐州模板网站
  • 罗城建设局网站做网站客户总是要退款
  • 公司网站制作 步骤网站常见错误代码
  • S32ds
  • 如何使用谷歌云的 Nano / Banana 大模型?从入门到实战演示
  • 树与图的深度和广度优先遍历-java实现邻接表存储
  • 有个网站可以学做ppt模板第三方网站
  • Python 中的 *args 和 **kwargs
  • 蘑菇街的网站建设如何做网站主赚钱
  • 全国网站直播平台被摧毁wordpress插件免费吗
  • 连锁餐饮行业ERP系统如何选择?
  • 做网站的绿色背景图有什么做木工的网站
  • 专业网网站建设赣州新闻头条
  • idea 启动失败,不加载自己的配置文件
  • 静态网站结构如何更新wordpress变色龙主题
  • 进入职场第五课——突破和跃升
  • SSN和ijtag在scan中的应用与区别
  • 网站设计总结与心得体会flash制作技巧
  • 怎么给网站做懒加载网站开发 非对称加密
  • 零偏压石墨烯探测器赋能《Nature Communications》披露全等离子体太赫兹收发芯片新突破
  • 在百度怎么建立自己的网站网推平台有哪些
  • 做搜狗pc网站点嘉上营销
  • Paimon的merge-engine配置