欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 嵌入式面试学习笔记(入门1)

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

2024/11/16 11:17:09 来源:https://blog.csdn.net/charlie114514191/article/details/142413306  浏览:    关键词:嵌入式面试学习笔记(入门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,这样我们就实际上测试了这个机器是不是小端法的机器了

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com