C/C++

2023-11-01


参考: 麦子学院-嵌入式C语言高级-内存空间

2-3 : C语言内存空间的使用-数组

内存分配的一种形式
在这里插入图片描述

数组的定义及初始化

定义一个空间:
1、大小
2、读取方式
数组名[]:升级为连续空间的名称, [m]的作用域只在申请的时候起作用
每个多大?数组名前定义数据类型,按int 还是char进行切割?
数组名是地址常量符号,地址常量标签;一定不要放在等号左边
char buff[100];
buf = “hello world!” //该语法错误,不能赋值给常量。
int a[100]; //空间申请的一种方式
数据会有越界问题
a[10] a是门牌号,是常量,a的属性已定。而指针是内存,可变。

数组空间的初始化

空间的赋值
按照标签逐一处理
int a[10]: [0-9]
a[0]= xx;
a[1]= yy;
程序员这样赋值,工作缗比较大,能不能让编译器进行一些自动处理,帮助程序员写如上的程序

空间的第一次赋值

->空间定义时,就告知编译器的初始化情况,空间的第一次赋值,初始化操作
int a[10] = 空间; //调用标准库或者逐一拷贝,和普通变量初始化有本质不同
c语言本身,CPu内部本身一般不支持大空间和大空间的拷贝
int a[10] = {10,20,30} //3个B,3个字节。
          =====>拷贝的过程,CPU做了,a[0] = 10; a[1] = 20;  a[2] = 0

和逐一赋值是一样的,{}代表空间的限制块

数组空间的初始化和变量的初始化本质不同,尤其在嵌入式的裸机开发中,空间的初始化往往需要库函数的铺助。

char

char buf[10] = {‘a’,‘b’,‘c’} //a是8b,占1个B,共占3个字节
buf当成普通内存来看,没有问题
buf当成一个字符串来看,最后加上一个’\0’或者0
字符串的重要属性是,结尾一定有一个’\0’(内存中0的表示形式)

char buf[10] = {“abc”}; //编译器看到"“自动加0
char buf[10] = “abc”; //执行后,系统中有2个abc,一个是"abc”(常量),一个是buf的变量-字符串"abc",=号完成常量向变量去拷贝。buf可变,可操作。不能改变abc
本质区别于:char p = “abc”; //指向"abc"的指针,buf可变,但p不变
buf[2] = ‘e’ //可行, p[2] = ‘e’ //不行,出现段错误,因为p指向常量,不能修改
示例:

#include <stdio.h>

int main()
{
    printf("the x  is %x\n","abc");  //显示的是内存地址
    return 0;

}
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the x  is 409020

char buf[]=“abc”; //4个字节,因为还有0
char buf[10] = “abc”;
buf = “hello world!” //buf是常量标签,不能修改,故编译错误,标签不能变
第二次内存初始化,赋值?
逐一处理 buf[0] = ‘h’ ,…buf[n+1] = 0;

strcpy(工程禁用), strncpy(推荐)

一块空间,当成字符空间,提供了一套字符拷贝函数
在这里插入图片描述
字符拷贝函数的原则:
内存空间和内存空间的逐―赋值的功能的一个封装体,一旦空间中出现了0这个特殊体,则函数拷贝结束。
strcpy()
标准函数代码中会嵌入一些汇编语言,开发过程中效率进一步提高。
嵌入式开发工程师:根据CPU的体系结构不同以及CPU的能力,这段程序是可以改变的,能够更加优化。
char buf[10] = “abc”;
strcpy(buf,“hello world”); //完成复制,并自动加入"\0"结束标志
当"hello world*********"等待复制的字符串会一直copy,典型的内存泄漏,工程不能使用,只能用strncpy,控制复制长度,限制拷贝数量。

非字符串空间

字符空间
ASCII码编码来解码的空间,–》给人看
%s abc ‘a’ ‘b’ ‘c’
'\0’作为结束标志
非字符空间
数据采集 采集0x00-0xff 8bit
开辟一个存储这些数据的盒子,
定义时用
char buf[10]; ----》string
有歧义,怎么能保证完全是数据?
采集过来的只有位的概念,和符号无关,
必须定义为unsigned char buf[10]; ----》一定是最小data单位

unsigned char *p = sensor_ base;
strcpy(buf,p);//此方法这里不适用,看到\0才停止,存在非常不合理的风险,
所有,非字符串空间只管逐一拷贝,结束在哪里?只能是定义个数。
拷贝三要素:
 1、src
 2、dest
 3、个数(sizeof)

memcpy

int buf[10];
int sensor_buf[100];

memcpy(buf,sensor_buf,10sizeof(int)); //非单字节空间是,需sizeof
在这里插入图片描述

unsigned char buf1[10];
unsigned char sensor_buf[100];// 00 00 00 23 45 78
// strncpy(buf,sensor_buf,10) //拷贝不成功,因为数据00开头
memcpy(buf,sensor_buf,10*sizeof(unsigned char)); //10个字节(数据空间),对应10个内存单位,这里正好一一对应,书写规范要求带sizeof
先找对象(有无符号),再确定操作。

指针与数组

指针数组

建立与二维指针的关系,哪种好理解,记哪个
*a[100] //*代表a的属性
char *a[100];
sizeof(a) = 100 * 4; //一个地址4个字节
char **a;

数组名的保存

定义一个指针,指向int a[10]的首地址
定义一个指针,指向int b[5][6]的首地址
int *p1 = a; //这种写法是合理的,p指针定义读内存的方法是int,4个字节一组,读取,与原数据定义一致,2个内存读取的方式一样,就不会报警告或者错误。
int **p2 = b; //错误方法

示例代码

#include <stdio.h>

int main()
{
    int a[10];
    int b[5][6];

    int *p1 = a;

    int **p2 = b; //提示警告,因为二维数组与二维指针没有任何关系

}
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// 2.c: In function 'main':
// 2.c:10:16: warning: initialization from incompatible pointer type [enabled by default]

二维数组不是按基本的数据类型读内容,因为每行中的列不同,上例中,每移动一行的指针,内存跨度为4*6=24B,是一块一块的内存。
正确写法为:int (*p)[6]; //*p是整体,[6]每行二维数组的列数。
故示例中应改为:
int (*p2)[6] = b; //在内存中指针移动或指代的方式根据之前的int和之后的[]修饰
int b[2][3][4]
int (*p)[3][4];

2-4 : C语言内存空间的使用-结构体

字节对齐

出于CPU效率的考虑,牺牲一点空间换取时间的效率。
这样的解决方案又称为字节对齐,32bit对齐。

示例

#include <stdio.h>

struct  abc
{
    char a;
    int  b;
};

int main()
{
    struct  abc buf;

    printf("the buf is %lu\n",sizeof(buf)); //8个字节

};
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the buf is 8

打包顺序的影响

最终结构体的大小一定是4的倍数
结构体成员变量顺序不一致,也会影响他们的大小
可以由低2位对齐,满足效率要求。

示例

#include <stdio.h>

struct  abc
{
    char a;
    short e;
    int  b;
};

struct  my
{
    char a;
    int  b;
    short e;

};

int main()
{
    struct  abc buf1;
    struct  my  buf2;
    printf("the buf1 is %lu\n",sizeof(buf1)); //8个字节
    printf("the buf2 is %lu\n",sizeof(buf2)); //8个字节

};
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the buf1 is 8
// the buf2 is 12

内存分布图

内存的属性

1、大小
2、在哪里
int a; //默认方式进行最终的定位,放置的区域叫section(分段)的概念

编译—》汇编―–》链接
*.o build

#include <stdio.h>

int a;   //主函数体外,可看成是全局变量,存放位置离mian的空间较近
int main()
{
   //int a;  //临时的,当函数完成后,会销毁,存放位置也相对较高

   a = 0x10;

   printf("the a is %p\n",&a);

   printf("the main is %p\n",main);

   return 0;
};
// 局部变量:
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the a is    000000000064FE2C
// the main is 0000000000401500
// 全局变量:
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the a is    000000000040D3C8
// the main is 0000000000401500
内核空间    应用程序不许访问,看也不行

----------------3G
栈空间(局部变量)

运行时的堆空间(malloc申请的空间)

全局数据空间    (初始化的空间,未初始化) data bss
只读数据段  "hello world"                TEXT段
代码段      code(只读)                 TEXT段

0x0;

#include <stdio.h>

int b = 100;   //主函数体外,可看成是全局变量,存放位置离mian的空间较近
int fun()
{
    static int a = 100;

    return a++;
}
int main()
{
    static int a;
    unsigned char *p;
    a = 0x10;

    printf("the a is %p\n",&a);

    printf("the main is %p\n",main);

    p = (unsigned char *)main;

    printf("the p[0] is %x\n",p[0]);
    p[0] = 0x12; //操作这样属性的区域,会报错:segmentation fault
    printf("+++++ the p[0] is %x\n",p[0]);
    return 0;

};
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the a is 000000000040C030
// the main is 0000000000401519
// the p[0] is 55
// 无显示

示例代码:

#include <stdio.h>

int b = 100;   //主函数体外,可看成是全局变量,存放位置离mian的空间较近
int fun()
{
    static int a = 100;

    return a++;
}
int main()
{
    static int a;
    unsigned char *p;
    char *p1 = "helao world";
    a = 0x10;

    printf("the p1 is %s\n",p1);
    printf("the string address is %p\n","helao world");
    p1[3] = 'l';
    printf("the p1 is %s\n",p1);

    // printf("the a is %p\n",&a);
    // printf("the main is %p\n",main);

    // p = (unsigned char *)main;

    // printf("the p[0] is %x\n",p[0]);
    // p[0] = 0x12; //操作这样属性的区域,会报错:segmentation fault
    // printf("+++++ the p[0] is %x\n",p[0]);
    return 0;

};
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the p1 is helao world
// the string address is 0000000000409020
// 无显示
示例3
```C
#include <stdio.h>

// int b = 100;   //主函数体外,可看成是全局变量,存放位置离mian的空间较近
int fun()
{
    static int a = 100;

    return a++;
};
int main()
{
    static int a;
    unsigned char *p;
    //char *p1 = "helao world";
    const int b = 0x12345678; //b只读变量
    a = 0x10;
    char *p1 = &b;

    printf("the p1 is %x\n",p1);
    printf("the string address is %p\n","helao world");
    p1[3] = '0';
    printf("the p1 is %x\n",p1);
    return 0;
};

// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// 2.c: In function 'main':
// 2.c:17:16: warning: initialization from incompatible pointer type [enabled by default]
// the p1 is 64fe24
// the string address is 000000000040902E
// the p1 is 64fe24

// E:\temp>size 2.exe
//    text    data     bss     dec     hex filename
//   30936    2864    5136   38936    9818 2.exe

示例4:同样的字符串增加字符前后堆空间分布

#include <stdio.h>

// int b = 100;   //主函数体外,可看成是全局变量,存放位置离mian的空间较近
int fun()
{
    static int a = 100;

    return a++;
};
int main()
{
    static int a;
    unsigned char *p;
    char *p1 = "helao world";
    const int b = 0x12345678; //b只读变量
    a = 0x10;

    printf("the p1 is %s\n",p1);
    printf("12345678the string address is %p\n","helao world");  //对独一无二的字符串加上1234
    p1[3] = 'l';
    printf("the p1 is %s\n",p1);
    return 0;
};
//    text(代码)    data(数据)     bss(未初始化信息)     dec     hex filename
// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the p1 is helao world
// the string address is 0000000000409020

// E:\temp>size 2.exe
//    text    data     bss     dec     hex filename
//   30952    2864    5136   38952    9828 2.exe

// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the p1 is helao world
// 1234the string address is 0000000000409020

// E:\temp>size 2.exe
//    text    data     bss     dec     hex filename
//   30952    2864    5136   38952    9828 2.exe

// E:\temp>cd "e:\temp\" && gcc 2.c -o 2 && "e:\temp\"2
// the p1 is helao world
// 12345678the string address is 0000000000409020

// E:\temp>size 2.exe
//    text    data     bss     dec     hex filename
//   30984    2864    5136   38984    9848 2.exe

在这里插入图片描述

size命令

# E:\temp>size 2.exe
#    text    data     bss     dec     hex filename
#   30984    2864    5136   38984    9848 2.exe

# E:\temp>strings 2.exe  //显示双引号内的字符
# ffffff.
# ATUWVSH
# T$ E
# t5H9
# [^_]A\
# fff.
# D$ tv
# D$(H
# T$ H
# L$ H
# L$(H
# fffff.
# ATUWVSH
#  [^_]A\
# ......

static int a; 放在bss空间中,而默认值是auto。
在这里插入图片描述
可以认为是:局部访问的全局数据段标识,仅在函数中起作用,但是存放位置还是在全局的数据空间。
查看内存分配命令可以用:nm 2.exe >>2.txt

堆空间

运行时的,函数内部使用的变量,生存周期为,函数一旦返回就释放,生存周期是函数内。
运行时可以自由分配、自我管理或释放的空间。生存周期由程序员来决定。

分配

malloc(),一旦成功,返回分配好的地址,我们只需要接受即可。对于这个新地址的读取方法由程序员指定。
输入参数指定分配的大小,单位是B
char *p;
p = (char *)malloc(100);
if (p == NULL){
error
}

int a[5]; malloc(5*sizeof(int))

存在泄漏风险的代码示例

void fun()
{
    char *p;
    p = (char *)malloc(100);
    return;
}

这个函数中的p当函数执行完毕时,还是指向malloc已申请的空间(未释放),存在泄漏风险。
解决方式:free。

释放(与分配配对执行)

free§;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++ 的相关文章

随机推荐

  • 【SQL注入-02】SQL注入点的简单判断

    目录 1 实验环境 2 准备工作 3 判断是否存在SQL注入点及注入的类型 4 结论 1 实验环境 实验靶场 虚拟机 IP为172 16 1 1 本节实验靶场是在win2008系统上基于phpstudy搭建的一个简单网站 win2008及p
  • 区块链学习笔记1

    1 什么是区块链 区块链是通过去中心 化去信任的方式集体维护一个可靠数据库的技术方案 参与系统中的任意多个节点把系统一段时间内的全部的信息 数据通过密码学算法计算和记录到一个数据块 区块block 并且生成该区块的指纹用于链接下一个区块和校
  • Hadoop三种运行模式详解

    5 1 本地运行模式 本地运行模式不需要额外的设置 只需要执行响应的jar包就可以了 不需要任何的集群配置 本地运行模式其实也是一种单节点模式 Hadoop提供了两个案例 我们执行这两个案例 5 1 1 官方 grep 案例 这个案例是提供
  • Fast Global Registration 快速全局配准

    目录 1 算法过程 2 基于四元约束关系集的生成 3 基于关系集目标函数的构建 4 目标函数的优化 本文出自CSDN点云侠 爬虫自重 把自己当个人 1 算法过程 Fast Global Registration算法利用四元约束剔除匹配的关系
  • 字符串去掉小数点后取整数

    字符串去掉小数点后取整数 String str 70 2 if str contains int indexOf str indexOf str str substring 0 indexOf int matchRateInt Intege
  • VS 项目中查找中文正则表达式

    u4E00 u9FA5 u4E00 u9FA5 直接在VS中用Ctrl F或者Ctrl Shift F 在 查找内容 中输入 u4E00 u9FA5 u4E00 u9FA5 然后在 查找选项 使用正则表达式 打钩 就能找出所有这些中文字符串
  • ios gzip 压缩字符串

    1 import zlib h 2 libz 1 2 5 tbd 3 NSData gzipDeflate NSString str NSData data str dataUsingEncoding NSUTF8StringEncodin
  • CeiT阅读笔记

    CeiT Incorporating Convolution Designs into Visual Transformer 纯Transformer架构通常需要大量的训练数据和额外的监督才能获得与卷积神经网络CNNs相当的性能 为了克服这
  • Docker容器访问宿主机网络

    业务请求量小的时候 我们会把一些工程部署到同一台机器上 这些工程之间也会相互访问 如果是http的接口 我们最方便的是使用localhost带地本机的ip 不过结合docker容器后出现了问题 docker容器中localhost表示容器的
  • IDEA项目实践——JavaWeb简介以及Servlet编程实战

    系列文章目录 IDEA项目实践 创建Java项目以及创建Maven项目案例 使用数据库连接池创建项目简介 IDEWA项目实践 mybatis的一些基本原理以及案例 IDEA项目实践 动态SQL 关系映射 注解开发 IDEA项目实践 Spri
  • C# 系统应用之清除Cookies、IE临时文件、历史记录

    本文主要是项目 个人电脑使用记录清除软件 系类文章中关于清除浏览器Cookies IE临时文件 最近使用历史记录等内容 该篇文章的基本思想路线是首先了解上网历史记录的Windows中常用文件路径 再通过文件delete方法删除该文件下内容
  • leetcode86 分割链表

    题目 给你一个链表的头节点 head 和一个特定值 x 请你对链表进行分隔 使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前 你应当 保留 两个分区中每个节点的初始相对位置 示例 输入 head 1 4 3 2 5 2 x 3
  • LeetCode-216-组合总和Ⅱ

    题目链接 LeetCode 216 组合总和 解题思路 回溯算法 注意事项注释中有 代码实现 class Solution 和为 n 个数为 k 求的是组合 不要求顺序 递归的深度是 k public List
  • 2019.9.4 key-value stores 和 timestamps

    这一章先是将key value stores 其实这个就是对应的关系型数据库的以schema为基础的存储原理 一般的nosql就是用的这种 特点就是没有什么明显的schema 除了key以外就是一大坨的value 不需要有什么整齐的格式 讲
  • Leetcode刷题笔记:二叉树篇(下)

    1 Leetcode 110 平衡二叉树 难度 递归 迭代 给定一个二叉树 判断它是否是高度平衡的二叉树 本题中 一棵高度平衡二叉树定义为 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1 示例 1 给定二叉树 3 9 20 nu
  • Java类文件结构、Java类加载过程、类加载器(双亲委派模型)、Java对象实例化过程

    1 类文件结构 根据 Java 虚拟机规范 类文件由单个 ClassFile 结构组成 ClassFile u4 magic Class 文件的标志 u2 minor version Class 的小版本号 u2 major version
  • 【银河麒麟V10】【服务器】系统负载分析

    一 cpu负载 1 CPU基本概念 1 1 什么是物理cpu个数 物理cpu个数即主板上实际能够看到的cpu个数 也是cpu插槽数 1 2 什么是cpu核数 cpu核数指的是物理cpu个数 每个物理cpu的核心数 因为每个物理cpu可能包含
  • 深度学习笔记(八):目标检测性能评价指标(mAP、IOU..)

    一 mAP 这里首先介绍几个常见的模型评价术语 现在假设我们的分类目标只有两类 计为正例 positive 和负例 negtive 分别是 1 True positives TP 被正确地划分为正例的个数 即实际为正例且被分类器划分为正例的
  • iptables 规则管理

    参考 http www zsythink net archives 1517 有两台测试机 zk02 192 168 27 152 zk03 192 168 27 153 目录 1 增加规则 2 追加规则 1 增加规则 首先看一条命令 表示
  • C/C++

    文章目录 2 3 C语言内存空间的使用 数组 数组的定义及初始化 数组空间的初始化 空间的第一次赋值 和逐一赋值是一样的 代表空间的限制块 char strcpy 工程禁用 strncpy 推荐 非字符串空间 memcpy 指针与数组 指针