史上最全C语言static与extern指南

news2024/10/11 9:20:58

在这里插入图片描述

一、引言

在C语言中,staticextern关键字是非常重要的,它们分别用于控制变量的作用域和生命周期,以及变量和函数的定义和声明。理解这些关键字如何工作对于编写清晰、高效的代码至关重要。

二、static关键字

static关键字用于声明变量或函数具有特定的作用域或生命周期。它可以应用于全局变量、局部变量以及函数声明。

2.1 全局变量

当在全局作用域中声明一个变量时,加上static关键字会使该变量仅对该文件可见。

// file1.c
static int globalStatic = 10;  // 只对file1.c可见

// file2.c
int globalNonStatic = 20;  // 对所有文件可见
2.2 局部变量

在函数内部声明为static的关键字会使该变量具有全局生存期,即每次函数调用时其值不会重置。

// 使用static局部变量
#include <stdio.h>

void countVisits() {
    static int visits = 0;  // 静态局部变量
    visits++;
    printf("This function has been called %d times.\n", visits);
}

int main() {
    countVisits();
    countVisits();
    countVisits();
    return 0;
}

输出结果将是:

This function has been called 1 times.
This function has been called 2 times.
This function has been called 3 times.
2.3 函数

函数也可以被声明为static,使其只在定义它的文件中可见。

// static函数
#include <stdio.h>

static void localFunction() {  // 只对当前文件可见
    printf("Called localFunction.\n");
}

void globalFunction() {  // 对所有文件可见
    localFunction();  // 调用localFunction
}

int main() {
    globalFunction();
    return 0;
}

输出结果将是:

Called localFunction.

在这里插入图片描述

三、extern关键字

extern关键字用于声明一个变量或函数是在其他地方定义的。它常用于多个文件共享同一变量或函数的情况。

3.1 全局变量

如果多个文件需要访问同一个全局变量,可以在一个文件中定义该变量,在其他文件中声明为extern

// file1.c
extern int sharedVariable;  // 声明sharedVariable为外部定义

void useSharedVariable() {
    sharedVariable = 100;
}

// file2.c
int sharedVariable = 50;  // 定义sharedVariable

int main() {
    useSharedVariable();  // 调用file1.c中的useSharedVariable函数
    printf("The value of sharedVariable is %d.\n", sharedVariable);
    return 0;
}

输出结果将是:

The value of sharedVariable is 100.
3.2 函数

同样地,extern也可以用来声明函数是在其他文件中定义的。

// file1.c
extern void printMessage();  // 声明printMessage为外部定义

int main() {
    printMessage();  // 调用外部定义的函数
    return 0;
}

// file2.c
void printMessage() {  // 定义printMessage函数
    printf("Hello from file2.c!\n");
}

输出结果将是:

Hello from file2.c!
四、static与extern的区别
4.1 作用域
  • static:限制变量或函数的可见性,使其仅在声明它的文件内可见。
  • extern:声明变量或函数是在其他地方定义的,允许跨文件引用。
4.2 生命周期
  • static:对于局部变量,意味着在整个程序执行期间都存在;对于全局变量,则表示只在定义它的文件内可见。
  • extern:没有改变变量的生命周期,只是声明了一个在别处定义的变量。

在这里插入图片描述

五、综合示例

下面是一个综合示例,展示如何使用staticextern关键字来共享变量和函数。

// shared_var.h
#ifndef SHARED_VAR_H
#define SHARED_VAR_H

/**
 * @file shared_var.h
 * @brief Shared variable declaration.
 *
 * This header file declares a shared variable that can be used across multiple files.
 */

extern int sharedVar;  // 声明sharedVar为外部定义

#endif // SHARED_VAR_H
// shared_var.c
#include "shared_var.h"

/**
 * @file shared_var.c
 * @brief Shared variable definition.
 *
 * This source file defines the shared variable declared in shared_var.h.
 */

int sharedVar = 50;  // 定义sharedVar

void modifySharedVar(int newValue) {
    sharedVar = newValue;
}
// main.c
#include <stdio.h>
#include "shared_var.h"

/**
 * @file main.c
 * @brief Main program.
 *
 * This source file contains the main function and uses the shared variable defined in shared_var.c.
 */

int main() {
    printf("Initial value of sharedVar: %d\n", sharedVar);
    modifySharedVar(100);  // 调用修改sharedVar的函数
    printf("Modified value of sharedVar: %d\n", sharedVar);
    return 0;
}

输出结果将是:

Initial value of sharedVar: 50
Modified value of sharedVar: 100
六、高级话题
6.1 多个定义错误

如果在一个项目中有多个文件定义了相同的全局变量,编译器会报错。为了避免这种情况,可以在定义变量的文件中使用static关键字,或者在其他文件中使用extern关键字来声明该变量。

// file1.c
int globalVar = 50;  // 错误:重复定义

// file2.c
int globalVar = 100;  // 错误:重复定义

// 正确的做法:
// file1.c
static int globalVar = 50;  // 仅在file1.c中可见

// file2.c
extern int globalVar;  // 声明globalVar为外部定义
6.2 强制链接

在某些情况下,可能需要强制链接器包含某个函数或变量。可以使用extern来声明一个函数或变量,然后在某个文件中定义它。

// file1.c
extern void forceLink();  // 声明forceLink为外部定义

int main() {
    forceLink();  // 强制链接器包含forceLink函数
    return 0;
}

// file2.c
void forceLink() {  // 定义forceLink函数
    printf("Function forceLink is linked.\n");
}

输出结果将是:

Function forceLink is linked.

在这里插入图片描述

七、最佳实践
7.1 减少全局变量使用

过多的全局变量会增加代码的复杂性和维护难度。尽可能减少全局变量的使用,并使用局部变量或参数传递。

7.2 适当使用static

合理使用static可以提高代码的安全性和可维护性。例如,使用static局部变量可以保留状态信息,而不会影响全局作用域。

7.3 明确声明extern变量

使用extern声明可以明确指出变量或函数是在其他地方定义的,有助于代码的理解和维护。

八、常见问题解答
8.1 Q: static变量和extern变量有什么区别?

A: static变量仅在声明它的文件中可见,而extern变量则声明了一个在其他地方定义的变量。

8.2 Q: 是否可以在函数内部声明extern变量?

A: 不可以。extern只能用于声明全局变量或函数,不能用于局部变量。

8.3 Q: 如何在多个文件之间共享一个变量?

A: 在一个文件中定义变量,在其他需要访问该变量的文件中使用extern声明即可。

九、深入探讨
9.1 编译与链接过程

在编译阶段,编译器只会关注当前文件的内容。而在链接阶段,链接器会处理来自不同文件的符号引用。使用extern声明可以让编译器知道某个符号将在链接阶段可用。

9.2 作用域链

在C语言中,作用域链是由多个作用域组成的层次结构。局部作用域可以访问其外围的作用域中的变量,但反之则不行。使用static关键字可以限制变量的作用域,而extern则可以扩展变量的作用域。

十、实战演练

下面是一些实战演练题目,帮助巩固对staticextern的理解。

10.1 练习题
10.1.1 练习题1

编写一个程序,包含两个文件,其中一个文件定义一个全局变量,另一个文件声明该变量并使用它。

// file1.c
#include <stdio.h>

int globalVar = 10;  // 定义全局变量

void printGlobalVar() {
    printf("Global variable: %d\n", globalVar);
}

int main() {
    printGlobalVar();
    return 0;
}

// file2.c
extern int globalVar;  // 声明全局变量

void modifyGlobalVar(int newValue) {
    globalVar = newValue;
}

int main() {
    modifyGlobalVar(20);
    printGlobalVar();
    return 0;
}

输出结果将是:

Global variable: 20
10.1.2 练习题2

编写一个程序,使用static关键字来实现一个函数内的计数器,并在多次调用该函数时显示计数结果。

#include <stdio.h>

void countVisits() {
    static int visits = 0;  // 静态局部变量
    visits++;
    printf("This function has been called %d times.\n", visits);
}

int main() {
    countVisits();
    countVisits();
    countVisits();
    return 0;
}

输出结果将是:

This function has been called 1 times.
This function has been called 2 times.
This function has been called 3 times.
十一、总结

通过本文,你应该已经理解了staticextern关键字在C语言中的作用以及如何正确使用它们。合理运用这些关键字可以帮助你写出更安全、更高效的代码。希望本文对你有所帮助!

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

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

相关文章

背靠背 丢包问题分析

存在一个设计需求&#xff0c;原始数据不足满字节有效的&#xff0c;要将数据整体右移对齐后输出。 整体移位后&#xff0c;存在eop 前移一拍情况。 实际分析时&#xff0c;因为是做数据裁剪&#xff0c;不会出现带宽膨胀问题&#xff08;1拍需要多拍出&#xff09;。 最…

关于摩托车一键启动无钥匙进入、智能科技创新

摩托车一键启动无钥匙进入功能 一、工作原理 摩托车的一键启动无钥匙进入功能采用了世界最先进的RFID无线射频技术和最先进的车辆身份编码识别系统&#xff0c;率先应用小型化、小功率射频天线的开发方案&#xff0c;并成功融合了遥控系统和无钥匙系统&#xff0c;沿用了传统…

基于Springboot+Vue的线上一流课程教学辅助系统 (含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 在系统…

01 为什么要学习数据结构与算法

为什么要学习数据结构与算法 一、问题提出 ​ 最早计算机的设计初衷主要用于军事上枪炮的弹道计算和火力表的测试&#xff0c;后来更多的用于科学计算&#xff0c;即数值类的计算&#xff0c;而现在&#xff0c;计算机深入到日常生活的各个方面&#xff0c;其计算的数据早已从…

【论文阅读】Cross Attention Network for Few-shot Classification

用于小样本分类的交叉注意力网络 引用&#xff1a;Hou, Ruibing, et al. “Cross attention network for few-shot classification.” Advances in neural information processing systems 32 (2019). 论文地址&#xff1a;下载地址 论文代码&#xff1a;https://github.com/bl…

畅阅读微信小程序+ssm论文源码调试讲解

2 系统开发环境 2.1微信开发者工具 微信开发者工具现在已经被小程序开发团队开发运行&#xff0c;目前微信开发者工具任然在不断的完善中&#xff0c;在开发小程序时经常要不断的更新。可以使用微信扫码登陆开发者工具&#xff0c;开发者工具将使用这个微信帐号的信息进行小程…

【含文档】基于Springboot+Vue的母婴全程服务管理系统(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…

CSS @规则(At-rules)系列详解___@charset规则使用方法

CSS 规则(At-rules)系列详解 ___charset规则使用方法 本篇目录&#xff1a; 零、时光宝盒 一、charset规则定义和用法 二、CSS charset语法 三、charset 使用方法例子 1、正确使用方法 2、无效的&#xff0c;错误的使用方法 零、时光宝盒 &#xff08;https://blog.csd…

解决 IntelliJ IDEA 运行时 “Command line is too long“ 问题

文章目录 文章标题&#xff1a;解决 IntelliJ IDEA 运行时 "Command line is too long" 问题简介问题描述解决方案代码示例代码示例1&#xff1a;使用JAR Manifest代码示例2&#xff1a;使用Classpath File代码示例3&#xff1a;优化项目依赖 结论进一步的资源 文章标…

【算法】滑动窗口(续)

一、将x减到0的最小操作数 1658. 将 x 减到 0 的最小操作数 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&#xff0c;然后从 x 中减去该元素的值。请注意&#xff0c;需要…

如何使用 Hailuo AI 生成视频

在这个科技飞速发展的时代&#xff0c;人工智能已经成为创意领域的重要工具。Hailuo AI 是一个强大的平台&#xff0c;可以帮助你将文字描述转化为高质量的视频。以下是详细的步骤&#xff0c;教你如何使用 Hailuo AI 生成视频。 第一步&#xff1a;访问 Hailuo AI 网站 首先…

第十四章 Java多线程--阻塞队列--SynchronousQueue

目录 一、SynchronousQueue基础概念 主要特点 使用场景 示例代码 二、SynchronousQueue深入了解 1 SynchronousQueue介绍 2 SynchronousQueue核心属性 3 SynchronousQueue的TransferQueue源码 3.1 QNode源码信息 3.2 transfer方法实现 3.3 tansfer方法流程图 一、Sy…

【C++堆(优先队列)】1834. 单线程 CPU|1797

本文涉及知识点 C堆(优先队列) LeetCode1834. 单线程 CPU 给你一个二维数组 tasks &#xff0c;用于表示 n​​​​​​ 项从 0 到 n - 1 编号的任务。其中 tasks[i] [enqueueTimei, processingTimei] 意味着第 i​​​​​​​​​​ 项任务将会于 enqueueTimei 时进入任务…

QStandardItemModel的role

QStandardItemModel定义了一些标准的角色&#xff0c;而QAbstractItemModel允许自定义角色。以下是一些常见的数据角色&#xff1a;1. **Qt::DisplayRole**&#xff1a;这是最基本的角色&#xff0c;用于显示在视图中的文本。当一个单元格被绘制时&#xff0c;通常会查询这个角…

Go 语言应用开发:从入门到实战

Go 语言应用开发&#xff1a;从入门到实战 引言 Go&#xff08;Golang&#xff09;是由 Google 开发的一种开源编程语言&#xff0c;设计初衷是提高编程效率&#xff0c;尤其是在高并发场景下表现出色。Go 语言以其简洁、易学、高效并发的特性&#xff0c;逐渐成为开发者的首…

<Project-8.1.1 pdf2tx-mm> Python 调用 ChatGPT API 翻译PDF内容 历程心得

原因 用ZhipuAI&#xff0c;测试用的PDF里&#xff0c;有国名西部省穆斯林&#xff0c;翻译结果返回 “系统检测到输入或生成内容可能包含不安全或敏感内容&#xff0c;请您避免输入易产生敏感内容的提 示语&#xff0c;感谢您的配合” 。想过先替换掉省名、民族名等&#xff…

DM8数据库用户和表空间管理

1 说明 DM8用户管理和表空间管理常用的管理命令&#xff0c;包括创建、修改和查看信息操作等。 2 用户管理 2.1 创建用户 创建一个用户lu9up&#xff0c;密码为"admin2024."&#xff0c;未制定表空间&#xff0c;使用默认的表空间main。 SQL> create user lu…

银河麒麟桌面操作系统V10:解决激活时“无法获取硬件信息(错误码#0017)”问题

银河麒麟桌面操作系统V10&#xff1a;解决激活时“无法获取硬件信息&#xff08;错误码#0017&#xff09;”问题 1、问题描述2、问题解决方法步骤一&#xff1a;打开终端步骤二&#xff1a;删除/etc/.kyhwid文件步骤三&#xff1a;重新激活系统总结 &#x1f490;The Begin&…

【Ubuntu】在Ubuntu上安装IDEA

【Ubuntu】在Ubuntu上安装IDEA 零、前言 最近换了Ubuntu系统&#xff0c;但是还得是要写代码&#xff0c;这样就不可避免地用到IDEA&#xff0c;接下来介绍一下如何在Ubuntu上安装IDEA。 壹、下载 这一步应该很容易的&#xff0c;直接打开IDEA的下载页面&#xff0c;点击下…

Python RabbitMQ 入门 pika

Python RabbitMQ 入门 RabbitMQ是实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff08;亦称面向消息的中间件&#xff09;。RabbitMQ服务器是用Erlang语言编写的&#xff0c;而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均…