C-语言特性相关

左值和右值

左值和右值是C/C++编程语言中的两个重要概念,它们在赋值、引用以及类型转换等方面表现出明显的区别。

定义:
  • 左值:表示存储在计算机内存中的对象,具有持久性和可寻址性。即,左值能够用“取地址&”运算符获得其内存地址,且在表达式结束后依然存在。左值可以出现在赋值语句的左侧,也可以出现在右侧。
  • 右值:与左值相对,右值通常表示临时对象,它们不具有持久性和可寻址性。即,右值不能用“取地址&”运算符获得其内存地址,且在表达式结束后就不再存在。右值只能出现在赋值语句的右侧。
左/右值引用
  • 左值引用:左值引用是对左值的引用,即给左值取别名。左值引用在C++中的内部实现通常是一个常量指针,左值引用可以引用非常量左值和常量左值,但不能直接引用右值。
  • 右值引用:右值引用是C++11中引入的新特性,用于引用右值。它允许程序员延长临时对象的生命周期,并在需要时移动资源而不是复制资源。用 && 表示,右值引用只能绑定到右值上,不能绑定到左值上(除非通过 std::move 强制转换)
转换
  • 左值到右值的隐式转换:在大多数情况下,左值可以隐式地转化为右值。例如,在赋值操作中,左侧的左值会“退化”为右值,以便与右侧的右值进行匹配。
  • 右值到左值的显式转换:右值到左值的显式转化通常是不允许的,因为右值在表达式结束后就不再存在。然而,通过 std::move 可以将左值强制转化为右值引用,从而允许对其进行移动操作。
std::move
  • 类型转换: 并不实际移动任何数据,而是将其参数转换为右值引用,从而允许利用移动语义(如果可用)进行资源的转移,而不是复制。

  • 函数原型std::move() 函数原型:move 函数是将任意类型的左值转为其类型的右值引用。

指针

在C++中,指针是一种非常基础且强大的特性,它允许你直接访问和操作内存地址。指针存储了变量的内存地址,而不是变量的值本身。

  • 大小:sizeof获得
    std::cout << "Size of pointer: " << sizeof(int*) << std::endl; // 输出指针大小  
  • 用法:基本用法包括声明、初始化、解引用(或取值)、指针运算等
//声明和初始化
int a = 10;  
int* ptr = &a; // ptr 是一个指向 int 类型的指针,它存储了变量 a 的地址

//解引用,使用 * 运算符来访问指针所指向的值:
std::cout << *ptr << std::endl; // 输出 10

//指针运算,指针可以进行算术运算,但仅限于加上或减去整数(表示偏移量),以及指针之间的比较。
int arr[5] = {1, 2, 3, 4, 5};  
int* p = arr; // p 指向数组的第一个元素  
std::cout << *(p + 2) << std::endl; // 输出 3,因为 p+2 指向了数组的第三个元素
  • 注意事项:
  1. 空指针:未初始化的指针是未定义的,可能指向任意内存地址。通常将指针初始化为nullptr(C++11及以后)或NULL(C++11之前,但在C++11中已不推荐使用)来避免野指针问题。
  2. 越界访问:指针运算时要小心,确保不要越界访问内存。
  3. 悬挂指针:如果指针指向的对象被删除或释放,但该指针未被设置为nullptr,则该指针成为悬挂指针。尝试通过悬挂指针访问内存是未定义行为。
  4. 内存泄漏:使用动态内存分配(如new)时,要确保在适当的时候释放内存(使用delete),以避免内存泄漏。
  5. 智能指针:C++11及以后版本中引入了智能指针(如std::unique_ptrstd::shared_ptr等),它们可以自动管理动态分配的内存,减少内存泄漏的风险。
  6. 指针和数组:在C++中,数组名在大多数情况下会被视为指向数组首元素的指针。但是,这种用法有一些陷阱,比如数组名本身不是一个可修改的左值。
指针和引用区别

指针和引用的主要区别可以简单归纳如下:

  1. 定义与性质

    • 指针:是一个变量,存储的是另一个变量的内存地址。
    • 引用:是某个变量的别名,与原变量共享同一块内存空间,实质上是同一个东西。
  2. 内存分配

    • 指针:需要分配内存空间来存储地址值。
    • 引用:不需要分配额外的内存空间,它只是一个别名。
  3. 初始化和赋值

    • 指针:可以在定义时不初始化,之后可以指向任何有效的内存地址,包括NULL。
    • 引用:必须在定义时初始化,且之后不能再改变为引用另一个变量。
  4. 多级性

    • 指针:可以有多级,例如指向指针的指针。
    • 引用:只能是一级的,不能有多级引用。
  5. 空值

    • 指针:可以为空(NULL或nullptr)。
    • 引用:不能为NULL,必须始终引用一个有效的对象。
  6. sizeof运算符

    • 指针:使用sizeof返回的是指针本身的大小(通常是4字节或8字节,取决于系统架构)。
    • 引用:使用sizeof返回的是被引用变量的大小。
  7. 运算操作

    • 指针:支持算术运算(如加减)和比较运算。
    • 引用:不支持算术运算,主要用于别名访问。
  8. 函数参数

    • 指针和引用作为函数参数时,都可以改变实参的值,但引用在语法上更为简洁,且使用上更安全。
  9. 安全性

    • 指针:使用时需要更加小心,因为错误的指针操作可能导致程序崩溃或安全问题。
    • 引用:相对更安全,因为它在定义时必须初始化,并且不能改变为引用另一个变量。
常量指针和指针常量

常量指针和指针常量在C++中是两种常见的指针类型,尽管它们都涉及到const关键字,但它们的含义和应用场景存在明显的区别。以下是对这两种指针的详细比较:

本质区别
  • 常量指针:本质上是一个指针,const修饰的是指针指向的内容,表示该指针指向一个“常量”,即指针指向的变量的值不能通过该指针来改变。
  • 指针常量:本质上是一个常量,const修饰的是指针本身,表示该常量是一个指针类型的常量,即指针的值(即它所指向的地址)不能改变。
声明方式
  • 常量指针:通常声明为const 数据类型 *指针变量名,例如const int *p = &a;,这里p是一个指向整型常量的指针,不能通过p来修改a的值,但p可以指向另一个整型变量的地址。
  • 指针常量:通常声明为数据类型 * const 指针变量名,例如int *const q = &a;,这里q是一个指针常量,它的值(即它指向的地址)不能被修改,但可以通过q来修改它所指向的变量的值(如果那个变量不是常量的话)。
应用场景
  • 常量指针:常用于需要保护数据不被意外修改的场景,同时又想通过指针来访问这些数据。

  • 指针常量:常用于需要确保指针始终指向同一个地址的场景,比如某些资源的句柄或引用,一旦初始化后就不应该再指向其他地址。

  • 常量指针示例

    int a = 10;
    const int *p = &a; // p 是常量指针,指向 a
    // *p = 20; // 编译错误,不能通过 p 修改 a 的值
    p = &b; // 正确,p 可以指向另一个变量 b
    
  • 指针常量示例

    int a = 10;
    int *const q = &a; // q 是指针常量,指向 a
    *q = 20; // 正确,可以通过 q 修改 a 的值
    // q = &b; // 编译错误,q 不能指向其他地址
    
函数指针

函数指针是C和C++语言中的一个特性,它允许程序员将函数的地址存储在变量中,并通过这个变量来调用函数。函数指针的概念是高级且强大的,因为它提供了一种机制来动态地调用不同的函数,基于运行时条件。

函数指针的声明

函数指针的声明需要指定函数返回值的类型、函数名(在这里我们使用指针名代替)以及函数的参数列表(包括参数的类型和数量)。但是,在声明函数指针时,我们不需要函数名,而是使用指针名来引用这个函数。

例如,假设我们有一个函数原型如下:

int add(int a, int b);

要声明一个指向这个函数的指针,我们可以这样写:

int (*ptrToAdd)(int, int);

这里,ptrToAdd是一个指针,它指向一个函数,该函数接受两个int类型的参数并返回一个int类型的值。

函数指针的赋值

一旦我们声明了函数指针,就可以将函数的地址赋给它。在C和C++中,函数名就代表了函数的地址,因此我们可以直接将函数名赋给函数指针。

ptrToAdd = add; // 将add函数的地址赋给ptrToAdd
通过函数指针调用函数

有了函数指针之后,我们就可以通过这个函数指针来调用函数了。调用方式是通过解引用函数指针,并像调用普通函数一样传递参数。

int result = ptrToAdd(5, 3); // 等同于调用 add(5, 3)
函数指针的应用

函数指针在C和C++中有很多应用,包括但不限于:

  • 回调函数:在某些API中,你可以将函数指针作为参数传递给另一个函数,这个被传递的函数指针指向的函数会在某个特定事件发生时被调用。
  • 排序函数:例如,在C标准库中的qsort函数,它接受一个比较函数的指针作为参数,这个比较函数用于定义排序的准则。
  • 动态函数表:通过函数指针数组,可以实现基于运行时条件的函数选择,这在实现多态或构建插件系统时非常有用。
  • 事件处理:在图形用户界面(GUI)编程中,事件处理函数通常是通过函数指针来指定的,这样当特定事件(如按钮点击)发生时,相应的函数就会被调用。
值/引用/指针传递

在参数传递中,值传递、引用传递和指针传递是C++(以及C语言)中常见的三种方式,它们各自具有不同的特点和用途。以下是这三种传递方式的详细区别:

值传递(Pass by Value)

定义:值传递时,形参是实参的副本(复制、拷贝)。即函数接收的是实参的一个拷贝,函数体内对形参的任何修改都不会影响到实参。

特点

  • 单向性:数据只能从实参传递到形参,不能反向传递。
  • 独立性:形参和实参是两个独立的变量,它们占据不同的内存空间。
  • 效率:对于大型对象或结构体,值传递可能会导致较高的内存开销和性能损耗,因为需要复制整个对象。

示例

void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    // 这里a和b的交换不会影响实参
}
引用传递(Pass by Reference)

定义:引用传递时,实参的引用(即内存地址)被传递给形参。形参和实参指向同一块内存地址,因此函数体内对形参的修改会直接影响到实参。

特点

  • 双向性:数据可以在实参和形参之间双向传递。
  • 共享性:形参和实参共享同一块内存空间,对形参的修改会反映到实参上。
  • 效率:对于大型对象或结构体,引用传递可以避免不必要的复制,提高效率。

示例

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
    // 这里a和b的交换会影响实参
}
指针传递(Pass by Pointer)

定义:指针传递时,实参的地址(即指针)被传递给形参。形参是一个指针变量,它存储了实参的地址,因此函数体内可以通过解引用形参来修改实参的值。

特点

  • 间接访问:通过指针可以间接访问和修改实参的值。
  • 灵活性:指针传递提供了更高的灵活性,可以动态地修改多个变量的值。
  • 风险:指针操作相对复杂,容易出错,如野指针、空指针解引用等问题。

示例

void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    // 这里通过解引用指针来交换实参的值
}
适用
  • 值传递:适用于不需要修改实参的场景,或者实参是基本数据类型时。
  • 引用传递:适用于需要修改实参的场景,特别是当实参是大型对象或结构体时,可以提高效率。
  • 指针传递:提供了更高的灵活性,但也需要更谨慎的操作,以避免指针相关的错误。
迭代器

迭代器模式(Iterator Pattern)是一种行为型设计模式,它提供了一种方法顺序访问一个容器对象中的各个元素,而又不暴露其内部的细节。

迭代器模式的核心思想是将集合的遍历功能从集合对象中分离出来,形成一个独立的迭代器对象来管理访问集合元素的逻辑。

  • 迭代器为不同数据结构提供统一的访问接口,使得遍历数据变得简单统一,同时支持高效处理大型数据集,通过逐个访问元素节省内存资源。

  • 迭代器不仅限于遍历,还能与算法结合实现复杂数据处理,支持惰性计算,提升代码可读性和可维护性,是现代编程中不可或缺的工具。

野指针和悬空指针

野指针和悬空指针是编程中常见的两种指针问题,它们通常会导致程序出现不可预测的行为和潜在的错误。

野指针(Wild Pointer)

定义
野指针是指向一个已删除的对象或未申请访问受限内存区域的指针。野指针的值是不确定的,可能指向任何位置,包括操作系统不允许访问的内存区域。

危害
访问野指针通常会导致程序崩溃或未定义行为,因为它可能指向任意内存地址,包括操作系统不允许访问的内存区域。

成因

  • 指针变量未初始化:任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的。如果指针变量在创建时没有初始化,它将指向一个不确定的内存地址,从而成为野指针。
  • 指针释放之后未置空:有时指针在free或delete后未赋值NULL,此时指针仍然指向已被释放的内存地址,但该内存可能已经被系统重新分配给其他对象,因此该指针成为野指针。
  • 指针操作超越变量作用域:例如,函数返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放,返回的指针将成为野指针。

预防措施

  • 初始化时置NULL:指针变量在创建时应立即初始化为NULL或有效的内存地址。
  • 释放时置NULL:当指针指向的内存空间释放时,应立即将指针置为NULL,防止产生野指针。
  • 避免返回局部变量的地址:不要从函数中返回局部变量的地址,因为局部变量在函数返回后会被销毁。
悬空指针(Dangling Pointer)

定义
悬空指针是指一个曾经指向有效内存区域,但由于该内存区域已经被释放或变得无效,因此现在指向一个不再可用的内存地址的指针。

危害
尝试通过悬空指针访问内存通常会导致程序崩溃或未定义行为,因为指针指向的内存区域已经不再可用。

成因

  1. 内存释放后继续使用:动态分配的内存被释放(如使用free()函数),但指针仍然指向原来的内存地址。
  2. 函数返回局部变量的地址:与野指针类似,但悬空指针特指在内存被释放后仍然指向该内存地址的情况。
  3. 使用已经释放的资源:例如,文件被关闭后,仍然使用指向该文件的指针。

预防措施

  • 释放内存后置空指针:在使用free()或delete释放动态分配的内存后,立即将指针置为NULL。
  • 避免返回局部变量的地址:同上,不要从函数中返回局部变量的地址。
  • 谨慎使用动态内存分配:尽量减少动态内存分配的使用,特别是在不需要的情况下。如果必须使用,确保正确管理内存的生命周期。
nullptr 与 NULL
  • NULL:预处理变量,是一个宏,它的值是 0,定义在头文件 中,即 #define NULL 0。
  • nullptr:C++ 11 中的关键字,是一种特殊类型的字面值,可以被转换成任意其他类型。
nullptr优势:
  • 有类型,类型是 typdef decltype(nullptr) nullptr_t;,使用 nullptr 提高代码的健壮性。
  • 函数重载:因为 NULL 本质上是 0,在函数调用过程中,若出现函数重载并且传递的实参是 NULL,可能会出现不知和哪一个函数匹配的情况;但是传递实参 nullptr 就不会出现这种情况。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记录通过Cloudflare部署属于自己的docker镜像源

引言 由于最近国内无法正常拉取docker镜像&#xff0c;然而找了几个能用的docker镜像源发现拉取回来的docker镜像不是最新的版本&#xff0c;部署到Cloudflare里Workers 和 Pages&#xff0c;拉取docker 镜像成功&#xff0c;故记录部署过程。 部署服务 登录Cloudflare后&…

鸿蒙开发HarmonyOS NEXT (三) 熟悉ArkTs

一、自定义组件 1、自定义组件 自定义组件&#xff0c;最基础的结构如下&#xff1a; Component struct Header {build() {} } 提取头部标题部分的代码&#xff0c;写成自定义组件。 1、新建ArkTs文件&#xff0c;把Header内容写好。 2、在需要用到的地方&#xff0c;导入…

如视“VR+AI”实力闪耀2024世界人工智能大会

7月4日&#xff0c;2024世界人工智能大会暨人工智能全球治理高级别会议&#xff08;以下简称为“WAIC 2024”&#xff09;在上海盛大开幕&#xff0c;本届大会由外交部、国家发展和改革委员会、教育部等部门共同主办&#xff0c;围绕“以共商促共享 以善治促善智”主题&#xf…

算法力扣刷题 三十一【150. 逆波兰表达式求值】

前言 栈和队列篇。 记录 三十一【150. 逆波兰表达式求值】 一、题目阅读 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意&#xff1a; 有效的算符为 、-、* 和 / 。 每个操作…

初尝PaddleOCR识别图片中的文字

引言 PaddleOCR是一个基于飞桨深度学习框架的OCR工具包&#xff0c;它集成了丰富的文字检测、识别和后处理算法&#xff0c;能够高效、准确地识别出图片中的文字。 说明 OpenVINO.NET是一个由开源开发者sdcb发布的&#xff0c;一个个强大的工具集&#xff0c;通过优化神经网…

科普文:Linux服务器性能调优概叙

概叙 Java web应用性能分析之服务端慢和优化概叙_cpu飙高java-CSDN博客 Java web应用性能分析之【CPU飙升分析概述】_web页面性能分析cpu占满是因为死循环,还是循环过多-CSDN博客 在我们的软件服务中&#xff0c;软件部署的服务器&#xff0c;一般都是linux服务器&#xff0c…

【每天学会一个渗透测试工具】SQLmap安装教程及使用

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 ✨SQLmap简介 Sqlmap是一款开源的渗透测试工具 &#x1f680;下载及安装 下载地址&#xff1a;http://sqlmap.org/ windo…

两个Activity之间切换时UI部分重叠

书籍 《第一行代码 Android》第三版 开发 环境 Android Studio Jellyfish | 2023.3.1 setContentView android studio自动生成的SecondActivity.kt中自动生成的代码中已经绑定了second_layout.xml的布局资源&#xff0c;通过代码&#xff1a;setContentView(R.layout.secon…

windows@资源管理器中的地址栏@访问共享文件夹的各种方法@管理共享文件夹

文章目录 资源管理器中的地址栏可以访问什么访问共享文件夹&#x1f47a;UNC路径资源管理器打开共享文件夹纯命令行方式访问共享文件夹 共享文件夹相关操作查看所有已经共享的文件夹&#x1f47a;停止某个文件的共享 共享文件夹的访问控制补充匿名访问问题&#x1f60a;强制启用…

【Linux】高级IO——五种IO模型和基本概念 ,非阻塞IO,fcntl,实现非阻塞IO,同步通信和异步通信

文章目录 Linux高级IO1. 五种IO模型1.1 阻塞IO1.2 非阻塞IO1.3 信号驱动IO1.4 IO多路转接1.5 异步IO 2. 同步通信和异步通信3. 阻塞和非阻塞 Linux高级IO 1. 五种IO模型 IO是什么&#xff1f; IO是计算机领域中的缩写&#xff0c;指的是输入/输出&#xff08;Input/Output&…

【vue3|第15期】Vue3模板语法入门指南

日期:2024年7月2日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^ 1.01365 = 37.7834;0.99365 = 0.0255 1.02365 = 1377.4083…

上海网站建设如何做

上海是中国最繁华的城市之一&#xff0c;作为全国的经济、文化和科技中心&#xff0c;网站建设在上海变得越来越重要。如何做好上海网站建设&#xff0c;让网站更加吸引人&#xff0c;成为企业和个人宣传自身的重要平台呢&#xff1f; 首先&#xff0c;要有清晰的定位和目标。在…

IT之旅启航:高考后IT专业预习全攻略

✨作者主页&#xff1a; Mr.Zwq✔️个人简介&#xff1a;一个正在努力学技术的Python领域创作者&#xff0c;擅长爬虫&#xff0c;逆向&#xff0c;全栈方向&#xff0c;专注基础和实战分享&#xff0c;欢迎咨询&#xff01; 您的点赞、关注、收藏、评论&#xff0c;是对我最大…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥生成介绍及算法规格】

密钥生成介绍及算法规格 当业务需要使用HUKS生成随机密钥&#xff0c;并由HUKS进行安全保存时&#xff0c;可以调用HUKS的接口生成密钥。 注意&#xff1a; 密钥别名中禁止包含个人数据等敏感信息。 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harm…

Java实现电子围栏的小例子

主要需求是实现一个电子围栏判断的小例子其中包括前端和后端的demo代码 引入对应的依赖库 <!--jts库通常用于几何计算和表示地理空间数据--> <dependency><groupId>org.locationtech.jts</groupId><artifactId>jts-core</artifactId><…

web学习笔记(七十五)

目录 1.小程序修改响应式数据 1.1修改基本数据类型的值 1.2修改复合数据类型的值 2. 发送请求 3.小程序解决跨域问题 1.小程序修改响应式数据 1.1修改基本数据类型的值 在小程序中需要先将data中的数据拿过来并结构&#xff0c;才可以在this.setdata中修改数据&#xf…

2024攻防演练:亚信安全推出MSS/SaaS短期定制服务

随着2024年攻防演练周期延长的消息不断传出&#xff0c;各参与方将面临前所未有的挑战。面对强大的攻击队伍和日益严格的监管压力&#xff0c;防守单位必须提前进行全面而周密的准备和部署。为应对这一形势&#xff0c;亚信安全特别推出了为期三个月的MSS/SaaS短期订阅方案。该…

SpringBoot Task 定时任务

springboot中使用Task定时任务非常简单 springboot 中自带的都有注解不需要引入依赖 第一步&#xff1a;在启动类上添加启用定时任务注解 EnableScheduling //开启任务调度 第二步&#xff1a;创建一个springboot组件用于定时任务管理 package cn.lsy.api.Task;import cn.ls…

【LeetCode】十一、滑动窗口:长度最小的子数组 + 定长子串的元音最大数目

文章目录 1、滑动窗口2、leetcode209&#xff1a;长度最小的子数组3、leetcode1456&#xff1a;定长子串中元音的最大数目 1、滑动窗口 如下&#xff0c;有一个数组&#xff0c;现三个元素为一组&#xff0c;求最大的和&#xff0c;自然可以while循环实现&#xff1a;i 、i1、…

着色器预热?为什么 Flutter 需要?为什么原生 App 不需要?那 Compose 呢?Impeller 呢?

依旧是来自网友的问题&#xff0c;这个问题在一定程度上还是很意思的&#xff0c;因为大家可能会想&#xff0c;Flutter 使用 skia&#xff0c;原生 App 是用 skia &#xff0c;那为什么在 Flutter 上会有着色器预热&#xff08;Shader Warmup&#xff09;这样的说法&#xff1…