C++基础(6) - 复合类型(下)

news2024/12/23 20:51:22

文章目录

  • 指针
    • 1、指针概述
      • 1.1 存储器和存储地址空间
      • 1.2 内存地址
      • 1.3 指针和指针变量
    • 2、声明和初始化指针变量
      • 2.1 指针变量的声明
      • 2.2 指针变量的初始化
    • 3、使用指针变量
      • 3.1 解除引用
      • 3.2 野指针和空指针
    • 4、指针的宽度和跨度
      • 4.1 自身类型和指向类型
      • 4.2 指针变量所取内容的宽度
      • 4.3 指针变量加1的跨度
    • 5、指针数组
    • 6、二级指针
    • 7、数组的地址
    • 8、`new` 和 `delete` 运算符
      • 8.1 使用 `new` 分配内存
      • 8.2 使用 `delete` 释放内存
    • 9、动态数组的创建
      • 9.1 使用 `new` 创建动态数组
      • 9.2 使用 `new` 和 `delete` 的规则
    • 10、动态结构体的创建
  • C++ 内存管理
    • 1、C++ 的内存划分
    • 2、C++ 管理数据内存的方式
    • 3、内存泄漏
  • 类型别名
  • 字符函数库 —— `cctype`

指针

1、指针概述

1.1 存储器和存储地址空间

  • 存储器:计算机的组成中,用来存储程序和数据,辅助 CPU 进行运算处理的重要部分。
    • 内存:内部存储器,暂存程序或数据,掉电丢失。如 SRAMDRAMDDR3DDR4
    • 外存:外部存储器,长时间保存程序或数据,掉电不丢失。ROMFLASH、硬盘、光盘;
  • 内存是沟通 CPU 和 硬盘的桥梁:
    • 暂时存放 CPU 中的运算数据;
    • 暂时存放与硬盘等外部存储器交换的数据;
  • 存储地址空间:对存储器编码的范围。
    • 编码:对每个物理存储单元(一个字节)分配一个号码;
    • 寻址:根据分配的号码找到相应的存储单元,完成数据的读写;

1.2 内存地址

  • 将内存抽象为一个很大的一维字符数组,通过编码为内存的每一个字节分配一个32位或64位的编号,这个内存编号就被称为内存地址(唯一);
  • 内存中的数据会被分配不同的地址个数。例如 char 占一个字节分配一个地址,int 占四个字节分配四个地址…
  • 对变量应用地址运算符 & 可以获取其在内存中的地址。例如对于变量 int num,则 &num 就是它的地址;

1.3 指针和指针变量

  • 指针:和 intdouble 等基础数据类型类似,是一种独立的数据类型,不同的前者存储的是实在的数据,但是指针这种类型存储的是指向这些数据的内存地址;
  • 指针变量:本质就是变量,只是该变量存储的是内存地址,而不是普通的数据。不同类型的指针变量所占用的存储单元长度是相同的;
  • 为什么要使用指针类型来保存地址值:
    • 编译时类型检查;
    • 指明一个内存地址所保存的二进制数据该怎么解释;

 

2、声明和初始化指针变量

2.1 指针变量的声明

数据类型 * 变量名;	int * num;
数据类型 *变量名;	int *num;
数据类型* 变量名;	int* num;

这里需要强调的是,数据类型* 才是指针变量的数据类型,如 int*double* 等等。而对于在哪里添加空格,对于编译器来说没有任何区别。

* 前面的类型是什么,*num 中就存储的是什么类型的数据。如 int* p,说明 *p 获取的是 int 型数据,但是存储地址的变量 p 的长度都是相同的,它取决于计算机系统。*p 这种形式的表示方法的意思之后会介绍。

注意:对每一个指针变量名,都需要使用一个 *

int* p1, p2;	// 声明创建一个指针变量p1和一个int类型变量p2
int *p1, *p2;	// 声明创建一个指针变量p1和一个指针变量p2

2.2 指针变量的初始化

  • 在声明语句中初始化指针变量:

    int num = 5;
    int *pt_num = #
    
  • 声明指针变量后对其进行初始化:

    double d = 2.0;
    double *pt_d;
    pt_d = &d;
    

指针变量

 

3、使用指针变量

3.1 解除引用

  • * 运算符称为间接值(indirect value)或解除引用运算符,将其应用于指针变量,可以得到该地址处存储的值;

  • 即假如 ptr 是一个指针变量,则 ptr 存储的是一个地址,而 *ptr 表示存储在该地址处的值;

  • 一定要在对指针变量应用解除引用之前,将指针变量初始化为一个确定的、适当的地址。因为在 C++ 中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存:

    long *ptr;
    *ptr = 22332223;	// 不能这样赋值!!
    

    若分配给 ptr 的地址中有重要的数据、甚至是系统值,此时给里面赋值,很可能就造成错误!

  • 不能简单的将整数赋值给指针变量,指针不是整型:

    // int *ptr = 0xN8000000;	// 错误!!
    
    int *ptr = (int *)0xB8000000;
    

3.2 野指针和空指针

  • 任意数值赋值给指针变量没有意义,这样的指针变量就是野指针,野指针指向的区域是未知的;
  • 野指针不会直接引发错误,操作野指针指向的内存区域可能会出问题;
  • 在 C++ 中,值为 0 的指针变量被称为空指针(null pointer)。空指针不会指向有效的数据;
  • 在 C++ 中,空指针的值可以用 0、NULL、nullptr 来展示;
  • C++ 提供了检测并处理内存分配失败的工具,后面再讨论;

 

4、指针的宽度和跨度

4.1 自身类型和指向类型

  • 指针变量的自身类型:去掉变量名,剩余的部分就是指针变量的自身类型,例如 int *int **
  • 指针变量的指向类型:去掉变量名和离它最近的一个 *,剩余的部分就是指针变量指向的类型,例如 intint *

4.2 指针变量所取内容的宽度

指针变量所取内容的宽度是由指针变量所指向的类型长度决定的。

例如 int *p1 = (int *)0x01020304;p1 指向 int 类型,所取内容宽度为 4short *p2 = (short *)0x01020304;p2 指向 short 类型,所取内容宽度为 2

4.3 指针变量加1的跨度

指针变量加1的跨度是由指针变量所指向的类型的大小决定。

例如,int 类型所占的字节数为4,那么它的指针变量加1,就会跨越4个地址。

 

5、指针数组

指针数组的本质是数组,数组中的每个元素都是指针类型。

int num1 = 1;
int num2 = 2;
int num3 = 3;
int num4 = 4;

int *arr[4];
arr[0] = &num1;
arr[1] = &num2;
arr[2] = &num3;
arr[3] = &num4;

// 另一种赋值
int *arr[4] = {&num1, &num2, &num3, &num4};

指针数组

 

6、二级指针

如果一个指针指向的是另外一个指针,称它为二级指针,或者指向指针的指针。

int num = 5;	// 内存中一块空间存储了5
int *p = #	// p存储了指向这块空间的地址,*p获取的是num的数值5
int **q = &p;	// q存储了指向p的内存地址,*q表示p中存储的地址值,**q获取的是num的值5

二级指针

 

7、数组的地址

一个数组的数组名被解释为其第一个元素的地址,而对数组名应用地址运算符时,得到的是整个数组的地址。

short tell[10];
cout << tell << endl;	// 0x61fe00;展示 &tell[0] 的地址
cout << (tell + 1) << endl;	// 0x61fe02;加了2
cout << &tell << endl;	// 0x61fe00;展示整个数组的地址
cout << (&tell + 1) << endl;	// 0x61fe14;因为十六进制,所以加20为0x14
cout << sizeof(tell) << endl;   // 20;将 sizeof 用于数组名,返回整个数组的长度(单位为字节)

虽然 tell&tell 的两个地址在结果上是一样的,但从概念上 &tell[0](即 tell) 是一个 2 字节内存块的地址,而 &tell 是一个 20 字节的内存块地址。因此 tell+1 将地址值加2,而 &tell + 1 将地址加20。

具体的 &tell 是怎么来的呢?语句如下:

short (*pas)[10] = &tell;

 

8、newdelete 运算符

8.1 使用 new 分配内存

  • C++ 中可以使用 new 运算符在运行阶段分配内存;

  • 动态分配内存的格式:

    typeName *pointer = new typeName;
    // 例如
    int *p = new int;
    
  • new 运算符返回该内存的地址;

  • 数据对象指的是为数据项分配的内存块;

  • 动态分配内存使程序在管理内存方面有更大的控制权;

  • new 分配的内存块通常与常规变量声明分配的内存块不同。变量的值都存储在栈内存(stack)区域中;而 new 运算符从堆(heap)或自由存储区(free store)的内存区域分配内存,所以最后一定要记得释放内存;

8.2 使用 delete 释放内存

  • delete 运算符可以在使用完内存后,将内存归还给内存池。归还或释放的内存可供程序的其他部分使用;

  • 使用 delete 时,后面要加上指向内存块的指针变量(内存块是由 new 分配):

    int *p = new int;
    ...
    delete p;
    

注意

  • 一定要配对使用 newdelete,斗则会发生内存泄漏(memory leak);
  • 不要释放已经释放的内存块;
  • 不能使用 delete 来释放声明变量所获得的内存;

 

9、动态数组的创建

9.1 使用 new 创建动态数组

  • 通常对于大型数据(如数组、字符串和结构),应使用 new 来分配;

  • 创建动态数组需要将数组的元素类型和元素个数数目告诉 new,格式如下:

    typeName *pointer = new typeName[num];
    // 例如
    int *p = new int[10];
    
  • new 运算符返回第一个元素的地址;

  • 通过指针变量 pointer 来操作数组;

  • 对于使用 new 创建的数组,应使用 delete [] 来释放:

    delete [] p;
    

9.2 使用 newdelete 的规则

  • 不要使用 delete 来释放不是 new 分配的内存;
  • 不要使用 delete 释放同一个内存块两次;
  • 如果使用 new [] 为数组分配内存,则应使用 delete [] 来释放;
  • 如果使用 new 为一个实体分配内存,则应使用 delete 来释放;
  • 对空指针应用 delete 是安全的(不会有任何事情发生);

 

10、动态结构体的创建

new 用于结构体由两步组成:创建动态结构体和访问其成员。

  1. 创建动态结构体的方式跟普通类型完全相同,格式如下:

    typeName *pointer = new typeName;
    // 例如:
    struct student {
    	int id;
        string name;
    };
    student *stu = new student;
    
  2. 访问动态结构体的成员:

    (*stu).id;
    // 或者
    stu->id;
    

 
 

C++ 内存管理

1、C++ 的内存划分

  • 栈区:由编译器自动分配与释放,存放为运行时的函数分配的局部变量、函数参数、返回数据、返回地址等。其操作类似于数据结构中的栈;
  • 堆区:一般由程序员自动分配,如果程序员没有释放,程序结束时可能由系统回收;
  • 全局区(静态区):存放全局变量、静态数据。程序结束后由系统释放;
  • 常量区(文字常量区):存放常量值,如字符串常量。不允许修改,程序结束后由系统释放;
  • 代码区:存放函数体(类成员函数和全局区)的二进制代码;

 

2、C++ 管理数据内存的方式

C++ 根据用于分配内存的方法,有 3 种管理数据内存的方式:自动存储、静态存储、动态存储。这 3 种方式分配的数据对象存在时间的长短方面各不相同。

  1. 自动存储:在函数内部定义的常规变量使用自动存储空间,被称为自动变量。它们在所属的函数被调用时自动产生,在该函数结束时自动消亡。自动变量通常存储在栈内存中。

  2. 静态存储:静态存储是整个程序执行期间都存在的存储方式。使变量称为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字 static

    static int num = 10;
    
  3. 动态存储newdelete 运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在 C++ 种被称为自由存储空间。该内存池跟用于静态变量和自动变量的内存是分开的。在栈中,自动添加和删除机制使得占用的内存总是连续的,但 newdelete 的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难。

 

3、内存泄漏

如果使用 new 运算符在自由存储空间(或堆)上创建变量后,没有调用 delete,即使指针变量的内存被释放,但在自由存储空间上动态分配的内存也将继续存在。因为指向这些内存的指针无效,这样将无法访问自由存储空间中的这些内存,从而导致内存泄漏。

被泄露的内存在程序整个生命周期内都不可使用,因为这些内存被分配出去但无法收回。

内存泄漏可能会非常严重,以致于应用程序可用的内存被耗尽,导致程序崩溃。要避免内存泄漏,最好是养成同时使用 newdelete 运算符的习惯,在自由存储空间上动态分配内存,随后便释放它。

 
 

类型别名

C++ 为类型建立别名的方式有两种,一种是使用预处理器,另一种是使用 C++ 的关键字 typedef 来创建别名。

  1. 使用预处理器

    #define BYTE char
    

    预处理器在编译阶段将所有的 char 替换成 BYTE

  2. 使用关键字 typedef

    typedef typeName aliasName;	// 为 typeName 起了个别名 aliasName
    // 例如:
    typedef char byte;
    typedef char* byte_pointer;
    

由于预处理器的局限,例如 #define FLOAT_POINTER float*,将其作用在 FLOAT_POINTER pa, pb; 时,真实情况是 float *pa, pb;。它只会将 pa 定义成指针变量。

但是 typedef 方法不会出现上面的问题,它能够处理更复杂的类型别名。

注意typedef 不会创建新的类型,而是为已有的类型建议一个别名!

 
 

字符函数库 —— cctype

C++ 从 C 语言种继承了一个与字符相关的、非常方便的函数软件包,它可以简化诸如确定字符是否为大写字母、数字、标点符号等工作。在 C 语言中的老式风格为 ctype.h

函数名称返回值
isalnum()如果字符是字母或数字,返回true,否则false
isalpha()如果字符是字母,返回true
iscntrl()如果字符是控制字符,返回true
isdigit()如果字符是数字(0~9),返回true
isgraph()如果字符是除空格之外的打印字符,返回true
islower()如果字符是小写字母,返回true
isprint()如果字符是打印字符(包括空格),返回true
ispunct()如果字符是标点符号,返回true
isspace()如果字符是标准空白字符,如空格、进纸、换行符、回车、水平制表符、垂直制表符,返回true
isupper()如果字符是大写字母,返回true
isxdigit()如果字符是十六进制数字(0~9、a~f、A~F),返回true
tolower()如果字符为大写,返回其小写,否则返回本身
toupper()如果字符为小写,返回其大写,否则返回本身

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

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

相关文章

chatGPT会是银弹吗

chatGP最近火的一塌糊涂&#xff0c;它通过语言生成技术和自然语言处理能力&#xff0c;帮助用户快速解决问题并生成内容。目前&#xff0c;这款工具现在已经拥有超过一亿的活跃用户&#xff0c;并且因其高效率和易用性而受到了广大用户的好评。 不过谷歌可就倒霉了&#xff0c…

Shells:一款功能强大的反向Shell快速生成工具

关于Shells Shells是一款功能强大的反向Shell快速生成工具&#xff0c;该工具由4ndr34z负责开发和维护&#xff0c;可以帮助广大研究人员轻松生成常用的反向Shell。如果你需要一种简单的方法来生成格式化的PowerShell以及Python反向Shell的话&#xff0c;Shells这款工具将是你…

【IPD】敏捷开发与IPD结合的实践培训课程「3月11-12日」

课程名称敏捷开发与 IPD结合的实践 (Agile Development - IPD and Agile Development Practice &#xff09;参加对象企业总工、技术总监、系统架构师、研发经理、测试经理、质量/品质经理、研发测试骨干&#xff0c;以及研发测试技术人员。课程背景软件系统的日益复杂化和用户…

C语言学习笔记-内存管理

这篇将讲解 C 中的动态内存管理。C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。 序号函数和描述1void calloc(int num, int size);在内存中动态地分配 num 个长度为 size 的连续空间&#xff0c;并将每一个字节都初始化为 0。所以…

2023的金三银四,测试员还能找到好工作吗?

按照往年的惯例&#xff0c;春节后复工的 3 月、4 月是人员跳槽最频繁的时候&#xff0c;俗称“金三银四”。然而&#xff0c;市场大环境的影响&#xff0c;很多行业感受到了一丝寒冷的气息。 我们以为受影响比较轻的互联网行业&#xff0c;头上也充满乌云&#xff0c;所谓互联…

ROS2机器人编程简述humble-第四章-BASIC DETECTOR .3

书中程序适用于turtlebot、husky等多种机器人&#xff0c;配置相似都可以用的。支持ROS2版本foxy、humble。基础检测效果如下&#xff1a;由于缺&#xffe5;&#xff0c;所有设备都非常老旧&#xff0c;都是其他实验室淘汰或者拼凑出来的设备。机器人控制笔记本是2010年版本。…

九龙证券|本周5只新股申购,特斯拉、蔚来、理想的供应商来A股了!

据现在组织&#xff0c;2月13日到17日共有5只新股申购&#xff0c;其间上证主板2只&#xff0c;深证主板1只&#xff0c;北交所2只。 2月14日发动打新的深证主板新股多利科技成立于2010年&#xff0c;是一家专心于轿车冲压零部件及相关模具的开发、出产与出售的企业。从2020年…

nodejs版本管理器nvm下载,安装详情

文章目录前言一、NVM下载二、NVM安装三.使用NVM安装nodejs1.NVM常用命令2.安装node3.使用node前言 安装nodejs方式有两种。 第一种&#xff1a;官网下载  通过nodejs官网https://nodejs.org/zh-cn/下载安装 &#xff0c;但有个缺陷&#xff0c;不同版本的nodejs无法顺利的切…

软件测试面试理论(超详细)

【面试理论知识】1、你的测试职业发展是什么? 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自己…

Dubbo中应用级,与接口级配置中心的使用,包括单配置中心与多配置中心

接口级或应用级服务发现 Dubbo3 默认采用 “应用级服务发现 接口级服务发现” 的双注册模式 可以通过配置 dubbo.registry.register-modeinstance/interface/all 来改变注册行为。 instance &#xff1a; 应用级interface &#xff1a; 接口级all &#xff1a;两者都注册&a…

一文详解jvm之-Xms -Xmx -Xmn -Xss -XX:PermSize -XX:MaxPermSize等参数的设置和优化以及如何选择垃圾回收器

文章目录1. 文章引言2. 常见配置汇总2.1 Xmn Xms Xmx Xss的区别2.2 其他常见配置2.3 典型设置举例3. 回收器选择3.1 吞吐量优先的并行收集器3.2 响应时间优先的并发收集器3.3 辅助信息4. 参考文档1. 文章引言 我们经常在tomcat的catalina.bat或者catalina.sh中配置如下参数&am…

亚马逊、速卖通、temu、Cdiscount通过自养号给自己店铺测评补单需要哪些技巧?

亚马逊卖家通过测评平台&#xff0c;获取亚马逊买家的真实服务点评&#xff0c;即亚马逊测评。它既可以让买家更加快速、有效地了解产品&#xff0c;也可以让卖家有机会通过买家的评论去优化产品&#xff0c;以获得更多买家的喜爱。因此&#xff0c;亚马逊测评之于卖家&#xf…

Linux下内存buff/cache占用过多问题解决

在Linux下经常会遇到buff/cache内存占用过多问题&#xff0c; 尤其是使用云主机的时候最严重&#xff0c;由于很多是虚拟内存&#xff0c;因此如果buff/cache占用过大的&#xff0c; free空闲内存就很少&#xff0c;影响使用&#xff1b; 通常内存关系是&#xff1a; 普通机器…

Python 获得摄像头捕捉的图像

Python 获得摄像头捕捉的图像 很多时候&#xff0c;我们都需要通过摄像头捕获图像&#xff0c;以便进行处理&#xff0c;在这里分享的是通过OPEN CV这个库来实现。 OPEN CV的安装和使用 安装很简单&#xff0c;相关文章也很多&#xff0c;注意一点&#xff0c;不要安装最新版…

【Android视频号信息获取①】

*在2019年深圳上班的时候 那时候还是个Java 码农 接触了一下 Xposed.时隔多年 忘记差不多了 用frida先来练练手 新公司又让我研究微信视频号获取个人的视频主页标题列表 * 确定微信版本 不同版本微信hook点不一样。 预想实现方式 用Xposed去请求注册一个中转服务 然后脚本请…

Java——编辑距离

题目链接 leetcode在线oj题——编辑距离 题目描述 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 题目示例 输入&#xff1a;word…

搭建DJI 无人机Onboard SDK ROS开发环境及测试

搭建DJI 无人机Onboard SDK ROS开发环境及测试功能包简介开发环境搭建测试功能包连接设备启动SDK功能包简介 ROS功能包名称&#xff1a;dji_sdk 功能包功能&#xff1a;用于DJI 板载SDK的ROS版本 OSDK 是一个用于开发无人机应用程序的开发工具包&#xff0c;基于OSDK 开发的…

CUDA线程层次一文搞懂|参加CUDA线上训练营

设备术语 Host&#xff1a;CPU 和 内存 (host memory)Device&#xff1a;GPU 和显存 (device memory) CUDA 线程层次 CUDA 线程层次分为&#xff1a; Thread 所有线程执行相同的核函数并行执行 Thread Block 执行在一个 Streaming Multiprocessor &#xff08;SM&#xff09…

Python快速上手系列--异常处理--详解篇

本章所说的就是我们经常遇到的一个问题&#xff0c;报错、异常。我们应该如何处理&#xff0c;让它不影响后面的程序运行。异常首先我们看看一个简单的示例。print(2/0)其结果可想而知&#xff0c;当然是报错了&#xff01;程序被终止了&#xff01;这里会提示用户&#xff0c;…

索引-性能分析-慢查询日志

索引语法 1、创建索引时候 [UNIQUE | FULLTEXT] 关键字是可选的&#xff1b; 1&#xff09;加上 UNIUQE 就是创建唯一索引&#xff08;唯一索引&#xff0c;说明改字段不能出现重复数据&#xff09;&#xff1b; 2&#xff09;加上FULLTEXT 创建的是一个全文索引&#xff1b;…