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

devtools/2024/9/24 20:20:40/

目录

指针的大小问题

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.ppmy.cn/devtools/116652.html

相关文章

【大模型专栏—实战篇】从0到1带你QLoar微调

大模型专栏介绍 &#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本文为大模型专栏子篇&#xff0c;大模型专栏将持续更新&#xff0c;主要讲解大模型从入门到实战打怪升级。如有兴趣&#xff0c;欢迎您的阅读。 &#x1f4…

Debezium日常分享系列之:将容器镜像移至 quay.io

Debezium日常分享系列之&#xff1a;将容器镜像移至 quay.io 在Debezium 3.0.0.Final发布之后&#xff0c;我们将不再向docker.io发布容器镜像更新。旧版本的Debezium 2.x和1.x镜像将继续保留在docker.io上&#xff1b;然而&#xff0c;所有未来的Debezium 2.7.x和3.x或更高版本…

python | huey,一个非常厉害的 任务调度 Python 库!

本文来源公众号“python”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;huey&#xff0c;一个非常厉害的 Python 库&#xff01; 大家好&#xff0c;今天为大家分享一个非常厉害的 Python 库 - huey。 Github地址&#xff1a;h…

图片切换示例2【JavaScript】

这段代码实现了一个简单的图片切换效果。当用户将鼠标悬停在不同的小缩略图上时&#xff0c;主显示框&#xff08;#box&#xff09;的背景图片会切换为相应的缩略图所代表的图片。 实现效果&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"zh"…

project generator 简单使用(二)之 CLion 与 AC6

文章目录 1 AC6 之于 CLion2 配置 progen3 可执行文件 size 显示优化4 测试 1 AC6 之于 CLion 1&#xff09;在上一篇文章中&#xff0c;我们知道 project generator 通过其 “Write Once, Compile any Tool” &#xff08;跨工具&#xff09;的特性&#xff0c;可以让我们使用…

Vue3:$refs和$parent实现组件通信

在Vue3中&#xff0c;refs和refs和parent是用于组件间通信的重要机制 一.$refs 1.操作子组件数据 一旦获取到子组件的实例&#xff0c;父组件可以修改子组件暴露的变量值&#xff0c;实现父子组件间的直接数据交换。 2.批量处理子组件 $refs可以用于同时获取多个子组件的…

Vue相关

Vue2 组件传递事件&#xff1a; props $emit sync v-model $parent / $children $parent获取父组件的实例&#xff0c;任意调用父组件的方法&#xff0c;修改父组件的数据 ref 父组件获取 子组件 实例&#xff0c;任意调用子组件的方法获取子组件的属性 provide / injectp…

【Linux】环境变量

一、引入环境变量 1.1 引入环境变量 main函数是有参数的&#xff0c;该参数就用来接收命令行参数的。当我们在执行文件的过程中&#xff0c;我们可以输入一些选项。 1.1.1 参数说明 argc&#xff08;argument count&#xff09;&#xff1a;表示命令行参数的数量&#xff0c…