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

初识C++:指针与引用的异同,inline关键字

大家好,我是小卡皮巴拉

文章目录

目录

一.指针和引用的关系

1.1 概念

1.2 相似点

1.3 不同点

二.inline关键字

2.1 概念

2.2 工作原理

2.3 使用场景

2.4 注意事项

三.nullptr

3.1 引入背景

3.2 语义和类型

3.3 使用场景

兄弟们共勉 !!! 


每篇前言

博客主页:小卡皮巴拉

咱的口号:🌹小比特,大梦想🌹

作者请求:由于博主水平有限,难免会有错误和不准之处,我也非常渴望知道这些错误,恳请大佬们批评斧正。

在上一次的博客初识C++:C++入门基础中,我们学习了引用,这是与C语言中的指针极其类似的语法结构,可以在一定程度上替代指针,下面我们来看看指针和引用有什么区别。

一.指针和引用的关系

1.1 概念

  • 指针指针是一个变量,其值为另一个变量的地址。在 C++ 中,通过使用*操作符来声明一个指针变量。例如,int *p;声明了一个名为p的指针,它可以指向一个int类型的变量。指针可以被重新赋值,让它指向不同的变量。

  • 引用引用是一个别名,它是一个变量的另一个名字。在 C++ 中,通过使用&操作符来声明一个引用。例如,int a = 10; int &r=a;,这里r就是a的引用,ra代表同一个变量,对r的操作就是对a的操作。

1.2 相似点

  • 访问变量内容

    • 指针和引用都可以用来访问所关联变量的内容对于指针,需要使用解引用操作符*来获取指针所指向变量的值。例如,如果p是一个指向int变量的指针,*p就可以获取指针所指向的int值。对于引用,直接使用引用名就可以访问它所关联变量的值,如上述例子中的r,它和a的值相同,使用r就相当于使用a

  • 函数参数传递效率提升方面(部分情况)

    • 在函数参数传递中,指针和引用都可以用来避免不必要的变量拷贝,提高效率。例如,当传递一个大型结构体作为函数参数时,使用指针或者引用可以避免整个结构体的拷贝。如果有一个函数void func(int *p)void func(int &r)当调用这些函数时,传递的是变量的地址或者别名,而不是变量的副本。

1.3 不同点

  • 初始化要求

    • 指针可以在声明后不立即初始化,它可以先定义为一个空指针(在 C++ 中nullptr是更好的表示空指针的方式),例如int *p = nullptr;,然后在后续的代码中再让它指向一个有效的变量。引用必须在声明时就进行初始化,并且一旦初始化后,就不能再绑定到其他变量例如,int a = 10; int &r=a;是正确的,但是不能在之后再将r绑定到其他变量,如r = b(假设b是另一个int变量)这种操作是不允许的。

  • 内存占用和操作灵活性

    • 指针本身占用内存空间,其大小通常取决于系统的寻址位数。例如,在 32 位系统中,指针大小一般是 4 字节,在 64 位系统中,指针大小一般是 8 字节。指针可以进行算术运算,如p++(假设p是一个指针)可以让指针指向下一个内存位置(如果p指向一个数组元素,那么它会指向下一个数组元素)引用在语法上只是一个别名,它本身不占用额外的内存空间(在底层实现上可能会有一些细微差异,但从用户角度可以这样理解),并且不能进行像指针那样的算术运算。

  • 重新赋值特性

    • 指针可以被重新赋值,使其指向不同的变量或者内存位置。例如,int a = 10, b = 20; int *p=&a; p = &b;是合法的,这样p就从指向a变成了指向b引用一旦初始化绑定到一个变量后,就不能被重新赋值去引用其他变量。例如,int a = 10, b = 20; int &r=a; r = b;这里并不是让r引用b,而是把b的值赋给r所引用的变量ar始终引用a

二.inline关键字

2.1 概念

在 C++ 中,inline是一个关键字,用于建议编译器将函数体直接插入到函数调用的地方,而不是像普通函数那样通过函数调用的机制(如栈帧的创建、参数传递、返回地址保存等)来执行函数。这样做的目的主要是为了提高程序的执行效率,减少函数调用的开销。例如:

inline int add(int a, int b) {
    return a + b;
}

这里的add函数被声明为inline函数,编译器可能会将函数体直接插入到调用add函数的地方。

2.2 工作原理

当编译器遇到inline函数调用时,它会尝试在编译阶段将函数的代码直接复制到调用点。这样,在程序执行时,就好像是直接在调用点执行了函数体的代码,而不是进行传统的函数调用过程。例如,如果有以下代码:

int main() {
    int x = 3, y = 5;
    int result = add(x, y);
    return 0;
}

编译器可能会将add函数的代码return a + b;直接替换到result = add(x, y);这个调用位置,就像写成了int result = x + y;一样。不过,这只是一种可能的优化方式,编译器是否真正执行这种优化取决于编译器的实现和优化策略。

2.3 使用场景

频繁调用的小型函数inline函数非常适合那些短小且被频繁调用的函数。例如,简单的数学运算函数(如加法、减法函数)或者获取和设置类成员变量的访问函数。这些函数的代码通常比较简短,将它们内联可以减少函数调用的开销,从而提高程序的性能。

class Rectangle {
private:
    int width;
    int height;
public:
    // 内联的获取函数
    inline int getWidth() const {
        return width;
    }
    // 内联的设置函数
    inline void setWidth(int w) {
        width = w;
    }
};

2.4 注意事项

代码膨胀过度使用inline可能会导致代码膨胀。因为每次函数被调用时,函数体的代码都会被插入到调用点,如果inline函数的代码很长或者被频繁调用,那么最终的可执行文件可能会变得很大。例如,一个有大量代码的inline函数在多个地方被调用,会导致程序中存在很多重复的代码。

编译器的决定权inline只是对编译器的一个建议,编译器并不一定会按照要求将函数内联。编译器会根据自己的优化策略、函数的复杂程度、调用频率等因素来决定是否真正内联一个函数。例如,一个包含复杂循环或者递归的函数,即使被声明为inline,编译器可能也不会将其内联,因为这样可能会导致代码变得更加难以优化或者不符合内联的实际效益。

三.nullptr

3.1 引入背景

在 C++ 早期版本中,使用NULL来表示空指针。NULL通常被定义为((void*)0),这在 C 语言中工作得很好。然而,在 C++ 中存在函数重载的情况,NULL的这种定义可能会导致一些问题。例如,假设有两个函数重载:void func(int)void func(void*),当调用func(NULL)时,编译器可能会产生歧义,因为NULL既可以被解释为整数0(对于int参数的函数),也可以被解释为void*类型的空指针(对于void*参数的函数)。为了解决这个问题,C++ 11 引入了nullptr

3.2 语义和类型

nullptr是一个表示空指针的常量。它的类型是std::nullptr_t,这是一种特殊的类型,能够隐式地转换为任何指针类型,但不能转换为非指针类型(除了boolnullptr转换为bool时为false)。例如:

int* p = nullptr;  // 正确,nullptr可以转换为int*
int i = nullptr;   // 错误,nullptr不能转换为int
if (nullptr) {     // 条件为假,因为nullptr转换为bool为false
    // 不会执行
}

3.3 使用场景

初始化指针变量在声明指针变量时,可以使用nullptr来初始化它,表示这个指针当前不指向任何有效的内存地址。这比使用NULL更加安全和明确,避免了上述提到的函数重载的歧义问题。

class MyClass {
public:
    void* ptr = nullptr;
};

作为函数参数传递空指针:当一个函数的参数是指针类型,并且需要传递一个表示 “没有指向任何东西” 的参数时,可以使用nullptr。例如:

void printString(const char* str) {
    if (str == nullptr) {
        std::cout << "字符串为空" << std::endl;
    } else {
        std::cout << str << std::endl;
    }
}

在指针比较中的应用可以使用nullptr来检查指针是否为空,就像以前使用NULL一样,但是更加符合 C++ 的类型系统。 

兄弟们共勉 !!! 

码字不易,求个三连

抱拳了兄弟们!


http://www.kler.cn/a/405240.html

相关文章:

  • shell编程(8) until循环以及函数基本创建调用
  • sql注入报错分享(mssql+mysql)
  • FileProvider高版本使用,跨进程传输文件
  • 招商蛇口|在低密园林里,开启生活的“任意门”
  • 【vba源码】导入excel批注信息
  • Nacos实现IP动态黑白名单过滤
  • Spring Boot整合Tomcat底层源码分析
  • Jtti:如何知晓服务器的压力上限?具体的步骤和方法
  • Three.js 闪电效果
  • 【2024最新】基于springboot+vue的疫情网课管理系统lw+ppt
  • js批量输入地址获取经纬度
  • 04 —— Webpack打包CSS代码
  • Vue项目开发 formatData 函数有哪些常用的场景?
  • 当你项目服务器磁盘报警
  • 如何利用Python爬虫精准获得1688店铺详情
  • Android 文件分段上传和下载方案
  • 兼顾高性能与低成本,浅析 Apache Doris 异步物化视图原理及典型场景
  • Java Servlet详解:Servlet的生命周期、请求处理与响应发送
  • css使用弹性盒,让每个子元素平均等分父元素的4/1大小
  • AI大模型系列之一:大模型原理科普(深度好文)
  • 「San」监听DOM变化的方法
  • 基于SpringBoot和uniapp开发的医护上门系统上门护理小程序
  • linux增量更新
  • 第2章-PostgreSQL 15安装及登录
  • 遗传算法与深度学习实战(22)——使用Numpy构建神经网络
  • 【人工智能】深入理解PyTorch:从0开始完整教程!全文注解