网站开发与推广方向/外链图片
目录
一、auto
1.1. 作用
1.2. 特性
1.3. 代码示例
二、register
2.1. 作用
2.2. 特性
2.3. 代码示例
三、static
3.1. 修饰局部变量
3.2. 修饰全局变量
3.3. 修饰函数
四、extern
4.1. 作用
4.2. 特性
4.3. 代码示例
五、volatile
5.1. 作用
5.2. 代码示例
六、总结
接上一篇https://blog.csdn.net/weixin_37800531/article/details/145430862?sharetype=blogdetail&sharerId=145430862&sharerefer=PC&sharesource=weixin_37800531&spm=1011.2480.3001.8118继续分析。
一、auto
在C语言中,auto
关键字用于修饰局部变量,尽管在实际编程中,我们往往省略这个关键字,因为局部变量默认就是自动存储类型(auto
)的。auto
关键字的主要作用是显式地表明变量的存储类型,并帮助程序员更好地理解代码。
1.1. 作用
- 修饰局部变量,表明该变量是自动存储类型的。
- 虽然通常省略,但在某些情况下,显式使用
auto
可以提高代码的可读性。
1.2. 特性
- 自动分配内存:当函数被调用时,
auto
变量会在栈上自动分配内存。 - 自动释放内存:当函数返回时,
auto
变量所占用的内存会自动释放,变量失效。 - 生命周期:
auto
变量的生命周期仅限于定义它的函数或代码块内。
1.3. 代码示例
虽然auto
关键字通常被省略,但以下示例可以展示其用法:
#include <stdio.h> void myFunction() { auto int myAutoVar = 10; // 显式使用 auto 关键字,但通常可以省略 printf("Value of myAutoVar: %d\n", myAutoVar); // myAutoVar 在这里有效,但函数返回后将自动释放内存
} int main() { myFunction(); // printf("Value of myAutoVar: %d\n", myAutoVar); // 错误:myAutoVar 在这里无效 return 0;
}
myAutoVar
是一个 auto
类型的局部变量,它在 myFunction
函数内部被定义并初始化。当 myFunction
被调用时,myAutoVar
会在栈上分配内存。当 myFunction
返回时,myAutoVar
所占用的内存会自动释放,变量失效。因此,在 main
函数中尝试访问 myAutoVar
会导致编译错误。
运行结果:
需要注意的是,由于局部变量默认就是
auto
类型的,所以在实际编程中,我们通常会省略auto
关键字。
二、register
在C语言中,register
关键字用于向编译器提出建议,希望编译器能够将特定的变量存储在CPU的寄存器中,以便提高对该变量的访问速度。寄存器是CPU内部的一种高速存储单元,其访问速度远快于内存。因此,如果某个变量被频繁地读取,且不会被修改,那么将其存储在寄存器中可以显著提高程序的性能。
2.1. 作用
register
关键字的主要作用是向编译器发出建议,希望编译器能够优化变量的存储位置,将其从内存移动到寄存器中。- 然而,需要注意的是,这只是一个建议,编译器并不一定会采纳。编译器会根据自身的优化策略、寄存器的可用性以及变量的使用情况来决定是否将变量存储在寄存器中。
2.2. 特性
-
适用于频繁读取且不会被修改的局部变量:由于寄存器的数量有限,且读写寄存器需要消耗一定的CPU资源,因此
register
关键字最适合用于那些被频繁读取且不会被修改的局部变量。 -
编译器可能会忽略此建议:如前所述,
register
只是一个建议,编译器并不一定会采纳。编译器会根据实际情况来决定是否将变量存储在寄存器中。 -
不能用于全局变量和静态变量:由于全局变量和静态变量在程序的整个生命周期内都有效,且可能会被多个函数访问和修改,因此它们不适合存储在寄存器中。
2.3. 代码示例
下面是一个使用register
关键字的简单示例:
#include <stdio.h> void count_loops(int n) { register int i; // 建议编译器将i存储在寄存器中 for (i = 0; i < n; i++) { // 循环体为空,仅用于演示 } printf("Loop count: %d\n", i); // 此时i的值应为n
} int main() { count_loops(1000000); // 调用函数,传入一个较大的值以演示效果 return 0;
}
使用了register
关键字来建议编译器将循环变量i
存储在寄存器中。然而,需要注意的是,编译器可能会忽略这个建议,并将i
存储在内存中。此外,即使编译器采纳了这个建议,由于寄存器的数量有限,如果程序中使用了大量的register
变量,那么编译器也可能无法将它们全部存储在寄存器中。
运行结果:
在使用
register
关键字时,我们需要保持谨慎,并意识到它只是一个建议,而不是一个强制性的要求。同时,我们还需要通过实际的性能测试来验证编译器是否采纳了我们的建议,并评估其对程序性能的影响。
三、static
在C语言中,static
关键字有多种用途,包括修饰局部变量、全局变量和函数。
3.1. 修饰局部变量
当static
修饰局部变量时,它会延长该变量的生命周期至整个程序运行期间,但变量的作用域仍然保持不变,即只能在定义它的函数或代码块内部访问。意味着,即使函数执行完毕,该变量的值也会保留下来,供下次函数调用时使用。
- 代码示例:
#include <stdio.h> void functionWithStaticVar() { static int count = 0; // 静态局部变量,只在第一次调用时初始化 count++; printf("Count: %d\n", count);
} int main() { functionWithStaticVar(); // 输出:Count: 1 functionWithStaticVar(); // 输出:Count: 2 functionWithStaticVar(); // 输出:Count: 3 return 0;
}
count
是一个静态局部变量。每次调用functionWithStaticVar
函数时,count
的值都会递增,并且在下次函数调用时保留下来。
- 实际运行结果:
3.2. 修饰全局变量
当static
修饰全局变量时,它会限制该变量的作用域,使其只能在定义它的文件内部访问。有助于避免不同文件之间的命名冲突。
- 代码示例:
// file1.c
#include <stdio.h> static int globalVar = 100; // 静态全局变量,只能在file1.c内部访问 void printGlobalVar() { printf("GlobalVar in file1.c: %d\n", globalVar);
} // file2.c
#include <stdio.h> // 尝试访问file1.c中的globalVar会导致编译错误
// extern int globalVar; // 注释掉这行以避免编译错误 // void printGlobalVarFromFile2() {
// printf("GlobalVar in file2.c: %d\n", globalVar); // 这行会导致链接错误
// } int main() { // printGlobalVarFromFile2(); // 注释掉这行以避免编译错误 printGlobalVar(); // 调用file1.c中的函数来打印globalVar return 0;
}
globalVar
是一个静态全局变量,它只能在file1.c
内部访问。如果尝试在file2.c
中访问它,会导致编译或链接错误。
3.3. 修饰函数
当static
修饰函数时,它会使该函数只能在定义它的文件内部使用,防止外部链接。有助于隐藏函数的实现细节,减少命名冲突的可能性。
- 代码示例:
// file1.c
#include <stdio.h> static void internalFunction() { printf("This is an internal function in file1.c\n");
} void externalFunction() { internalFunction(); // 调用内部函数 printf("This is an external function in file1.c\n");
} // file2.c
#include <stdio.h> // 尝试调用file1.c中的internalFunction会导致链接错误
// void callInternalFunction() {
// internalFunction(); // 这行会导致链接错误
// } int main() { externalFunction(); // 调用file1.c中的外部函数 // callInternalFunction(); // 注释掉这行以避免链接错误 return 0;
}
internalFunction
是一个静态函数,它只能在file1.c
内部使用。如果尝试在file2.c
中调用它,会导致链接错误。而externalFunction
是一个外部函数,它可以在其他文件中被调用。
四、extern
在C语言编程中,extern
关键字扮演着至关重要的角色,它允许我们声明在其他文件中定义的变量或函数,从而实现跨文件的资源共享。这是模块化编程的基础,使得我们可以将程序拆分为多个文件,每个文件负责特定的功能或数据结构,然后在需要时通过 extern
声明来访问这些外部定义的资源。
4.1. 作用
extern
关键字的主要作用是声明一个变量或函数是在其他文件中定义的,这样在当前文件中就可以访问到这个变量或函数。它是实现跨文件链接和访问的关键机制。
4.2. 特性
- 跨文件访问:
extern
允许我们访问在其他文件中定义的变量或函数。 - 声明顺序:在使用
extern
声明的变量或函数之前,编译器需要知道它们的存在。因此,extern
声明通常放在文件的开头部分,或者在变量或函数被实际使用之前。 - 模块化编程:
extern
是模块化编程的基础,使得我们可以将程序拆分为多个独立的文件,每个文件都可以定义自己的变量和函数,并通过extern
声明来访问其他文件中的资源。
4.3. 代码示例
下面是一个简单的示例,展示如何使用 extern
关键字来实现跨文件访问变量和函数。
- file1.c
#include <stdio.h> // 定义一个全局变量
int globalVar = 42; // 定义一个函数
void printGlobalVar() { printf("GlobalVar in file1.c: %d\n", globalVar);
}
- file1.h
// 在头文件中使用 extern 来声明 file1.c 中定义的变量和函数
extern int globalVar;
extern void printGlobalVar();
- file2.c
#include <stdio.h>
#include "file1.h" // 包含 file1.c 的头文件以访问其声明的变量和函数 int main() { // 访问 file1.c 中定义的全局变量 printf("Accessing globalVar from file2.c: %d\n", globalVar); // 调用 file1.c 中定义的函数 printGlobalVar(); return 0;
}
file1.c
定义了一个全局变量 globalVar
和一个函数 printGlobalVar()
。然后,在 file1.h
中使用 extern
关键字来声明这些变量和函数,以便在其他文件中访问它们。最后,在 file2.c
中,我们包含了 file1.h
头文件,从而能够访问 file1.c
中定义的变量和函数。
通过这种方式,我们可以将程序拆分为多个文件,每个文件负责特定的功能或数据结构,然后通过 extern
声明和头文件来实现跨文件的资源共享和访问。这是模块化编程的核心思想之一。
五、volatile
volatile
关键字在C/C++等编程语言中用于告诉编译器,某个变量的值可能会在程序的控制流之外被改变。这通常发生在硬件访问、多线程编程或中断服务程序中。使用 volatile
可以防止编译器对该变量进行优化,从而确保每次访问该变量时都能读取其最新的值。
5.1. 作用
-
防止优化:编译器在优化代码时,可能会将变量的值缓存在寄存器中,以减少对内存的访问。如果变量被声明为
volatile
,编译器就不会进行这种优化,而是每次访问该变量时都直接从内存中读取其值。 -
硬件访问:在嵌入式系统编程中,
volatile
常用于访问硬件寄存器的值。这些寄存器的值可能会由硬件本身或其他外部设备改变,因此需要使用volatile
来确保每次都能读取到最新的值。 -
多线程编程:在多线程环境中,一个线程可能会修改另一个线程中的变量。虽然C/C++标准并没有将
volatile
定义为线程之间的同步机制,但在某些平台上,使用volatile
可以防止编译器对共享变量的优化,从而增加线程间通信的可靠性(尽管这不是跨平台或标准的方法,通常应使用同步原语如互斥锁)。 -
中断服务程序:在中断服务程序中,全局变量的值可能会由中断处理程序改变。使用
volatile
可以确保主程序在访问这些变量时能够读取到最新的值。
5.2. 代码示例
示例1:硬件寄存器访问
#include <stdint.h> // 假设有一个硬件寄存器的地址是0x40000000
#define HARDWARE_REGISTER *((volatile uint32_t *)0x40000000) int main() { // 读取硬件寄存器的值 uint32_t value = HARDWARE_REGISTER; // 对寄存器进行写操作 HARDWARE_REGISTER = 0xDEADBEEF; // 再次读取寄存器的值 value = HARDWARE_REGISTER; return 0;
}
HARDWARE_REGISTER
是一个宏,它定义了一个指向硬件寄存器地址的 volatile
指针。确保了每次访问 HARDWARE_REGISTER
时都会直接从硬件寄存器中读取或写入值。
示例2:多线程编程中的共享变量
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> // 注意:这不是跨平台或标准的方法来实现线程间同步
volatile int shared_variable = 0; void *thread_function(void *arg) { // 模拟一些工作 sleep(1); // 修改共享变量的值 shared_variable = 1; return NULL;
} int main() { pthread_t thread; pthread_create(&thread, NULL, thread_function, NULL); // 等待线程完成工作 while (shared_variable == 0) { // 这里可能会进行忙等待,但这不是推荐的做法 // 在实际应用中,应该使用条件变量、信号量等同步原语 } printf("Shared variable has been changed by the thread.\n"); pthread_join(thread, NULL); return 0;
}
shared_variable
是一个 volatile
变量,它在多线程环境中被共享。虽然 volatile
在这里可以防止编译器对 shared_variable
的优化,但它并不能保证线程之间的同步。在实际应用中,应该使用互斥锁、条件变量等同步原语来确保线程之间的正确同步。
volatile
并不提供原子性保证,即它不能保证对volatile
变量的读写操作是原子的。在多线程环境中,即使使用了volatile
,也可能需要额外的同步机制来确保对共享变量的正确访问。
六、总结
C语言共有32个关键字。这些关键字根据作用可以分为以下四类:
- 数据类型关键字(12个):用于声明变量的数据类型。包括char、double、float、int、long、short、signed、unsigned、struct、union、enum、void等。
- 控制语句关键字(12个):用于控制程序的流程。包括for、do、while、break、continue、if、else、goto、switch、case、default、return等。
- 存储类型关键字(4个或5个):用于声明变量的存储类型。常见的包括auto、extern、static、register等,有时也将typedef视为存储类型关键字的一种,尽管其主要功能是为数据类型取别名。
- 其他关键字(3个或4个):包括const(声明只读变量)、sizeof(计算数据类型长度)、volatile(说明变量在程序执行中可被隐含地改变),有时不包括typedef,因其主要功能是数据类型定义。
这些关键字在C语言编程中具有重要的作用,是构成C语言程序的基本元素之一。掌握这些关键字的使用方法和注意事项,对于学习C语言和进行C语言编程至关重要。
相关文章:

【C语言入门】解锁核心关键字的终极奥秘与实战应用(三)
目录 一、auto 1.1. 作用 1.2. 特性 1.3. 代码示例 二、register 2.1. 作用 2.2. 特性 2.3. 代码示例 三、static 3.1. 修饰局部变量 3.2. 修饰全局变量 3.3. 修饰函数 四、extern 4.1. 作用 4.2. 特性 4.3. 代码示例 五、volatile 5.1. 作用 5.2. 代码示例…...

寒假day10
第十天:请写出以下几个数据的类型 整数 a int a的地址 int* 存放a的数组b …...

本地部署与使用SenseVoice语音大模型简析
前言 SenseVoice 是一种语音基础模型,具有多种语音理解功能,包括自动语音识别 (ASR)、口语识别 (LID)、语音情感识别 (SER) 和音频事件检测 (AED)。本博客将指导您安装和使用 SenseVoice 模型,使其尽可能方便用户使用。 Github 仓库链接: ht…...

Kafka SASL/SCRAM介绍
文章目录 Kafka SASL/SCRAM介绍1. SASL/SCRAM 认证机制2. SASL/SCRAM 认证工作原理2.1 SCRAM 认证原理2.1.1 密码存储和加盐2.1.2 SCRAM 认证流程 2.2 SCRAM 认证的关键算法2.3 SCRAM 密码存储2.4 SCRAM 密码管理 3. 配置和使用 Kafka SASL/SCRAM3.1 Kafka 服务器端配置3.2 创建…...

中间件漏洞之CVE-2024-53677
目录 什么是struts?CVE-2024-53677简介影响版本复现环境搭建漏洞利用修复 什么是struts? 在早期的 Java Web 开发中,代码往往混乱不堪,难以维护和扩展。比如,一个简单的用户登录功能,可能在不同的 Java 类…...

pytorch基于 Transformer 预训练模型的方法实现词嵌入(tiansz/bert-base-chinese)
以下是一个完整的词嵌入(Word Embedding)示例代码,使用 modelscope 下载 tiansz/bert-base-chinese 模型,并通过 transformers 加载模型,获取中文句子的词嵌入。 from modelscope.hub.snapshot_download import snaps…...

Windows电脑本地部署运行DeepSeek R1大模型(基于Ollama和Chatbox)
文章目录 一、环境准备二、安装Ollama2.1 访问Ollama官方网站2.2 下载适用于Windows的安装包2.3 安装Ollama安装包2.4 指定Ollama安装目录2.5 指定Ollama的大模型的存储目录 三、选择DeepSeek R1模型四、下载并运行DeepSeek R1模型五、常见问题解答六、使用Chatbox进行交互6.1 …...

区间覆盖问题
文章目录 1. 题面2. 简单分析3. 代码解答4. TLE的2点可能 1. 题面 给定 N N N个区间 [ a i , b i ] [a_i,b_i] [ai,bi] 以及一个区间 [ s , t ] [s,t] [s,t],请你选择尽量少的区间,将指定区间完全覆盖。 输出最少区间数,如果无法完全…...

【LLM-agent】(task2)用llama-index搭建AI Agent
note LlamaIndex 实现 Agent 需要导入 ReActAgent 和 Function Tool,循环执行:推理、行动、观察、优化推理、重复进行。可以在 arize_phoenix 中看到 agent 的具体提示词,工具被装换成了提示词ReActAgent 使得业务自动向代码转换成为可能&am…...

SpringAI 人工智能
随着 AI 技术的不断发展,越来越多的企业开始将 AI 模型集成到其业务系统中,从而提升系统的智能化水平、自动化程度和用户体验。在此背景下,Spring AI 作为一个企业级 AI 框架,提供了丰富的工具和机制,可以帮助开发者将…...

【axios二次封装】
axios二次封装 安装封装使用 安装 pnpm add axios封装 // 进行axios二次封装:使用请求与响应拦截器 import axios from axios import { ElMessage } from element-plus//创建axios实例 const request axios.create({baseURL: import.meta.env.VITE_APP_BASE_API,…...

P7497 四方喝彩 Solution
Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1,a2,⋯,an),有 m m m 个操作,分四种: add ( l , r , v ) \operatorname{add}(l,r,v) add(l,r,v):对于所有 i ∈ [ l , r ] i \in [l,r…...

深入剖析 Bitmap 数据结构:原理、应用与优化策略
深入理解 Bitmap 数据结构 一、引言 在计算机科学领域,数据的高效存储和快速处理一直是核心问题。随着数据量的不断增长,如何用最少的空间和最快的速度来表示和操作数据变得至关重要。Bitmap(位图)作为一种简洁而强大的数据结构…...

bypass hcaptcha、hcaptcha逆向
可以过steam,已支持并发,欢迎询问! 有事危,ProfessorLuoMing...

WebForms DataList 深入解析
WebForms DataList 深入解析 引言 在Web开发领域,控件是构建用户界面(UI)的核心组件。ASP.NET WebForms框架提供了丰富的控件,其中DataList控件是一个灵活且强大的数据绑定控件。本文将深入探讨WebForms DataList控件的功能、用法以及在实际开发中的应用。 DataList控件…...

C# List 列表综合运用实例⁓Hypak原始数据处理编程小结
C# List 列表综合运用实例⁓Hypak原始数据处理编程小结 1、一个数组解决很麻烦引出的问题1.1、RAW 文件尾部数据如下:1.2、自定义标头 ADD 或 DEL 的数据结构如下: 2、程序 C# 源代码的编写和剖析2.1、使用 ref 关键字,通过引用将参数传递,以…...

【C++基础】字符串/字符读取函数解析
最近在学C以及STL,打个基础 参考: c中的char[] ,char* ,string三种字符串变量转化的兼容原则 c读取字符串和字符的6种函数 字符串结构 首先明确三种字符串结构的兼容关系:string>char*>char [] string最灵活,内置增删查改…...

大模型-CLIP 详细介绍
CLIP简介 CLIP(Contrastive Language–Image Pre-training)是由OpenAI在2021年提出的一种多模态机器学习模型。它旨在通过大量的文本-图像对进行训练,从而学会理解图像内容,并能将这些内容与相应的自然语言描述相匹配。CLIP的核心…...

1.4 Go 数组
一、数组 1、简介 数组是切片的基础 数组是一个固定长度、由相同类型元素组成的集合。在 Go 语言中,数组的长度是类型的一部分,因此 [5]int 和 [10]int 是两种不同的类型。数组的大小在声明时确定,且不可更改。 简单来说,数组…...

WebSocket——环境搭建与多环境配置
一、前言:为什么要使用多环境配置? 在开发过程中,我们通常会遇到多个不同的环境,比如开发环境(Dev)、测试环境(Test)、生产环境(Prod)等。每个环境的配置和需…...

三、递推关系与母函数,《组合数学(第4版)》卢开澄 卢华明
文章目录 一、似函数、非函数1.1 母函数1.2 母函数的简单应用1.3 整数拆分1.4 Ferrers 图像1.5 母函数能做什么1.6 递推关系1.6.1 Hanoi 问题1.6.2 偶数个5怎么算 1.7 Fibonacci 序列1.7.1 Fibonacci 的奇妙性质1.7.2 Fibonacci 恒等式1.7.3 Fibonacci 的直接表达式1.7.4 Fibon…...

线程互斥同步
前言: 简单回顾一下上文所学,上文我们最重要核心的工作就是介绍了我们线程自己的LWP和tid究竟是个什么,总结一句话,就是tid是用户视角下所认为的概念,因为在Linux系统中,从来没有线程这一说法,…...

DeepSeek R1 AI 论文翻译
摘要 原文地址: DeepSeek R1 AI 论文翻译 我们介绍了我们的第一代推理模型,DeepSeek-R1-Zero 和 DeepSeek-R1。 DeepSeek-R1-Zero 是一个通过大规模强化学习(RL)训练的模型,且在此过程中未使用监督微调(…...

如何计算态势感知率?
态势感知率(Situational Awareness Rate)的计算通常需要结合具体应用场景和定义目标,通常涉及对感知、理解、预测三个层次的量化分析。不同领域(如网络安全、军事、工业控制等)可能有不同的量化方式。通用思路和常见方…...

二、CSS笔记
(一)css概述 1、定义 CSS是Cascading Style Sheets的简称,中文称为层叠样式表,用来控制网页数据的表现,可以使网页的表现与数据内容分离。 2、要点 怎么找到标签怎么操作标签对象(element) 3、css的四种引入方式 3.1 行内式 在标签的style属性中设定CSS样式。这种方…...

Alibaba开发规范_异常日志之日志规约:最佳实践与常见陷阱
文章目录 引言1. 使用SLF4J日志门面规则解释代码示例正例反例 2. 日志文件的保存时间规则解释 3. 日志文件的命名规范规则解释代码示例正例反例 4. 使用占位符进行日志拼接规则解释代码示例正例反例 5. 日志级别的开关判断规则解释代码示例正例反例 6. 避免重复打印日志规则解释…...

使用istio实现权重路由
istio概述 **概述:**Istio 是一个开源的 服务网格(Service Mesh)解决方案,主要用于管理、保护和监控微服务架构中的服务通信。它为微服务提供了基础设施层的控制功能,不需要更改应用程序的代码,从而解决服…...

M. Triangle Construction
题目链接:Problem - 1906M - Codeforces 题目大意:给一个 n 边形, 每一个边上有a[ i ] 个点, 在此多边形上求可以连的三角形有多少个, 每个点只能用一次。 输入: 第一行是一个整数 N ( 3 ≤ N ≤ 200000…...

每天学点小知识之设计模式的艺术-策略模式
行为型模式的名称、定义、学习难度和使用频率如下表所示: 1.如何理解模板方法模式 模板方法模式是结构最简单的行为型设计模式,在其结构中只存在父类与子类之间的继承关系。通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基…...

机试题——到邻国目标城市的最短距离
题目描述 A国与B国是相邻的两个国家,每个国家都有很多城市。国家内部有很多连接城市的公路,国家之间也有很多跨国公路,连接两个国家的边界城市。两个国家一共有N个城市,编号从1到N,一共有M条公路,包括国内…...