C primer plus学习笔记 —— 13、存储类别、内存管理

news2024/11/13 9:29:02

文章目录

    • 存储类别
      • 定义、声明和初始化的区别
      • 作用域
        • 翻译单元和文件
      • 链接属性
      • 存储期
      • 存储类别
        • 多文件共享全局变量
      • 函数的存储类别
      • 存储类别的选择
    • 分配内存(malloc、free)
      • malloc和calloc
        • 创建数组方式
        • free的重要性
      • 举例

存储类别

int a = 1;
int *p = &a;
int ranks[10];

首先我们定义一下对象,他是指一块内存。他可以存储一个或者多个值。
我们可以看到,变量名a是一个标识符,表示一个变量,他是一个对象。
而p这个标识符,他指定了一个存储地址的对象。
ranks声明了一个可容纳10个元素的对象。该数组每个元素也是一个对象。

不同的存储类别具有三种特性,存储期,作用域,链接。

定义、声明和初始化的区别

一般的情况下,我们把建立空间的声明称之为定义,而把不需要建立存储空间的声明称之为声明。
变量的初始化,就很简单了,为变量声明或者定义之后,赋上初值的过程,就是变量的初始化。
在这里插入图片描述
tern被声明了两次,
第一次声明为变量预留了存储空间构成了定义。称为定义式声明。
第二次声明只是使用之前的变量,而没有创建空间所以仅仅是声明。

定义和初始化的区别:
int a; 只是定义并没有付初始值,所以不是初始化。
int a = 4 既是定义又是初始化。
extern int a; 仅仅是声明。
extern关键字只能做声明,不能定义。

当然定义和初始化也可以分开

int b; //定义变量
b = 2//初始化变量

作用域

C变量作用域:表示变量的作用范围,空间维度
有四种:

  1. 块作用域:局部变量都具有块作用域
  2. 函数作用域:仅用于goto语句,一般很少。
  3. 函数原型作用域:形参,形参定义到函数结束。
  4. 文件作用域:直接定义在源文件中的变量,具有文件作用域。所以具有文件作用域的变量称为全局变量

翻译单元和文件

编译器会把源代码文件和所有包含的的头文件都看成是一个包含信息的单独文件,这个文件被称为一个翻译单元。描述一个文件作用域的变量时,他实际的可见范围是整个翻译单元。
所以一个翻译单元是一个源文件和他所有包含的所有头文件。

链接属性

C变量有三种链接属性:

  1. 外部链接
  2. 内部链接
  3. 无链接

局部变量都是无链接的属性。只有全局变量有外部链接和内部链接两种属性。
我们现在看一下全局变量:
在这里插入图片描述

变量具有外部链接属性:他可以在文件外部使用,也就是多文件程序中使用,也叫全局作用域或者程序作用域。
内部链接属性:static修饰的,只在本文件生效,简称文件作用域。

存储期

存储期顾名思义是是变量存在的时间维度。而作用域表示空间维度。这里注意区分。

举个简单的例子就是函数的局部变量分配的int a = 0;他会随着函数销毁自动释放。而malloc分配的变量会等到手动free的时候才会释放。

C变量有四种存储期:

  1. 静态存储期:程序运行期一直存在。全局变量具有静态存储期,不管是全局作用域还是文件作用域的变量**(带不带static的变量)都在程序运行时一直存在**。static修饰的局部变量也有静态存储期,在块销毁的时候他还存在,但是只有在块内才能访问到他。
  2. 线程存储期:具有线程存储期的变量,以_Thread_local声明的变量,存在于整个线程的生命周期中。
  3. 自动存储期:块作用域的局部变量,在块作用域内,块结束会自动释放变量内存。
  4. 动态分配存储期:malloc,和free,用户自己管理。

存储类别

所以下面我们主要区别,一个变量有五种存储类别,我们在声明时变量会属于其中一种。
在这里插入图片描述

  1. 自动存储期:程序运行到块内该变量声明时存在,退出时消失,占用的内存也被释放可另作他用。自动变量声明时就占用内存,而如果没有初始化,那么他的值为该块内存之前使用的值,所以是非预期值,记得在使用前一定要初始化或者赋值。
  2. 寄存器存储:这样的变量使用register关键字,直接存储到寄存器中,但是无法获取该变量的地址。但是编译器可能并不会直接分配真正的寄存器给你,那他和普通变量就一样了,但是还是不能使用该变量的地址。
  3. 块内static:该变量在每次函数调用的时候会记录该值。该变量在程序运行时一直存在内存中。函数的形参中不允许使用static
  4. 文件作用域的static变量:在这里插入图片描述该变量作用范围只在翻译单元中,但是生命周期是整个程序。
  5. 全局作用域变量:也就是不带static的变量在这里插入图片描述为了指出该变量是外部变量,
    在同一个翻译单元中:我们在使用外部变量可以在前面加上extern关键字,作为声明。
    如果不加这个extern 关键字,又声明了一次,那就表示我又创建了一个局部变量在函数中,这会隐藏外部变量。
    当然我也可以不声明,在同一个翻译单元中直接使用即可。
    如果在不同翻译单元中:也可加extern作为声明引用此外部变量。因为外部变量作用范围是整个程序。
    外部变量默认被初始化为0.

多文件共享全局变量

所以多个翻译单元中,使用全局变量作为共享,则在其中一个翻译单元定义一个变量,在其他翻译单元中进行使用该变量之前先使用extern声明该变量,如果不使用extern声明,则不能使用该变量,因为找不到。
extern关键字表示声明的变量在别处

函数的存储类别

有两种:外部和内部,外部指全局,内部指翻译单元内。
在这里插入图片描述
首先第一个表示声明了该函数,并且该函数默认为外部,那么其他所有翻译单元都可以使用。其实这里省略了extern关键字,所以和第三个函数是一样的。
第三个函数显示声明了extern,通常做法这样写表明该函数声明在了其他翻译单元中。但是一般做法是如果要使用某个函数,就把他的头文件包含进来,也就是在当前翻译单元声明了该函数,一起编译就可以直接使用了。
static修饰的函数,只能用于当前翻译单元,

存储类别的选择

默认的而且优先都是选择自动存储类别。
非必要不要声明和定义全局变量,因为他可能被其他人修改而造成你无法正常使用。
除非一种const修饰的全局变量,他在初始化后就不会被修改。

分配内存(malloc、free)

上面的内存分配与回收都是系统帮我们做了,而我们不用管。如果我们想自己更灵活的分配和回收内存可以使用malloc和free。
需要引入#include <stdlib.h>库头文件,内存分配函数在这里面。

malloc和calloc

malloc(size)
给malloc一个参数,表示要申请的内存大小,所需的byte数。
malloc会找到一块空闲内存,返回内存块的首字节地址,所以可以把该地址赋值给一个指针,后面可以使用该指针访问这块内存。
返回值类型是一个void类型的指针,该类型相当于一个通用指针,该指针通常被强转为一个匹配的类型并不需要考虑类型不匹配的问题,并且我们应该坚持这样做来保证代码的可读性,C中虽然不要求强制转换,但是C++中必须使用,那么我们坚持这样做来保证可移植性。

如果malloc分配内存失败,则他会返回一个空指针nullptr,我们可以通过是否为空指针来判断是否成功分配了内存。

double *pt = (double *) malloc (30 * sizeof(double));

这段代码为30个double类型的值申请了一块内存,并设置pt指向该内存首元素地址。
回忆一下,数组名为该数组的首元素地址。所以pt指向该块内存的首元素,那么我们可以用数组名来使用它。可以使用数组名来表示指针,也可以使用指针来表示数组名
我们可以使用pt[0]来访问该块的首元素,pt[1]来访问第二个元素。

long* num;
num = (long *)calloc(100, sizeof(long));

calloc第一个参数是存储单元数量,第二个参数是存储单元大小。
他的返回值也是void* 需要强转成需要的类型。
他还有一个特性是会把分配后的所有值都初始化为0.

创建数组方式

所以现在我们有两种方式创建数组
第一种:double item[30];
第二种:

int n = 6; 
double *pt = (double *) malloc(n * sizeof(double));

第一种我们必须指定一个常量作为数组的大小。
而第二种的优势是我们可以创建一个变长数组。
使用malloc在运行时才确定内存数组的大小。

#include <stdio.h>
#include <stdlib.h> /* for malloc(), free() */

int main(void)
{
    double * ptd;
    int max;
    int number;
    int i = 0;
    
    max = 6;
    ptd = (double *) malloc(max * sizeof (double)); //分配动态数组
    if (ptd == NULL) //如果分配失败则退出
    {
        puts("Memory allocation failed. Goodbye.");
        exit(EXIT_FAILURE);//异常退出程序
    }
    /* ptd now points to an array of max elements */
    puts("Enter the values (q to quit):");
    while (i < max && scanf("%lf", &ptd[i]) == 1)
        ++i;
    printf("Here are your %d entries:\n", number = i);
    for (i = 0; i < number; i++)
    {
        printf("%7.2f ", ptd[i]); //使用指针也即数组名访问元素
        if (i % 7 == 6)
            putchar('\n');
    }
    if (i % 7 != 0)
        putchar('\n');
    puts("Done.");
    free(ptd);//释放内存
    
    return 0;
}

free的重要性

我们使用free函数来释放malloc分配的内存,free接受参数为malloc返回的指针。
当不断分配内存而忘记释放,可能会耗尽内存,这类问题称为内存泄漏。

举例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int static_store = 30;
const char * pcg = "String Literal";
int main()
{
    int auto_store = 40;
    char auto_string[] = "Auto char Array";
    int * pi;
    char * pcl;
    
    pi = (int *) malloc(sizeof(int));
    *pi = 35;
    pcl = (char *) malloc(strlen("Dynamic String") + 1);
    strcpy(pcl, "Dynamic String");
    
    printf("static_store: %d at %p\n", static_store, &static_store);
    printf("  auto_store: %d at %p\n", auto_store, &auto_store);
    printf("         *pi: %d at %p\n", *pi, pi);
    printf("  %s at %p\n", pcg, pcg);
    printf(" %s at %p\n", auto_string, auto_string);
    printf("  %s at %p\n", pcl, pcl);
    printf("   %s at %p\n", "Quoted String", "Quoted String");
    free(pi);
    free(pcl);
    
    return 0;
}
// static_store: 30 at 0x601048     静态区域
//   auto_store: 40 at 0x7fff5f25f8dc   自动区域
//          *pi: 35 at 0xeea010     堆
//   String Literal at 0x4007a0     静态区域
//  Auto char Array at 0x7fff5f25f8c0   自动区域
//   Dynamic String at 0xeea030     堆
//    Quoted String at 0x40080e     静态区域

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

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

相关文章

【Stm32野火】:野火STM32F103指南者开发板烧写官方示例程序LCD无法点亮?LCD示例程序无法使用?

项目场景&#xff1a; 大家好&#xff0c;最近在使用野火STM32F103指南者开发板的时候发现官方的示例程序LCD驱动代码居然无法直接驱动LCD点亮&#xff0c;这让我百思不得其解&#xff0c;以下就是我的踩坑填坑的过程&#xff0c;希望对大家有所帮助。 野火官方资料下载文档链接…

systemd介绍

systemd是一个 Linux 系统基础组件的集合&#xff0c;提供了一个系统和服务管理器&#xff0c;运行为 PID 1 并负责启动其它程序。功能包括&#xff1a;支持并行化任务&#xff1b;同时采用 socket 式与 D-Bus 总线式激活服务&#xff1b;按需启动守护进程&#xff08;daemon&a…

与Oracle不一样的union

与Oracle不一样的union一、引言二、实验探寻union2.1 再现DM8案例2.2 再现Oracle案例2.3 实验结论一、引言 前三日&#xff0c;同事call我聊发文查询优化排序问题&#xff0c;当时联想到union自身的特性&#xff08;合并去重&#xff0c;默认排序输出结果集&#xff09;&#…

(考研湖科大教书匠计算机网络)第一章概述-第五节2:计算机网络体系结构之OSI参考模型和TCPIP参考模型

文章目录一&#xff1a;OSI参考模型&#xff08;1&#xff09;应用层&#xff08;Application Layer&#xff09;&#xff08;2&#xff09;表示层&#xff08;Presentation Layer&#xff09;&#xff08;3&#xff09;会话层&#xff08;Session Layer&#xff09;&#xff0…

STC32G 单片机系列通用定时器的用法及编程

STC32G单片机与STC15系列单片机一样有T0~T4共5个通用定时器。其功能大致相同&#xff0c;与STC15系列单片机定时器不同的是STC32G单片机的定时器每个都多了一个8位预分频器&#xff0c;如下&#xff1a;这样定时器可作为一个24位定时器使用&#xff0c;做计数器使用与分频器就没…

【Flink】浅谈Flink背压问题(1)

概述 在多线程的情况下有一个典型的模&#xff0c;型生产者消费者模型&#xff0c;该模型主要由生产者、消费者和一个大小固定的队列组成。生产者向队列发送数据&#xff0c;消费者从队列中取出数据并处理。 针对上述模型&#xff0c;如果队列属于有限长度&#xff0c;当消费者…

UE5执行Python脚本插件

1.启用UE5的Python脚本编辑器&#xff1a; 在Edit里面找到Plugins&#xff0c;然后打开插件管理器&#xff0c;搜索Python,找到 Python Editor Script Plugin并启用它。该插件也可能会自动启用&#xff08;至少我的UE5是这样的&#xff09;&#xff0c;如果已经自动启用&#…

python机器学习(一)算法学习的步骤、机器学习的应用及流程(获取数据、特征工程、模型、模型评估)

机器学习入门 机器学习中需要理论性的知识&#xff0c;如数学知识为微积分(求导过程&#xff0c;线性回归的梯度下降法)&#xff0c;线性代数(多元线性回归&#xff0c;高纬度的数据&#xff0c;矩阵等)&#xff0c;概率论(贝叶斯算法)&#xff0c;统计学(贯穿整个学习过程)&a…

Nginx使用(五)配置高可用集群示例

一、条件&#xff08;1&#xff09;需要两台Nginx服务器&#xff08;2&#xff09;需要keepalived&#xff08;3&#xff09;需要虚拟ip二、准备工作&#xff08;1&#xff09;需要两台服务器&#xff08;2&#xff09;在两台服务器安装nginx&#xff08;3&#xff09;在两台服…

Linux应用基础与实训小结

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java案例分…

ElasticSearch安装及基本使用

文章目录0. 学习资料1. 概念2. 安装ESdocker安装1. **下载镜像**2. **创建实例**3. **浏览器访问&#xff1a;**4. 测试使用Kibana安装1. 下载镜像2. 创建实例3. 效果3. 检索_cat保存查询通过id查询乐观锁操作更新删除批量操作**导入测试数据**4. 进阶搜索基本检索参考文档基本…

将数据从Java Bean复制到另一个Java Bean

JavaBean复制的几种方式1&#xff1a;概述在实际编程过程中&#xff0c;我们常常要遇到这种情况&#xff1a;有一个对象A&#xff0c;在某一时刻A中已经包含了一些有效值&#xff0c;此时可能 会需要一个和A完全相同新对象B&#xff0c;并且此后对B任何改动都不会影响到A中的值…

P2T: Pyramid Pooling T ransformer for Scene Understanding

论文链接&#xff1a; https://arxiv.org/abs/2106.12011 中文版本&#xff1a; https://mmcheng.net/wp-content/uploads/2022/08/22PAMI_P2T_CN.pdf 代码链接&#xff1a; https://github.com/yuhuan-wu/P2T P2T: Pyramid Pooling T ransformer for Scene Understanding一、摘…

2023年网络安全比赛--JavaScript安全绕过中职组(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 1.使用渗透机场景kali中工具扫描服务器,将服务器上apache版本号作为flag提交; 2.使用渗透机场景windows7访问服务其场景中的网站(网站路径为IP/javascript),找到网站首页中flag并提交; 3.使用渗透机场景windows7根据第二题的…

2. 拍照的基础知识

1. 单反相机的全称为单镜头反光相机 单反相机的原理就体现在“单”和“反”上&#xff0c;单字就是单镜头&#xff0c;就是说单反相机只能安装一个镜头&#xff0c;光线或是影像通过单反镜头进行来取景的。 对于“反”来说就是相机系统内部有一个反光板。光线通过单反镜头投射…

你应该知道的 7 个最受欢迎的 Python 机器学习库

2023年你应该知道的 7 个最受欢迎的 Python 机器学习库 33/100 发布文章 weixin_38037405 未选择任何文件 new 有一句谚语“你不必重新发明轮子”&#xff0c;工具库就是最好的例子。它可以帮助您以简单的方式编写复杂且耗时的功能。在我看来&#xff0c;一个好的项目会使用一些…

LeetCode分类刷题----字符串篇

字符串字符串1.反转字符串344.反转字符串541.反转字符串||2.替换空格剑指offer05.替换空格3.翻转字符串里的单词151.反转字符串里的单词4.左旋转字符串剑指 Offer 58 - II. 左旋转字符串5.实现strStr函数()28.实现strStr()函数6.重复的子字符串459.重复的子字符串字符串 1.反转…

YOLOv1学习笔记

来源&#xff1a;投稿 作者&#xff1a;ΔU 编辑&#xff1a;学姐 论文笔记 《You Only Look Once:Unifified, Real-Time Object Detection》 Joseph Redmon∗ , Santosh Divvala∗†, Ross Girshick , Ali Farhadi∗† University of Washington∗ , Allen Institute for A…

【JavaGuide面试总结】计算机网络·上

【JavaGuide面试总结】计算机网络上1.OSI 七层模型是什么&#xff1f;每一层的作用是什么&#xff1f;2.TCP/IP 四层模型是什么&#xff1f;每一层的作用是什么&#xff1f;应用层传输层网络层网络接口层3.应用层有哪些常见的协议&#xff1f;4.为什么网络要分层&#xff1f;5.…

数据分析方法与模型

文章目录1 数据分析1.1 占比分析1.2 趋势分析1.3 对比分析1.4 象限分析1.5 排名分析1.6 维度分析2 分析模型2.1 费米问题-大致估算2.2 七问分析法-思考角度的拓展2.3 互联网通用模型AARRR、八角分析法2.3.1 AARRR2.3.2 游戏化用户增长策略-八角模型本文来源&#xff0c;为接地气…