嵌入式面试学习笔记(入门1)

目录

指针的大小问题

sizeof和strlen

C语言分配内存的方式

数组(的)指针和指针(的)数组

union


指针的大小问题

指针对于不少新手而言是一道难关,但是不必恐惧于指针。他的本质其实就是一个地址。请冷静下来仔细思考。回顾一下对于当今已经普及的“64位计算机”这个说法。这个64不是空穴来风,他指代的是CPU内部的机器字长(一次运算可以处理的最大位数)的大小。那么,我们都知道计算机想要知道你想操作的数在哪,就很有必要提供一个“地址”告知操作数在何处。这就是“地址”。对于64位平台,地址显然是64位(MAR64位长度)。

那么指针实际上就是地址的一个“别称”了,当我们说一个指向某一个对象的指针,实际上就是再说这个对象的地址如何。

void handleTheDemo(int* ptr);
...
int  demo   = 3;
int* ptr    = &demo;
handleTheDemo(ptr)

回到这个问题,既然指针只是一个地址的存储器,那么,他显然跟类型完全无关!你想,对于你根据地址找人,这个人胖还是瘦,半毛钱关系没有,不会影响他的家庭住址地址丝毫。这样我们就可以猜到,对于如下的代码:

#include <stdio.h>
​
void displayPtrSize()
{printf("Size of pointer: %d\n", sizeof(void*));printf("Size of int pointer: %d\n", sizeof(int*));printf("Size of double pointer: %d\n", sizeof(double*));printf("Size of char pointer: %d\n", sizeof(char*));printf("Size of long pointer: %d\n", sizeof(long*));printf("Size of float pointer: %d\n", sizeof(float*));printf("Size of long long pointer: %d\n", sizeof(long long*));printf("Size of short pointer: %d\n", sizeof(short*));printf("Size of unsigned int pointer: %d\n", sizeof(unsigned int*));printf("Size of unsigned long pointer: %d\n", sizeof(unsigned long*));printf("Size of unsigned long long pointer: %d\n", sizeof(unsigned long long*));printf("Size of unsigned short pointer: %d\n", sizeof(unsigned short*));return;
}
​
int main()
{displayPtrSize();return 0;
}

其打印出来的结果是可想而知的。

那么,难道所有的平台上都是如此吗?显然不是,再仔细看看我的第一段话,我们只是在讨论64位平台,事实上还存在一个32位平台,如法炮制,不难猜出地址在这里就是32位了

# 64位平台运行结果
Size of pointer: 8
Size of int pointer: 8
Size of double pointer: 8
Size of char pointer: 8
Size of long pointer: 8
Size of float pointer: 8
Size of long long pointer: 8
Size of short pointer: 8
Size of unsigned int pointer: 8
Size of unsigned long pointer: 8
Size of unsigned long long pointer: 8
Size of unsigned short pointer: 8
# 32位平台运行结果
Size of pointer: 4
Size of int pointer: 4
Size of double pointer: 4
Size of char pointer: 4
Size of long pointer: 4
Size of float pointer: 4
Size of long long pointer: 4
Size of short pointer: 4
Size of unsigned int pointer: 4
Size of unsigned long pointer: 4
Size of unsigned long long pointer: 4
Size of unsigned short pointer: 4

sizeof和strlen

这个话题于我看来有些莫名其妙,后来笔者想到大部分学校喜欢在教授字符串的时候扣这些颇为无聊的小玩意,笔者决定分享一下我的看法。

我们认为sizeof这个东西是操作符,在C文件编译的时候就已经静态的计算好了,如果不信,可以看看这一份简单的C文件的汇编结果:

int main()
{int intSize = sizeof(int);return 0;
}
    .file   "demo.c".text.globl  main.def    main;   .scl    2;  .type   32; .endef.seh_proc   main
main:pushq   %rbp.seh_pushreg    %rbpmovq    %rsp, %rbp.seh_setframe   %rbp, 0subq    $48, %rsp.seh_stackalloc 48.seh_endprologuecall    __mainmovl    $4, -4(%rbp)movl    $0, %eaxaddq    $48, %rsppopq    %rbpret.seh_endproc.def    __main; .scl    2;  .type   32; .endef.ident  "GCC: (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r3) 14.1.0"
​

可以看到我们的sizeof(int)直接被替换成为4了。这个玩意纯粹的是计算目标占用内存的大小。

那strlen呢?他又是怎么一回事呢,其实他是一个string.h里的库函数,用于求解字符串长度的

一个函数!一个操作符!首先差别就很大!

我们下一步是思考他们在字符串使用上的区别

#include <stdio.h>
#include <string.h>
​
int main() {char str[] = "buffer";printf("sizeof: %d\n strlen: %d\n", sizeof(str), strlen(str));return 0;
}

这段代码是显而易见的。不难猜出一个结果是

sizeof: 7 strlen: 6

为什么?我们知道,strlen在实现上不考虑'\0'的存在,他是我们字符串的公认终止符。

size_t strlen(const char* str){int size = 0;while(*str++) size++;return size;
}

可以查看各个编译器对string.h标准库的实现,这里笔者提供的是一个比较好理解的实现,可以发现当我们的*str == '\0'的时候,循环退出,size不自增。

而sizeof不在乎这些,他只知道我们的字符数组的大小是7个(6个字符,一个\0),因此事情就很简单。

Questions:如果我改变为

#include <stdio.h>
#include <string.h>
​
int main() {const char* str = "buffer";printf("sizeof: %d\n strlen: %d\n", sizeof(str), strlen(str));return 0;
}

请问此时又该如何呢?

仔细思考指针和数组是否严肃等价!

C语言分配内存的方式

其实就三种:

void display_bufferAllocation()
{static int _inDataSegment = 1; // 可以在任何地方定义,通通放到静态区int _inStackSegment = 2;int* __inHeapSegment = (int*)malloc(sizeof(int));...free(__inHeapSegment);
}

1、静态存储区分配

内存分配在程序编译之前完成,且在程序的整个运行期间都存在,例如全局变量、静态变量等。

2、栈上分配

在函数执行时,函数内的局部变量的存储单元在栈上创建,函数执行结束时这些存储单元自动释放。

3、堆上分配

堆分配(又称动态内存分配)。程序在运行时用malloc或者new申请内存,程序员自己用free或者delete释放,动态内存的生存期由我们自己决定。

数组(的)指针和指针(的)数组

啥?好奇这个区别,嗨老铁,加一个(的)就搞定了。

int arr[10];
int* ptr_of_array = arr;
​
int* ptr_arr[10];
...

可以看到:数组的声明方式无非就是:

type var_name[size];

type是指针,那就是指针(的)数组。

union

这个有意思,我第一次看到的时候是在确定机器的大小端的时候出现的

#include <stdio.h>
union TestEndian
{unsigned int a;char b;
};
​
int check_endian()
{union TestEndian te;te.a = 0x12345678;if (te.b == 0x78)return 1;elsereturn 0;
}
​
int main()
{printf("Endian: %d\n", check_endian());return 0;
}

为了理解这段代码,我们需要首先需要理解联合体是什么。

我们假想一种有意思的场景

我们需要对一系列的人进行信息存储,对于学生,我们需要存储的是学号;另一方面,对于教师只需要知道名字即可。

当然这个例子没有联合体在Linux进程信息应用上来的好,但是笔者还是决定考虑用这个蹩脚的例子来说明事情。除了抽象两个结果体之外,我们还可以这样做:

#include <stdio.h>
#include <stdlib.h>
typedef enum {STUDENT,TEACHER
}FellowType;
​
typedef struct 
{FellowType type;union {char studentName[20];unsigned int teacherID;} data;
}FellowInSchool;
​
FellowInSchool* createFellowStuent(FellowType type, char* name)
{FellowInSchool* fellow = (FellowInSchool*)malloc(sizeof(FellowInSchool));fellow->type = type;strcpy(fellow->data.studentName, name);return fellow;
}
​
FellowInSchool* createFellowTeacher(FellowType type, unsigned int id)
{FellowInSchool* fellow = (FellowInSchool*)malloc(sizeof(FellowInSchool));fellow->type = type;fellow->data.teacherID = id;return fellow;
}
​
void freeFellow(FellowInSchool* fellow)
{free(fellow);
}
​
void displayFellow(FellowInSchool* fellow)
{switch (fellow->type){case STUDENT:printf("Student Name: %s\n", fellow->data.studentName);break;case TEACHER:printf("Teacher ID: %d\n", fellow->data.teacherID);break;default:printf("Invalid Fellow Type\n");break;}
}
​
int main()
{FellowInSchool* fellow1 = createFellowStuent(STUDENT, "John");FellowInSchool* fellow2 = createFellowTeacher(TEACHER, 1234);displayFellow(fellow1);displayFellow(fellow2);freeFellow(fellow1);freeFellow(fellow2);return 0;
}

你看,对于同一个FellowInSchool结构体可以产生复用,而大大节约内存,让一块内存根据条件的不同按照不同的姿势进行解读。就这个用处

仔细品鉴一下这句话,就会高兴的发现

union TestEndian
{unsigned int a;char b;
};

其实很简单:

你看,这就是小端法的存0x12345678的方式,可以看到,如果我们采取char 的方式读取读到的就是0x78,大端法这里填写的就是0x12,这样我们就实际上测试了这个机器是不是小端法的机器了

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

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

相关文章

人工智能开发实战辅助诊断应用解析

内容导读 项目分析预备知识项目实战 一、项目分析 1、提出问题 随着人们生活水平的提升和健康意识的增强&#xff0c;民众定期进行身体健康体检已成为常态&#xff0c;这种早期的疾病检测和筛查可以及早发现身体里已经出现的异常体征信息&#xff0c;做出正确诊断和有效处理…

信息安全数学基础(15)欧拉定理

前言 欧拉定理是数论中的一个重要定理&#xff0c;它建立了模运算下指数与模的互质关系。这个定理在密码学、信息安全等领域有着广泛的应用&#xff0c;特别是在公钥密码体制&#xff08;如RSA加密算法&#xff09;中。 一、表述 设 n 是一个正整数&#xff0c;a 是一个与 n 互…

万字长文——ConvNeXt(2022CVPR),卷积网络的顶峰之作,在Transformer盛行的当下,卷积网络还能再战!

ConvNext:A ConvNet for the 2020s ConvNext:2020 年代的卷积神经网络 论文地址: https://arxiv.org/pdf/2201.03545 自从Transformer成功应用在视觉领域并且取得显著成绩后,很多人开始抛弃卷积网络架构,转而使用Transformer。然而有的大佬不认为卷积过时了,于是有了这篇…

Sigmoid引发的梯度消失爆炸及ReLU引起的神经元参数失效问题思考

Sigmoid和ReLU激活函数思考&#xff09; 引文Sigmoid函数梯度消失问题梯度爆炸问题解决方案 ReLU函数简化模型示例场景设定前向传播对反向传播的影响总结 内容精简版 引文 梯度消失和梯度爆炸是神经网络训练中常见的两个问题&#xff0c;特别是在使用Sigmoid激活函数时。这些问…

图形化编程012(变量-倒计时)

案例展示 点击绿旗&#xff0c;使用空格键控制鳐鱼&#xff0c;按下空格向上游&#xff0c;松开下落。 在舞台右侧会出现障碍物从右向左移动&#xff0c;移动到左侧边缘发出声音并隐藏。 鳐鱼碰到障碍停止全部脚本&#xff0c;坚持60秒程序结束。 一、逻辑思维 通过读题将大…

鸿蒙媒体开发系列09——OpenSL ES音频录制

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 1、概述 OpenSL ES全称为Open Sound Library for Embedded Systems&#xff0c;是一…

【我的 PWN 学习手札】tcache extend

目录 前言 一、利用手法 二、流程演示 &#xff08;1&#xff09;三块物理相邻的堆块 &#xff08;2&#xff09;溢出修改 size &#xff08;3&#xff09;释放该 chunk &#xff08;4&#xff09;重新申请该 chunk &#xff08;5&#xff09;释放第三块 chunk&#x…

vcs/verdi常用命令(持续更新)

1. 操作rtl 1.1 加载rtl命令 verdi -dbdir simv.daidir的目录 1.2 显示某时刻rtl的值 首先鼠标左键在波形上选中某个特定时刻&#xff0c;然后鼠标选中rtl代码文件&#xff0c;按x就会显示&#xff0c;再按x就会退出显示。 1.3 查找字符串 按/ 1.4 vcs将rtl的信号加载到…

DNS是什么?怎么设置

NS是什么意思?有什么用呢?专业的说DNS就是域名系统 (Domain Name System)的简称&#xff0c;也就是IT人士常说的域名解析系统。主要是让用户在互联网上通过域名找到域名对应的IP地址&#xff0c;因为IP地址都是一串数字(例如&#xff1a;192.168.0.1)不方便记忆&#xff0c;便…

与 CESS Network 共探去中心化创新:重塑数据基础设施,驱动未来变革

随着互联网的快速发展和数据量的爆炸式增长&#xff0c;如何有效管理、存储和保护数据成为了一个日益重要的课题。传统的中心化平台&#xff0c;如 YouTube&#xff0c;虽然为用户提供了便捷的服务&#xff0c;但数据的所有权和控制权往往掌握在平台手中&#xff0c;用户的内容…

口腔检测系统源码分享

口腔检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

Memory Controller Unit (MCU)内存控制器介绍

文章目录 Memory Controller Unit (MCU)内存控制器介绍1. MCU基本概念和功能地址映射读写操作缓存控制内存刷新1.1 地址映射1.2 读写操作1.3 缓存控制1.4 内存刷新 2. MCU的工作原理接收CPU的请求地址转换执行操作管理缓存 3. MCU的类型SDRAM控制器DDR控制器Flash控制器 4. MCU…

嵌入式 开发技巧和经验分享

文章目录 前言嵌入式 开发技巧和经验分享目录1.1嵌入式 系统的 定义1.2 嵌入式 操作系统的介绍1.3 嵌入式 开发环境1.4 编译工具链和优化1.5 嵌入式系统软件开发1.6 嵌入式SDK开发2.1选择移植的系统-FreeRtos2.2FreeRtos 移植步骤2.3 系统移植之中断处理2.4系统移植之内存管理2…

与姜妍同款冰箱,容声516WILL养鲜冰箱领“鲜”上市

9月20日&#xff0c;容声冰箱在“养鲜新净界”——2024年容声新品上市发布会上推出了WILL系列的最新力作——516WILL养鲜冰箱。 据「TMT星球」了解&#xff0c;此次新品搭载了升级版的WILL自然养鲜技术&#xff0c;并以60CM整机平嵌一体&#xff0c;完美融入现代家居美学&…

Mapper代理开发

目的 解决原生方式中的硬编码简化后期执行SQL 步骤 1&#xff0c; 整体目录结构 2&#xff0c; UserMapper.xml 设置SQL映射文件的namespace属性为Mapper接口全限定名 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC &quo…

P9235 [蓝桥杯 2023 省 A] 网络稳定性

*原题链接* 最小瓶颈生成树题&#xff0c;和货车运输完全一样。 先简化题意&#xff0c; 次询问&#xff0c;每次给出 &#xff0c;问 到 的所有路径集合中&#xff0c;最小边权的最大值。 对于这种题可以用kruskal生成树来做&#xff0c;也可以用倍增来写&#xff0c;但不…

程序员工作中经常使用的C/C++开源库

Bundle 项目地址&#xff1a;GitHub - r-lyeh-archived/bundle: :package: Bundle, an embeddable compression library: DEFLATE, LZMA, LZIP, BZIP2, ZPAQ, LZ4, ZSTD, BROTLI, BSC, CSC, BCM, MCM, ZMOLLY, ZLING, TANGELO, SHRINKER, CRUSH, LZJB and SHOCO streams in a …

Datawhale X 南瓜书 task02学习笔记

算法原理引入 样本点通常应该在模型的2侧&#xff0c;原因&#xff1a;在实际中&#xff0c;因为某种不可控的因素&#xff0c;测出来的样本点肯定是有误差的。如果样本数据点都在模型上&#xff0c;则说明在建立模型时&#xff0c;把误差也考虑进去了&#xff0c;这就是我们说…

9月21日 电子产品世界上海站沙龙

9月21日 电子产品世界上海站沙龙 有幸参加了 9月21日 14: 00 在上海 九江路 700号 上海南新雅皇冠假日酒店 4楼 举行的 TI MSPM0 MCU开发经验交流 会 本次邀请资深开发者&#xff0c;现场跟大家进行TI MSPM0 MCU开发经验交流&#xff0c;并详细展示基于TI MSPM0 MCU开发的实用…