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

由浅到深认识C语言(6):变量的存储类型

该文章Github地址:https://github.com/AntonyCheng/c-notes

在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.csdn.net/AntonyCheng/article/details/136555245),该模板集成了最常见的开发组件,同时基于修改配置文件实现组件的装载,除了这些,模板中还有非常丰富的整合示例,同时单体架构也非常适合SpringBoot框架入门,如果觉得有意义或者有帮助,欢迎Star & Issues & PR!

上一章:由浅到深认识C语言(5):函数

5.变量的存储类别

5.1.内存的分区

可执行文件未运行时所占内存:

bss段:全局未初始化数据

data段:全局初始化数据

text段:代码段

可执行文件运行时所占内存:

堆区:使用malloc , calloc , realloc , free动态申请和释放

栈区:局部变量/数组、函数形参以及函数中大于 4B 的返回值

全局区:全局未初始化数据,全局初始化数据,全局变量和静态变量static bss段和data段)

文字常量区:字符串常量、符号常量

代码区:代码段,二进制代码text段)

各个区域的读写性:

可读可写:堆区、栈区、全局区;

只读:文字常量区、代码区;

5.2.变量类型

普通局部变量

定义形式:在 {} (复合语句)里面定义的普通变量;

void test()
{//复合语句int num = 0; //普通变量
}

作用范围:离它最近的大括号之间有效;

void test()
{//复合语句int num1 = 0; //普通局部变量 num1 对外层大括号有效{int num2 = 0; //普通局部变量 num2 对内层大括号有效}printf("num2 = %d",num2); //这里会报错,找不到 num2 这一个变量
}

生命周期:离它最近的大括号有效,离开大括号的局部变量,系统自动回收;

存储区域:栈区;

注意事项

  1. 普通局部变量不初始化,内容不确定;

  2. 普通局部变量如果同名且不报错的情况下,就近原则;

    void test(){int data = 100;{int data = 200;printf("A = %d",data); //这里 A = 200}printf("B = %d",data); //这里 B = 100
    }
    

    但是我们要杜绝同名的情况;

普通全局变量

定义形式:定义在函数外边的变量,称之为全局变量;

int data; //这里就是普通全局变量
void test(){}
int main(int argc,char *argv){}

作用范围:当前的源文件都有效(可加可不加 extern),如果让这个变量在其他源文件有效的话,必须加上 extern;

#include<stdio.h>
extern int num; //由于 num 在调取函数之间,所以此处要像函数一样声明一下;
//上面的这个代码尽量加一个 extern ,保证代码的可读性;
void test01() {printf("test01 中 num = %d\n", num);
}
int num = 100; //这里就是 num 的一个全局变量;
void test02() {printf("test02 中 num = %d\n", num);
}
int main(int argc, char* argv[]){printf("main 中 num = %d\n", num);test01();test02();return 0;
}
//打印结果都为 num = 100;

生命周期:整个进程都有效,程序结束的时候,全局变量才被释放;

存储区域:全局区;

注意事项

  1. 全局变量不初始化,内容为零,原因是不初始化的话就会被放在bss段,该段会自动置零;

  2. 如果全局变量要在其他源文件中使用,必须在所使用的源文件中加 extern 声明;

  3. 如果全局变量和局部变量同名,在大括号语句中优先使用局部变量;

    int num = 100;
    int main(int argc,char *argv){num = 10;printf("num = %d",num);//这里打印出来是 num = 10
    }
    

静态局部变量

定义形式:在大括号中定义,前面必须加 static 修饰,这样的变量叫静态全局变量;

#include<stdio.h>
void test() {static int num;//静态局部变量;return;
}
int main(int argc, char *argv[]) {test();return 0;
}

作用范围:离它最近的大括号之间有效;

#include<stdio.h>
void test() {{static int num;//静态局部变量;}printf("num = %d\n", num);//这里不识别 numreturn;
}
int main(int argc, char *argv[]) {test();return 0;
}

生命周期:整个进程,程序结束的时候静态局部变量才被释放;

我们来比较一下普通局部变量和静态局部变量:

普通局部变量:

#include<stdio.h>
void test() {int num = 10;num++;printf(" %d ", num);return;
}
int main(int argc, char *argv[]) {test();test();test();test();printf("\n");return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

静态局部量:

#include<stdio.h>
void test() {static int num = 10;num++;printf(" %d ", num);return;
}
int main(int argc, char *argv[]) {test();test();test();test();printf("\n");return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

存储区域:全局区;

注意事项

  1. 静态局部变量如果不初始化,内容为零;
  2. 只能被定义一次(重要);

静态全局变量

定义形式:在函数外边定义,同时加上 static 修饰;

#include<stdio.h>
static data = 10;//静态全局变量
void test() {return;
}
int main(int argc, char *argv[]) {return 0;
}

作用范围:当前源文件有效,不能在其他源文件使用;

生命周期:整个进程,当程序结束后,静态全局变量才会被识别

存储区域:全局区;

注意事项

  1. 静态全局变量不初始化,内容为零;
  2. 静态全局变量只在当前源文件有效;

5.3.函数类型

全局函数(普通函数)

void test() {printf("这就是一个全局函数\n");return;
}
  • **特点:**其他源文件可以使用全局函数,但是必须加 extern 声明;

静态函数(局部函数)

static void test() {printf("这就是一个静态函数\n");return;
}
  • **特点:**其他源文件不可以直接使用静态函数,只能在当前源文件使用;
  • **注意:**如果想在其他源文件直接调用静态函数,需要将静态函数封装在全局函数中,同时全局函数和静态函数必须是同一个源文件,这样可以在其他源文件中直接调用全局函数,然后由全局函数直接调用静态函数;

**案例:**计算;

  • fun1.c

    int va = 7;
    int getG(void){int va = 20;return va;
    }
    // va = 7(是); getG = 20;(是)
    
  • fun2.c

    static int va = 18;
    static int getG(void){return va;
    }
    int getO(void){return getG();
    }
    // va = 18(否); getG = 18(否); getO = 18(是);
    
  • main.c

    #include<stdio.h>
    extern int va;
    extern int getG(void);
    extern int getO(void);
    int main(void){printf("va = %d\n",va);				//答案为 7printf("getO = %d\n",getO());		//答案为 18printf("getG = %d\n",getG());		//答案为 20printf("%d\n",va*getO()*getG());	//答案为 2520
    }
    

下一章:由浅到深认识C语言(7):预处理&二进制

相关文章:

  • VS Code安装Live Server插件搭建web网页结合内网穿透实现公网访问
  • 快速高效地数据分析处理:QtiPlot for Mac中文直装版 兼容M
  • 海豚调度系列之:集群部署(Cluster)
  • c语言实现https客户端 源码+详细注释(OpenSSL下载,visual studio编译器环境配置)
  • 【办公类-22-15】周计划系列(5-6)“周计划-06 周计划打印pdf(docx删除内容转PDF)“ (2024年调整版本)
  • PHP修改默认上传文件缓存位置
  • 蓝桥杯算法基础(13):十大排序算法(希尔排序) (快速排序)c语言版
  • LeetCode108 将有序数组转换为二叉搜索树
  • 《计算机视觉中的深度学习》之目标检测算法原理
  • 如何进行Android的SDK开发
  • ssh免密登陆更换目标主机后无法连接
  • 深度学习-2.6在MINST-FASHION上实现神经网络的学习流程
  • idea远程试调jar、远程试调war
  • ArcGIS Pro 和 ArcMap 10个不同
  • 中国传统游戏-幻方-c/c++实现
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 03Go 类型总结
  • C++类的相互关联
  • ECS应用管理最佳实践
  • GraphQL学习过程应该是这样的
  • in typeof instanceof ===这些运算符有什么作用
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • Logstash 参考指南(目录)
  • PHP 的 SAPI 是个什么东西
  • SSH 免密登录
  • 分享几个不错的工具
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 力扣(LeetCode)56
  • 聊聊springcloud的EurekaClientAutoConfiguration
  • 日剧·日综资源集合(建议收藏)
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 学习笔记TF060:图像语音结合,看图说话
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • # 数论-逆元
  • #13 yum、编译安装与sed命令的使用
  • $.each()与$(selector).each()
  • (30)数组元素和与数字和的绝对差
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (ZT)出版业改革:该死的死,该生的生
  • (八)Flask之app.route装饰器函数的参数
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • .NET 将多个程序集合并成单一程序集的 4+3 种方法
  • @Autowired和@Resource的区别
  • @Autowired和@Resource装配
  • @Conditional注解详解
  • @Not - Empty-Null-Blank
  • [3D游戏开发实践] Cocos Cyberpunk 源码解读-高中低端机性能适配策略
  • [C++] sqlite3_get_table 的使用
  • [CentOs7]搭建ftp服务器(2)——添加用户
  • [CLR via C#]11. 事件
  • [codeforces]Checkpoints
  • [CQOI 2010]扑克牌
  • [HarmonyOS]第一课:从简单的页面开始
  • [IE编程] IE8 新增的C++开发接口