[C高手编程] const, volatile, register, inline: 修饰符与关键字全面解析

news2024/11/15 1:46:14

在这里插入图片描述

💖💖⚡️⚡️专栏:C高手编程-面试宝典/技术手册/高手进阶⚡️⚡️💖💖
「C高手编程」专栏融合了作者十多年的C语言开发经验,汇集了从基础到进阶的关键知识点,是不可多得的知识宝典。如果你是即将毕业的学生,面临C语言的求职面试,本专栏将帮助你扎实地掌握核心概念,轻松应对笔试与面试;如果你已有两三年的工作经验,专栏中的内容将补充你在实践中可能忽略的新技术和技巧;而对于资深的C语言程序员,这里也将是一本实用的技术备查手册,提供全面的知识回顾与更新。无论处在哪个阶段,「C高手编程」都能助你一臂之力,成为C语言领域的行家里手。

概述

本章深入探讨C语言中的const, volatile, register, 和 inline关键字。这些关键字在控制变量属性、提高代码可读性和性能方面发挥着重要作用。通过本章的学习,读者将能够理解这些关键字的工作原理,并能在实际编程中正确地运用它们。

1. const关键字

1.1 基本概念

const关键字用于声明一个常量或表示一个变量的某些属性不可更改。

1.2 常量声明

1.2.1 声明常量
  • 定义:使用const关键字声明的变量被视为常量,其值在初始化后不能改变。

  • 示例代码:

    const int MAX_SIZE = 100; // 声明整型常量
    

    详细说明

    • MAX_SIZE是一个整型常量,一旦被初始化为100,就不能再次赋值。
    • 常量可以用来定义程序中不变的数值,比如数组的最大尺寸、枚举类型中的值等。
    • 使用const关键字声明常量有助于提高代码的可读性和维护性。
    • 声明常量时必须进行初始化,否则编译器会报错。
    • const常量的值在编译时就可以确定,因此可以被编译器优化,减少运行时的计算开销。
1.2.2 字符串常量
  • 定义:使用const关键字声明字符串常量,可以提高代码的安全性和可维护性。

  • 示例代码:

    const char *greeting = "Hello, World!"; // 声明字符串常量
    

    详细说明

    • greeting是一个指向字符串的指针,该字符串在初始化后不能被修改。
    • 使用const关键字声明字符串常量有助于防止意外修改字符串内容。
    • 字符串常量通常是指向只读内存区域的指针,这有助于保护内存免受意外修改。
    • 由于字符串常量的值不能改变,因此它们可以被编译器优化,减少运行时的内存访问。

1.3 参数传递

1.3.1 作为函数参数
  • 定义:可以将const关键字用于函数参数,以指示该参数不应该被修改。

  • 示例代码:

    void printMessage(const char *msg) {
        printf("%s\n", msg);
    }
    

    详细说明

    • printMessage()函数中,const char *msg表明函数不应该修改msg所指向的字符串。
    • 使用const关键字作为函数参数可以提高代码的安全性,防止意外修改传入的数据。
    • const关键字作为参数类型的一部分,可以作为类型系统的一部分,帮助编译器进行类型检查。
    • 使用const关键字可以提高函数的重用性,因为函数可以接收任何类型的字符串,只要它是只读的。
1.3.2 作为返回值
  • 定义:可以将const关键字用于函数返回值,以指示返回值不应被修改。

  • 示例代码:

    const char *getGreeting() {
        return "Hello, World!";
    }
    

    详细说明

    • getGreeting()函数返回一个字符串常量。
    • 使用const关键字作为函数返回值可以确保调用者不会修改返回的字符串。
    • 返回const类型的值有助于保护内存免受意外修改,并提高函数的健壮性。

1.4 示例代码

#include <stdio.h>

const int MAX_SIZE = 100; // 声明整型常量

void printMessage(const char *msg) {
    printf("%s\n", msg);
}

const char *getGreeting() {
    return "Hello, World!";
}

int main() {
    printMessage(getGreeting());
    return 0;
}

详细说明

  • MAX_SIZE是一个整型常量,用于定义程序中不变的数值。
  • printMessage()函数接受一个字符串常量作为参数,并打印该字符串。
  • getGreeting()函数返回一个字符串常量。
  • 通过这种方式,我们可以确保程序中的关键数据不会被意外修改。

在这里插入图片描述

2. volatile关键字

2.1 基本概念

volatile关键字用于声明一个变量可能在程序运行期间被外部因素改变,因此编译器不能对其做任何优化。

2.2 变量声明

2.2.1 声明易变变量
  • 定义:使用volatile关键字声明的变量被视为可能在任何时候被外部因素(如硬件中断)改变。

  • 示例代码:

    volatile int sensorValue; // 声明易变变量
    

    详细说明

    • sensorValue是一个易变变量,表示其值可能在任何时候被硬件中断改变。
    • 使用volatile关键字声明易变变量可以确保编译器不会对这些变量做任何优化,从而保证程序的正确性。
    • volatile关键字告诉编译器,即使没有明确的赋值操作,该变量也可能发生改变。
    • 这有助于避免编译器对易变变量进行优化,例如缓存其值在寄存器中,导致读取的是旧值。
2.2.2 处理硬件中断
  • 定义:在处理硬件中断时,使用volatile关键字声明的变量可以确保中断服务程序对变量的修改被正确反映。

  • 示例代码:

    volatile int interruptCounter = 0; // 声明易变变量
    
    void ISR() {
        interruptCounter++; // 中断服务程序
    }
    

    详细说明

    • interruptCounter是一个易变变量,表示其值可能在任何时候被中断服务程序改变。
    • 使用volatile关键字声明易变变量可以确保主程序能够正确读取中断服务程序对变量所做的修改。
    • volatile关键字确保编译器不会假设变量的值在中断处理前后保持不变。
    • 这有助于防止编译器对易变变量进行优化,例如缓存其值在寄存器中,导致读取的是旧值。

2.3 示例代码

#include <stdio.h>

volatile int interruptCounter = 0; // 声明易变变量

void ISR() {
    interruptCounter++; // 中断服务程序
}

int main() {
    // 假设这里触发了中断
    printf("Interrupt counter: %d\n", interruptCounter);
    return 0;
}

详细说明

  • interruptCounter是一个易变变量,表示其值可能在任何时候被中断服务程序改变。
  • 使用volatile关键字声明易变变量可以确保主程序能够正确读取中断服务程序对变量所做的修改。
  • 在实际应用中,ISR()函数通常是外部中断处理程序的一部分,由硬件触发。

在这里插入图片描述

3. register关键字

3.1 基本概念

register关键字用于建议编译器将变量存储在寄存器中,以提高性能。

3.2 变量声明

3.2.1 声明寄存器变量
  • 定义:使用register关键字声明的变量被建议存储在寄存器中,以减少访问内存的次数。

  • 示例代码:

    register int counter = 0; // 声明寄存器变量
    

    详细说明

    • counter是一个寄存器变量,编译器会尝试将其存储在寄存器中,以提高性能。
    • 使用register关键字声明变量并不总是有效,因为编译器会根据可用寄存器的数量和其他因素来决定是否遵循此建议。
    • 寄存器变量的使用有助于减少内存访问次数,提高循环等操作的性能。
    • 寄存器变量通常用于频繁访问的简单变量,如计数器、索引等。
3.2.2 限制
  • 定义:使用register关键字声明的变量不能是constvolatile类型,也不能是数组或结构体类型。
  • 详细说明
    • register关键字主要用于提高频繁访问的简单变量的性能。
    • 由于寄存器数量有限,编译器可能会忽略register关键字的建议。
    • 使用register关键字的变量不能是constvolatile类型,因为这些类型的变量需要保证可变性或外部可见性。
    • register关键字不适用于数组或结构体类型,因为这些类型的大小通常超过了单个寄存器所能容纳的大小。

3.3 示例代码

#include <stdio.h>

register int counter = 0; // 声明寄存器变量

void increment() {
    counter++;
}

int main() {
    for (int i = 0; i < 10; i++) {
        increment();
    }
    printf("Counter is %d\n", counter);
    return 0;
}

详细说明

  • counter是一个寄存器变量,编译器会尝试将其存储在寄存器中,以提高性能。
  • 使用register关键字声明变量可以减少内存访问次数,从而提高循环等操作的性能。
  • 在这个例子中,counter变量频繁被访问,因此使用register关键字可以提高性能。

在这里插入图片描述

4. inline关键字

4.1 基本概念

inline关键字用于建议编译器将函数体直接插入到每次调用该函数的位置,以提高性能。

4.2 函数声明

4.2.1 声明内联函数
  • 定义:使用inline关键字声明的函数会被建议编译器将其展开到调用点处。

  • 示例代码:

    inline int square(int x) {
        return x * x;
    }
    

    详细说明

    • square()函数是一个内联函数,表示编译器应该将其函数体直接插入到每次调用该函数的地方。
    • 使用inline关键字声明函数可以减少函数调用的开销,提高性能。
    • 内联函数通常用于简单的函数,特别是那些执行时间短于函数调用开销的函数。
    • inline关键字只是一个建议,编译器可以选择忽略它。
4.2.2 限制
  • 定义inline关键字只是一个建议,编译器可以选择忽略它。
  • 详细说明
    • 编译器可能会根据函数的大小和调用频率来决定是否将其内联。
    • 过大的函数或者过于频繁的内联可能会导致代码膨胀,反而降低性能。
    • 使用inline关键字的函数应该足够简单,以便编译器可以轻松地将其内联。
    • 如果函数太大或调用频率过高,内联可能会导致代码膨胀,增加编译时间和执行时间。

4.3 示例代码

#include <stdio.h>

inline int square(int x) {
    return x * x;
}

int main() {
    int result = square(10);
    printf("Square of 10 is %d\n", result);
    return 0;
}

详细说明

  • square()函数是一个内联函数,表示编译器应该将其函数体直接插入到每次调用该函数的地方。
  • 使用inline关键字声明函数可以减少函数调用的开销,提高性能。
  • 在这个例子中,square()函数足够简单,内联它可以减少函数调用的开销。

在这里插入图片描述

5. 综合使用

在实际编程中,const, volatile, register, 和 inline关键字常常结合使用,以达到特定的效果。

5.1 结合使用

  • 定义:通过结合使用这些关键字,可以在提高代码质量和性能的同时,确保程序的正确性和安全性。

  • 示例代码:

    const int MAX_SIZE = 100; // 声明整型常量
    volatile int sensorValue; // 声明易变变量
    register int counter = 0; // 声明寄存器变量
    inline int square(int x) {
        return x * x;
    }
    

    详细说明

    • MAX_SIZE是一个整型常量,用于定义程序中不变的数值。
    • sensorValue是一个易变变量,表示其值可能在任何时候被硬件中断改变。
    • counter是一个寄存器变量,编译器会尝试将其存储在寄存器中,以提高性能。
    • square()函数是一个内联函数,表示编译器应该将其函数体直接插入到每次调用该函数的地方。
    • 通过这种方式,我们可以确保程序中的关键数据不会被意外修改,并提高程序的性能。

6. 常见陷阱与注意事项

6.1 const关键字

  • 定义:确保在声明const变量时正确初始化,否则会导致编译错误。

  • 解决方案:始终在声明const变量时初始化。

    详细说明

    • const变量必须在声明时初始化,否则编译器会报错。
    • 确保在声明const变量时正确初始化,可以避免编译错误。
    • 尽量在初始化时使用字面量或已知的常量表达式,以确保编译器可以在编译时确定常量的值。

6.2 volatile关键字

  • 定义:确保正确使用volatile关键字,避免不必要的性能损失。

  • 解决方案:只在确实需要时使用volatile关键字。

    详细说明

    • 使用volatile关键字会禁用编译器对相关变量的优化。
    • 应该只在确实需要处理外部变化时使用volatile关键字,以避免不必要的性能损失。
    • 在多线程或中断处理程序中,volatile关键字尤其重要,但在其他情况下,应避免过度使用。

6.3 register关键字

  • 定义:理解register关键字的局限性,不要过度依赖它。

  • 解决方案:使用register关键字时要考虑到其实际效果。

    详细说明

    • register关键字是一个建议而非命令,编译器可能会忽略它。
    • 不要过度依赖register关键字,应该更多地考虑算法优化和数据结构选择。
    • 使用register关键字的变量应该是频繁访问的简单变量,以充分利用其优势。

6.4 inline关键字

  • 定义:理解inline关键字的限制,避免滥用。

  • 解决方案:合理使用inline关键字,考虑函数的大小和调用频率。

    详细说明

    • inline关键字只是一个建议,编译器可能会忽略它。
    • 不要滥用inline关键字,应该根据函数的实际大小和调用频率来决定是否使用。
    • 使用inline关键字的函数应该是足够简单,以避免代码膨胀带来的负面影响。

在这里插入图片描述

7. 性能考量与优化技巧

7.1 使用const

  • 定义:使用const关键字可以提高代码的安全性和可维护性。

  • 理由const关键字可以帮助避免意外修改关键数据,提高代码质量。

    详细说明

    • 使用const关键字声明常量有助于提高代码的安全性,防止意外修改重要数据。
    • 在函数参数和返回值中使用const关键字可以提高代码的安全性,防止意外修改传入的数据。
    • 使用const关键字可以作为类型系统的一部分,帮助编译器进行类型检查。

7.2 使用volatile

  • 定义:使用volatile关键字可以确保编译器正确处理易变变量。

  • 理由volatile关键字可以帮助确保编译器正确处理可能被外部因素改变的变量。

    详细说明

    • 在处理硬件中断和多线程环境中,使用volatile关键字可以确保编译器不会对易变变量做任何优化,从而保证程序的正确性。
    • 使用volatile关键字可以避免由于编译器优化而导致的程序行为错误。
    • 在多线程环境下,volatile关键字可以确保线程之间的可见性,但并不能保证原子性。

7.3 使用register

  • 定义:使用register关键字可以提高频繁访问的简单变量的性能。

  • 理由register关键字可以减少内存访问次数,提高性能。

    详细说明

    • 使用register关键字声明变量可以减少内存访问次数,从而提高循环等操作的性能。
    • 但是,不要过度依赖register关键字,因为编译器可能会忽略它。
    • 在资源受限的环境中,使用register关键字可以更有效地利用有限的寄存器资源。

7.4 使用inline

  • 定义:使用inline关键字可以减少函数调用的开销。

  • 理由inline关键字可以提高性能,减少函数调用的开销。

    详细说明

    • 使用inline关键字声明函数可以减少函数调用的开销,提高性能。
    • 但是,对于过大的函数或者过于频繁的内联,可能会导致代码膨胀,反而降低性能。
    • 在选择使用inline关键字时,应权衡函数的大小和调用频率,以达到最佳的性能平衡。

8. 总结

通过本章的学习,我们深入了解了C语言中const, volatile, register, 和 inline关键字的功能及其对变量属性、代码质量和性能的影响。我们探讨了这些关键字的基本概念、使用方法以及注意事项,并提供了详细的示例代码。此外,我们还讨论了使用这些关键字时需要注意的一些常见陷阱,并提供了一些优化技巧,以帮助开发者编写更高效、更易于维护的代码。

  • const关键字:用于声明常量或表示变量的某些属性不可更改。
  • volatile关键字:用于声明一个变量可能在程序运行期间被外部因素改变。
  • register关键字:用于建议编译器将变量存储在寄存器中。
  • inline关键字:用于建议编译器将函数体直接插入到每次调用该函数的位置。

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

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

相关文章

内存管控:内存池篇

目录 1.内存池的工作原理 2.内存池的优势 3.长时间运行的应用和内存碎片 4.代码实践 4.1核心结构&#xff08;内存池结构&#xff09; 4.2核心函数 4.2.1创建内存池 4.2.2销毁内存池 4.2.3分配内存块 4.2.4归还内存块入池 内存池&#xff08;Memory Pool&#xff09…

CMake导入OpenCV检测人脸

基于级联分类器的人脸检测示例"face_detection.cpp"&#xff1a; #include <opencv2/opencv.hpp>int main(int argc,char *argv[]) {// load face classifiercv::CascadeClassifier faceCascade;faceCascade.load(argv[3]);cv::Mat image cv::imread(argv[1]…

Effective Java 学习笔记--42-44 函数接口及其应用(Lambda与方法引用)

目录 函数接口 标准函数接口 自定义函数接口 函数接口的应用 Lambda表达式 Lambda表达式相比于匿名类的优点 方法引用 函数式接口是只包含了一个抽象方法的接口&#xff0c;这种接口的设计目的是为了支持Lambda表达式为代表的函数输入&#xff0c;在Java中有43个标准的默…

基于SpringBoot的旅游网站系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于JavaSpringBootVueMySQL的旅游网站系统【附源码文档】、…

远程连接Hiveserver2服务

目录 1.修改 core-site.xml 和 hive-site.xml 的配置文件 2.启动HiveServer2服务 3.启动Beeline工具连接Hiveserver2服务 4.利用IDEA工具连接Hiveserver2服务 完成Hive本地模式安装后&#xff0c;可以启动hiveserver2服务进行远程连接和操作Hive。 1.修改 core-site.xml …

ctf Mark loves cat (超详细记录)

分析&#xff1a;首先根据问题名称看不出明显的考点方向&#xff0c;先看了一下源代码&#xff0c;没有flag相关内容 我后面就去尝试sql注入方向&#xff0c;但是试了几个参数发现得到的回显都是一致的&#xff0c;全是主页&#xff0c;显然是做了错误重定向&#xff0c;那我就…

基于SpringBoot+Vue+MySQL的垃圾分类回收管理系统

系统展示 用户界面 管理员界面 系统背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上不可跨域的鸿沟&#xff0c;信息的传播速度…

PhpStudy下载安装使用学习

一、官网下载 官网地址&#xff1a;Windows版phpstudy下载 - 小皮面板(phpstudy)https://old.xp.cn/download.html 【首页】选择Windows版&#xff0c;进行下载 下载完成是一个压缩包的形式&#xff0c;解压得到一个.exe的执行文件&#xff0c;点击执行安装程序&#xff08;注…

016.Python爬虫系列_关于登录和验证码

我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈 入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈 虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈 PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈 Oracle数…

BMP280气压传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 3.传感器数据获取流程 三、程序设计 main.c文件 bmp280.h文件 bmp280.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 BMP280是一款基于博世公司APSM工艺的小封装低功耗数字复合传感器&#xff0c;它可以测…

circuitjs 继电器使用的注意事项及疑惑解答

本篇是关于继电器使用中的一些注意事项, 及疑惑解答. 我们会深入分析下继电器外在行为表现的内在原因, 并针对一些异常情况提出解决方案. 两个输出端点都显示为高电平 在你使用继电器的过程中, 你可能注意到一种令人困惑的现象, 两个输出端有一个明明没有连接到电源, 却也显示…

Docker方式部署ProxySQL和Keepalived组合实现MGR的高可用访问

ProxySQL 代理访问MGR&#xff0c;可以做到故障自动切换主节点&#xff0c;可以自定义访问规则实现读写分离 目录 网络架构一、环境准备二、软件安装1. MGR集群配置2. 创建必要用户2. ProxySQL部署测试3. Keepalived部署高可用4. 测试高可用 网络架构 一、环境准备 三台物理主…

Ubuntu22.04之禁止内核自动更新(二百六十八)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

2516. 每种字符至少取 K 个 滑动窗口 正难则反

给你一个由字符 a、b、c 组成的字符串 s 和一个非负整数 k 。每分钟&#xff0c;你可以选择取走 s 最左侧 还是 最右侧 的那个字符。 你必须取走每种字符 至少 k 个&#xff0c;返回需要的 最少 分钟数&#xff1b;如果无法取到&#xff0c;则返回 -1 。 示例 1&#xff1a; …

专注LabVIEW 做好一件事

在一次LabVIEW的系统改造项目中&#xff0c;遇到一个设备&#xff0c;操作手册居然还是基于DOS系统的。不难看出&#xff0c;这套设备虽然年代久远&#xff0c;但背后的技术积淀极为深厚。事实证明&#xff0c;这套系统在业内享有很高的认可度&#xff0c;由国外团队开发&#…

翻译神器大盘点:哪些软件让语言障碍不再是问题

如果你渴望提升外文阅读能力&#xff0c;却面临外语基础薄弱的挑战&#xff0c;不必过于担心。幸运的是&#xff0c;当前科技为我们提供了诸多便捷的解决方案——翻译工具。比如百度在线翻译这些工具功能强大&#xff0c;极大地拓宽了我们在日常生活与学习中的语言应用边界。 …

PHP轻松创建高效收集问卷调查小程序系统源码

轻松创建&#xff0c;高效收集 —— 问卷调查小程序&#xff0c;你的调研神器&#xff01; 一、告别繁琐&#xff0c;一键开启调研之旅 还在为设计问卷、收集数据而头疼不已吗&#xff1f;现在&#xff0c;有了“问卷调查小程序”&#xff0c;一切都变得轻松简单&#xff01;无…

戏曲文化苑管理系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;操作日志管理&#xff0c;基础数据管理&#xff0c;公告管理&#xff0c;戏曲管理&#xff0c;用户管理&#xff0c;轮播图信息 微信端账号功能包括&#xff1a;系统首页&#…

向改变技术世界的“导师们”致敬

在教师节这个特殊的日子里&#xff0c;我们通常向那些在课堂上辛勤工作的教师表达敬意&#xff0c;但在技术领域&#xff0c;也有一些导师通过他们的思想、发明和贡献&#xff0c;深刻改变了我们的世界。今天&#xff0c;让我们向那些改变技术世界的导师们致敬&#xff0c;他们…

VUCA时代与传统企业数字化转型

VUCA时代是一个具有现代概念的词&#xff0c;它代表了当前社会和经济环境的一种普遍特征。VUCA是Volatility&#xff08;易变性&#xff09;、Uncertainty&#xff08;不确定性&#xff09;、Complexity&#xff08;复杂性&#xff09;和Ambiguity&#xff08;模糊性&#xff0…