【带头学C++】----- 三、指针章* ---- 3.1指针变量的定义

news2024/11/28 22:03:31

     指针在C语言是核心,在C++中更是核心。所以本章节将详细讲解指针的使用方法以及指针的一些特殊用法,和引用的区别,以及指针涉及到一些算法基础。通过案例引导,使得能更清楚命明白。在C++中的指针是一种数据类型,其使用方法和C语言并无差别,用于存储变量的内存地址。指针提供了一种间接访问内存的方式,允许我们在程序中操作和处理内存地址,以及访问存储在该地址上的数据。

三.、指针变量

 1.内存相关知识介绍 

     在32位平台,每一个进程 拥有4G的空间。(进程的生命周期通常包括创建、运行和终止三个主要阶段。(去学习一下有关进程的章节))
系统为内存的每一个字节分配一个32位的地址编号 (虚拟地址)。这个编号称之为内存地址

       无论什么数据类型的地址,都是存储单元的编号,在32位平台下都是4个字节,即任何类型的指针变量都是4个字节大小。

      在64位平台上,每个进程通常拥有更大的内存空间。具体的大小取决于操作系统和硬件的限制,但通常远远大于32位平台的4GB。

      在64位平台上,操作系统为每个进程分配的虚拟地址空间通常达到了 2^64(即16EB,其中EB表示exabyte,1EB等于1亿亿字节)。这样巨大的地址空间允许进程能够使用更多的内存,处理更大的数据集。

注意:对于一个变量来取它的地址,实际上是取的该变量存储位置的首地址。

2. 地址和指针变量的关系

地址 就是内存的地址编号。
指针变量:本质是变量 只是该变量 保存的是内存的地址编号。(不是普通的数值)

3.1 指针变量的定义

1、定义步骤 (定义的时候)

    *修饰指针变量p。(引用千锋教育C++基础班笔记)
     保存谁的地址 就先定义谁。
从上往下整体替换。

  1. 定义一个指针变量*p,保存int num的地址:int *p;

  2. 定义一个指针变量p,保存数组int arr[5]的首地址:int (*p)[5];

  3. 定义一个指针变量p,保存函数int fun(int,int)的入口地址:int (*p)(int,int);

  4. 定义一个指针变量p,保存结构体变量struct stu Tucy的地址:struct stu *p;

      *p;

      struct stu Tucy;

  从上往下替换:struct stu *p;

  1. 定义一个指针变量p,保存指针变量int *p的地址:int **p ;

2.在32位平台任何类型的指针变量都是4字节(我的是64位,8字节)

注:定义指针的大小和类型没有关系,指针指向的的内存地址,是地址的首地址位数的大小。

 3.指针变量和普通变量建立关系

4.指针变量操作之前必须初始化

指针变量在操作之前必须指合法的地址空间
1、指针变量如果不初始化 立即操作会出现段错误


2、指针变量如果没有指向合法的空间建议初始化为NULL (代码中使用nullptr)

int *p = nullptr;//nullptr是赋值给p   int *p; p = nullptr;

 案例:使用qt5.13.2时,可以看到此时引发了错误,并且编译器也在提示警告信息,黄色的灯泡(warning: variable 'p' is uninitialized when used here)

不用操作指向null的指针变量 (如下案例,使用qt5.13.2,此时操作了指向空的指针变量,产生了段错误)

5. 将指针变量初始化为 合法的地址 (变量的地址、动态申请的地址、函数入口地地址)

                          

可以使用一行语句进行定义 ,需要注意英文输入法下的“,”前的数据类型是什么。

                          

此时,data为int类型,p为int *类型。

3.2 指针变量的类型

1、指针变量自身的类型

      将指针变量名使用鼠标拖拉遮住蓝色,剩下的类型就是指针变量自身的类型

 例:int *p; p的类型就是int *

指针变量自身的类型一般用于赋值语句的判断 

 int num = 10;
 int *p = #
//在使用中
//num 为int       &num为int*          ---->对变量名 取地址 整体类型加一个*

//p      为int *     *p     为  int          ---->对指针变量 取*  整体类型减一个*
//在使用中&和*相遇 从右往左 依次抵消
*&p == P

案例1:int num=11,*p=&num,**q=&p;以下结果正确的是   ABC


int num = 11;

int *p = #

int **q =&p; 

A;*p =11     B:*q = &num     C:p=&num    D:q=&num

2. 指针变量指向的类型

将指针变量名和离它最近的一个* 一起拖蓝,剩下的类型就是指针变量指向的类型

int *p; p指向的类型为int

 3.指针变量的指向类型 决定了取值宽度

int num = 0x01020304;

int *p = #
为啥*p == num == 0x01020304?

(引用千锋教育C++基础班笔记

 

 4、指针变量的指向类型 决定了+1跨度是多少

 

 

 

 案例1: 取出0x0102的值

short *p = (short *)#*(p+1);

案例2: 取出0x02的值 

char *p = (char )#*(p+2);

案例3:取出0x0203的值

char *p = (char *)#*(short *)(p+1)

3.3 指针变量的注意事项

1.void 不能定义普通变量

void num;//错误,不能给num变量申请内存空间

2.void*可以定义指针变量

void *p; //允许,p本身的类型为void* 。32位为4B,64位位8B

那么系统可以直接开辟空间,定义是成功的(不说没有初始化。另说)

p就是万能指针的一级指针变量,能保存任意一级指针的地址编号。

int num = 10;

vold *p=#      ----->int *
short data = 20;

p=&data;                ----->short *

万能指针一般用于函数的形参达到算法操作多种数据类型的目的;

特殊:不要对void *p的指针变量取*

int num = 10;
void *p = #
*p;//err p指向的类型为void 无法确定p的取值宽度 所以不能*p

对p取*之前对p先进行指针类型强转

int num=10;
void *p = #
cout<<*(int*)p<<end1;

 3.指针变量未初始化之前不要取*

int *p;
*p;//err 段错误

4、指针变量初始化nullptr 不要取*

int *p = nullptr;
*p;//err 段错误

5、指针变量不要越界访问

char ch = 'a';
int *p = &ch;
*p;//error越界访问非法内存

int num = 10;
int *p = &num;
p++;
*p;//越界访问

面试背题

(拓展,引用了阿秀的学习笔记中的一些知识)

1.指针和引用的区别

  1. 定义和初始化:

    • 指针是一个变量,它存储了一个地址,并允许通过该地址访问相应的内存空间。指针需要使用*操作符进行声明和初始化,例如int *ptr = &num;,其中ptr是一个指向num变量的指针。
    • 引用是一个别名,它相当于对象的一个别名,该别名在创建时必须被初始化,并且不能被重新绑定到另一个对象上。引用使用&操作符进行声明和初始化,例如int &ref = num;,其中refnum的引用。
  2. 内存管理和空值:

    • 指针可以指向任何有效的内存地址,在使用指针前需要确保它指向一个有效的对象,否则可能会导致未定义的行为。指针也可以被设置为空指针(nullptr)来表示不指向任何对象。
    • 引用必须在初始化时指向一个有效的对象,并且不能被重新绑定到其他对象上。引用不存在空值的概念,它始终引用一个有效的对象。
  3. 操作和语法:

    • 指针可以通过解引用操作符*来访问所指向的对象,并可以通过->操作符访问指向对象的成员(如果指向的是一个类类型)。指针还可以进行指针运算(如加法、减法等)。
    • 引用在使用时不需要解引用操作符,可以直接像访问对象一样使用,编译器会自动解引用引用,并访问相应的对象。引用不能进行指针运算。
  4. 重新指向和重新绑定:

    • 指针可以在任何时候重新指向不同的对象,即可以通过改变指针指向的地址来改变其所指向的对象。
    • 引用在初始化后不能再重新绑定到其他对象上,它始终引用同一个对象。

      其实根本是在于,指针指向的是内存地址,引用是引用的指针变量。引用必须初始化一个对象,指针可以为空nullptr,也可以指向内存地址编号。

2.野指针和悬空指针

    首先二者都是是指向无效内存区域(这里的无效指的是”不安全不可控)的指针,访问行为将会导致未定义行为。
野指针,指的是没有被初始化过的指针

int main(void) {
     int* p; // 未初始化
     cout<< *p << endl; // 未初始化就被使用
     return 0;
}

因此,为了防止出错,对于指针初始化时都是赋值为 nullptr,这样在使用时编译器就会直接报错,
产生非法内存访问。

悬空指针,指针最初指向的内存已经被释放了的一种指针

int main(void) {
     int * p = nullptr;
     int* p2 = new int;
     p = p2;
     delete p2;
}

      此时 p和p2就是悬空指针,指向的内存已经被释放。继续使用这两个指针,行为不可预料。需要设置为p=p2=nullptr。此时再使用,编译器会直接保错。
     避免野指针比较简单,但悬空指针比较麻烦。c++引入了智能指针,C++智能指针的本质就是避免悬空指针的产生。

3.指针加减计算要注意什么?

      指针加减本质是对其所指地址的移动,移动的步长跟指针的类型是有关系的,因此在涉及到指针加减运算需要十分小心,加多或者减多都会导致指针指向一块未知的内存地址,如果再进行操作就会很危险。

#include <iostream>
using namespace std;
int main()
{
    int *a, *b, c;
    a = (int*)0x500;
    b = (int*)0x520;
    c = b - a;
    printf("%d\n", c); // 8
    a += 0x020;
    c = b - a;
    printf("%d\n", c); // -24
    return 0;
}

       首先变量a和b都是以16进制的形式初始化,将它们转成10进制分别是1280(5*16\^2=1280)和1312(5*16\^2+2*16=1312), 那么它们的差值为32,也就是说a和b所指向的地址之间间隔32个位,但是考虑到是int类型占4位,所以c的值为32/4=8
       a自增16进制0x20之后,其实际地址变为1280 + 2*16*4 = 1408,(因为一个int占4位,所以要乘4),这样它们的差值就变成了1312 - 1280 = -96,所以c的值就变成了-96/4 = -24
       遇到指针的计算,需要明确的是指针每移动一位,它实际跨越的内存间隔是指针类型的长度,建议都转成10进制计算,计算结果除以类型长度取得结果;

4.数组名和指针(这里为指向数组首元素的指针)区别

二者均可通过增减偏移量来访问数组中的元素。
数组名不是真正意义上的指针,可以理解为常指针,所以数组名没有自增、自减等操作。
       当数组名当做形参传递给调用函数后,就失去了原有特性,退化成一般指针,多了自增、自减操作,但sizeof运算符不能再得到原数组的大小了。

5.函数指针

1) 什么是函数指针?
       函数指针指向的是特殊的数据类型,函数的类型是由其返回的数据类型和其参数列表共同决定的,而函数的名称则不是其类型的一部分。
       一个具体函数的名字,如果后面不跟调用符号(即括号),则该名字就是该函数的指针(注意:大部分情况下,可以这么认为,但这种说法并不很严格)。
2) 函数指针的声明方法

int (*p1)(const int&, const int&);

     上面的pf就是一个函数指针,指向所有返回类型为int,并带有两个const int&参数的函数。注意*p1两边的括号是必须的,否则上面的定义就变成了:

int *p2(const int&, const int&);

而这声明了一个函数p2,其返回类型为int *, 带有两个const int&参数。
3) 为什么有函数指针
   函数与数据项相似,函数也有地址。我们希望在同一个函数中通过使用相同的形参在不同的时间使用产生不同的效果。
4) 一个函数名就是一个指针,它指向函数的代码。
     一个函数地址是该函数的进入点,也就是调用函数的地址。函数的调用可以通过函数名,也可以通过指向函数的指针来调用。函数指针还允许将函数作为变元传递给其他函数;
5) 两种方法赋值:
指针名 = 函数名; 指针名 = &函数名


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1168540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java 进阶篇】Java ServletContext功能详解:域对象的使用

Java ServletContext是Java Web应用程序中的一个关键组件&#xff0c;它提供了一种在不同Servlet之间共享数据的机制。这种共享通过域对象来实现&#xff0c;包括ServletContext域、Session域和Request域。在本篇博客中&#xff0c;我们将重点关注ServletContext域&#xff0c;…

批处理写定时关机和开机自动运行老化视频

扬创科技出品的X86平板电脑&#xff08;7寸-21.5寸 6代7代10代酷睿系列 、J4125 、J1900&#xff0c;承接客户各种尺寸定制&#xff09;视频方式老化测试。 需求&#xff1a;1、开机启动自动运行老化视频。 2、下午5点25正式关机。 1、开机启动自动运行老化视频。 start &quo…

ERP系统物料管理

一、ERP系统物料管理的概述 ERP系统物料管理是企业资源计划系统中的一个关键模块&#xff0c;用于管理企业的物料信息、库存和供应链活动。通过集成各个部门的信息和流程&#xff0c;物料管理模块提供了全面的物料控制和管理能力&#xff0c;包括物料采购、入库、出库、库存调…

js 根据word文档模板导出内容

一、创建word导出模板 1、本地创建一个test.docx 2、将最终需要的文档内容及样式编辑完成(图1) 3、将所需动态值的位置,替换为变量参数(图2) 注: 动态值书写 图1 图2 模板值的书写要求 二、项目中使用 1、安装依赖 npm install docxtemplater-image-module-free --save n…

一个查看医保通讯日志的工具

一个查看医保通讯日志的工具&#xff0c;启动是可以选择不同地区。当时是负责实施的同事说&#xff0c;医保出问题时&#xff0c;核对日志文件比方便&#xff0c;所以俺写了这个工具。用于查看东软的通讯日志。这个就是个绿色的小工具。因为不同地区的通讯协议有差别&#xff0…

“第五十九天”

这是昨天那道题&#xff0c;这个后面自己的处理思路还是差了点&#xff0c;这道题关键感觉就是对进位的处理的&#xff0c;由于进位的存在&#xff0c;所以处理数据的时候只能从最低位开始&#xff0c;我一开始是从高位处理的&#xff0c;而且后面越来越迷&#xff0c;这个点一…

FSB逮捕为乌克兰网络部队工作的俄罗斯黑客

导语 近日&#xff0c;俄罗斯联邦安全局&#xff08;FSB&#xff09;逮捕了两名涉嫌协助乌克兰网络部队对俄罗斯重要基础设施目标进行网络攻击的个人。这起事件引起了广泛关注&#xff0c;涉及到了网络安全和国际关系等多个领域。本文将为您详细介绍这一事件的背景和最新进展。…

思维训练3

题目描述1 Problem - A - Codeforces 题目分析 样例1解释&#xff1a; 对于此题&#xff0c;我们采用贪心的想法&#xff0c;从1到n块数越少越好&#xff0c;故刚好符合最少的块数即可&#xff0c;由于第1块与第n块是我们必须要走的路&#xff0c;所以我们可以根据这两块砖的…

leetcode 117

leetcode 117 代码 #include <iostream>// Definition for a Node. class Node { public:int val;Node* left;Node* right;Node* next;Node() : val(0), left(NULL), right(NULL), next(NULL) {}Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}No…

【Linux进阶之路】进程(下)—— 进程控制

文章目录 前言一.再识fork1.为啥有两个返回值&#xff1f;2.为啥给父进程返回子进程的pid&#xff0c;给子进程返回0&#xff1f;3.为啥返回的同一个变量&#xff0c;地址相同&#xff0c;但值不同&#xff1f; 二.进程退出1.退出情况1.1正常退出&#xff0c;退出码正常1.2正常…

【漏洞库】XXL-JOB 默认accessToken权限绕过导致RCE

文章目录 漏洞描述漏洞编号漏洞评级影响版本漏洞复现- EXP 编写 漏洞挖掘修复建议 漏洞描述 XXL-JOB 是一款开源的分布式任务调度平台&#xff0c;用于实现大规模任务的调度和执行。 XXL-JOB 默认配置下&#xff0c;用于调度通讯的 accessToken 不是随机生成的&#xff0c;而…

基于SpringBoot+Vue的旅游系统、前后端分离

博主24h在线&#xff0c;想要源码文档部署视频直接私聊&#xff0c;低价有偿&#xff01; 基于SpringBootVue的旅游系统、前后端分离 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI 工具&#xff1a;IDEA/Ec…

微信怎么设置自动通过好友申请?

当开展引流获客活动时&#xff0c;员工会在一段时间内频繁收到好友添加的申请&#xff0c;手动同意好友请求费时费力还容易出现漏加的情况&#xff0c;那么微信能自动通过好友请求吗&#xff1f; 如何设置快速自动通过好友申请呢&#xff1f; 当微信号在系统登录&#xff0c;…

【计算机网络】网络层:数据平面

一.网络层概述 每台路由器的数据平面的主要功能时从其输入链路向其输出链路转发数据报&#xff0c;控制平面的主要功能是协调这些本地的每路由转发动作&#xff0c;使得数据报沿着源和目的地主机之间的路由器路径最终进行端到端传送。 网络层不运行运输层和应用层协议。 转发是…

【QT】绘图设备

绘图设备是指继承QPainterDevice的子类。Qt提供了很多这样的类&#xff0c;例如QPixmap、QBitmap、QImage和 QPicture。其中&#xff0c; QPixmap专门为图像在屏幕上的显示做了优化QBitmap是QPixmap的一个子类&#xff0c;它的色深限定为1&#xff0c;可以使用 QPixmap的isQBi…

安徽省黄山景区免9天门票为哪般?

今日浑浑噩噩地睡了大半天&#xff0c;强撑起身子写网文......可是&#xff0c;题材不好选&#xff0c;本“人民体验官”只得推广人民日报官方微博文化产品《这两个月“黄山每周三免门票”》。 图&#xff1a;来源“人民体验官”推广平台 因年事渐高&#xff0c;又有未愈的呼吸…

麒麟KYLINIOS软件仓库搭建02-软件仓库添加新的软件包

原文链接&#xff1a;麒麟KYLINIOS软件仓库搭建02-软件仓库添加新的软件包 hello&#xff0c;大家好啊&#xff0c;今天给大家带来麒麟桌面操作系统软件仓库搭建的文章02-软件仓库添加新的软件包&#xff0c;本篇文章主要给大家介绍了如何在麒麟桌面操作系统2203-x86版本上&…

改进YOLOv5 | 头部解耦 | 将YOLOX解耦头添加到YOLOv5 | 涨点杀器

改进YOLOv5 | 头部解耦 | 将YOLOX解耦头添加到YOLOv5 论文地址:https://arxiv.org/abs/2107.08430 文章目录 改进YOLOv5 | 头部解耦 | 将YOLOX解耦头添加到YOLOv51. 解耦头原理2. 解耦头对收敛速度的影响3. 解耦头对精度的影响4. 代码改进方式第一步第二步第三步第四步第五步本…

0-1背包 完全背包 + 至多/恰好/至少 + 空间优化 + 常见变形题

# capacity:背包容量 # w[i]: 第 i 个物品的体积 # v[i]: 第 i 个物品的价值 # 返回:所选物品体积和不超过 capacity 的前提下&#xff0c;所能得到的最大价值和 def zero_one_knapsack(capacity:int,w:List[int],v:List[int]) -> int:n len(w)cache #记忆化搜索 def dfs(i…

吴恩达《机器学习》4-6->4-7:正规方程

一、正规方程基本思想 正规方程是一种通过数学推导来求解线性回归参数的方法&#xff0c;它通过最小化代价函数来找到最优参数。 代价函数 J(θ) 用于度量模型预测值与实际值之间的误差&#xff0c;通常采用均方误差。 二、步骤 准备数据集&#xff0c;包括特征矩阵 X 和目标…