嵌入式面试——C语言数据结构篇

前言:本人是新乡一个小小二本的物联网工程大四学生,之前面试江苏的一个嵌入式开发岗位时,被面试官狠狠滴拷打了,最后也是不出意外的挂掉了面试。我痛定思痛,在当天下午就开始复习专业知识和背面试题。但是其他大佬们写的博客不是十分贴合我的情况,就导致了我的收藏夹存了几十篇博客,所以我就整理了这个嵌入式面试专栏。我按照学习时的顺序编写和发布,所以以C语言和数据结构就作为本专栏的第一篇。

1、数据类型及其占用字节大小

各个类型在64位系统中占字节数

char 1; short int 2 ;

int 4; unsigned int 4; float 4;

double 8; long 8; long long 8; unsigned long 8;

int*、char*、short*  只要是指针,就是4/8个字节

2、内存分区及其存储的内容

  • 代码区:存储程序的机器指令。在程序加载到内存时,操作系统会将可执行文件中的二进制代码复制到代码区,CPU执行的就是这部分内存里的指令。

  • 数据区:存储程序中已初始化的全局变量和静态变量的值。

  • BSS区:存储未初始化或者初始化为0的全局变量和静态变量。BSS段并不在可执行文件中存储这些变量的值(都为0),而是在程序加载到内存时自动为这些变量分配内存并清零。

  • 栈 区:存储函数调用时的局部变量、函数参数和返回地址等临时数据。栈是从高地址向低地址增长的,采用“后进先出”(LIFO)的原则。

  • 堆 区:用于动态内存分配,即程序员在程序运行过程中通过系统调用分配的内存区域。

3、内存分配方式

I、栈内存分配(Stack Allocation):

栈内存是一种自动分配和释放的内存,用于存储局部变量和函数调用的上下文信息; 变量在函数内部声明时,存储在栈内存中。栈的特性是后进先出(LIFO),所以最后进入栈的变量会最先被释放。

II、堆内存分配(Heap Allocation):

堆内存是一种动态分配和释放的内存,用于存储程序运行时需要动态分配的数据。 使用malloc()、calloc()、realloc() 等函数在堆上分配内存,并使用 free() 函数释放堆内存。

III、全局变量和静态变量分配(Static and Global Variables):

全局变量和静态变量存储在静态存储区,它们在程序的整个生命周期内存在。 全局变量在所有函数之外声明,而静态变量在函数内部使用 static 关键字声明。

4、关键字及其作用

I、static:控制变量的存储方式和作用范围

  • static用于全局变量:表示该变量是静态全局变量

  • 作用域为当前文件用于函数:该函数为静态函数,只能在本文件中调用;静态函数在内存中只有一份,普通函数在内存中只有一份拷贝;

  • 用于局部变量:为静态局部变量,只初始化一次,之后调用函数都是上次函数退出的值。即改变变量的生存周期为整个程序运行时间段内。

II、const:限定一个变量不可被修改

  • 修饰一般常量:初始化完成后,值不可被修改;

  • 修饰常量指针:不能通过指针修改指针所指向的变量的值;但是指针可以指向别的变量;

  • 修饰指向常量的指针(指针常量):指针常量的值不能被修改,即不能存一个新的地址,不能指向别的变量。但是可以通过指针修改它所指向的变量的值。

III、volatile:阻止编译器进行优化

用 volatile 声明的变量表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错。并且在使用过程中需要注意,它只是告诉编译器该变量是易变的,不要直接从寄存器中读取该变量的值,但并不保证进程访问该变量的正确性,仍然需要考虑对多线程的并发安全问题,要遵循原子操作和加锁等操作。

IV、extern

当extern修饰一个变量或函数时时,就是在声明这个变量(函数),告诉编译器在外部文件中已经这个变量(函数),要通过编译。

V、sizeof和strlen

  • sizeof 是一个运算符,并不是函数;而 strlen() 是一个函数。

  • sizeof 计算所占内存的大小,就是看我们传入的数据,它的数据类型是几个字节,sizeof 算出来的就是几个字节。

  • strlen 计算字符串长度,遇到字符串中的‘\0’终止,得到的长度是'\0'之前的字符个数,不包括'\0'本身。

5、C语言编译到执行的过程

  • 预处理:处理源文件,包括宏定义、头文件的展开,条件编译等 文件后缀名为.i

  • 编 译:编译器将经过预处理的文本文件翻译成汇编代码 文件后缀名.s

  • 汇 编:汇编器将汇编代码转换成及其可以执行的指令 文件后缀名为.o

  • 链 接:连接器将目标文件以及一些库文件进行连接,生成可执行文件

6、堆和栈的区别

I、生存周期:

  • 栈:栈上的变量的生命周期通常局限于函数的执行期间。当函数返回时,栈上的局部变量会被销毁,内存被释放。

  • 堆:堆上分配的内存的生命周期可以长于任何特定函数调用,通常由程序员来管理。内存只有在显式释放时才会被回收。

II、 大小限制:

  • 栈:栈的大小通常受限于编译器或操作系统的设置,较小。因此,栈适合存储相对较小的数据结构。

  • 堆:堆的大小通常受系统内存的限制,可以分配较大的内存块,适合存储大型数据结构。

III、 访问速度:

  • 栈:栈上的内存访问速度较快,因为它是线性分配,且栈上的数据通常存储紧凑。

  • 堆:堆上的内存访问速度较慢,因为它是动态分配的,数据可能分散存储在内存中。

IV 分配方式:

  • 栈:栈上的内存分配和释放是自动的,不需要程序员干预。

  • 堆:堆上的内存需要程序员手动分配和释放,使用 malloc()free() 等函数。

7、指针

指针是一种编程概念,它是一个变量或数据类型,用于存储内存地址。指针允许程序直接访问和操作内存中的数据,而不仅仅是访问变量的值。指针通常在低级编程语言(如C和C++)中使用,以及某些高级编程语言中的底层编程任务中。

关键要点关于指针包括:

  • 内存地址:指针存储一个内存地址,该地址指向计算机内存中的某个位置。

  • 指向:指针可以指向内存中的数据,这可以是基本数据类型(如整数、字符)或复杂的数据结构(如数组、结构体)。

  • 解引用:通过解引用指针,可以访问指针所指向的内存地址上的值。解引用操作使用 * 符号。

  • 地址运算:指针可以进行地址运算,例如指针加法或减法,以访问相邻内存位置。

  • 动态内存分配:指针在动态内存分配中非常有用,允许程序在运行时分配和释放内存,避免静态内存分配的限制。

  • 传递参数:指针允许将变量的地址传递给函数,以便在函数内部修改变量的值。

好处

  • 指针可以动态分配内存

  • 在链表中可以方便修改链表的节点

  • 解析字符串

  • 相同类型的指针可以直接复制

8、指针和数组的区别

数据类型:

  • 指针是一种数据类型,用于存储内存地址。指针可以指向不同数据类型的内存位置。

  • 数组是一种数据结构,用于存储相同数据类型的一组连续内存单元。

大小:

  • 指针的大小通常与系统架构相关,它存储一个内存地址,因此大小在不同系统上可能会有所不同。

  • 数组的大小是由其包含的元素数量决定,每个元素的大小也是相同的。

初始化和赋值:

  • 指针需要显式初始化为一个有效的内存地址,可以通过将其设置为某个变量的地址来初始化。

  • 数组在声明时需要指定大小,而且在创建时会自动初始化,可以直接为数组元素赋值。

地址运算:

  • 指针允许进行地址运算,例如指针加法或减法,以访问内存中的相邻位置。

  • 数组的元素在内存中是连续存储的,因此可以通过索引来访问不同的元素。

传递给函数:

  • 指针可以用于将变量的地址传递给函数,以便在函数内部修改变量的值。

  • 数组在传递给函数时通常会退化为指针,因此函数接收到的是指向数组第一个元素的指针。

动态内存分配:

  • 指针可用于动态内存分配,例如使用 mallocnew 来分配内存,然后通过指针访问分配的内存。

  • 数组的大小通常在编译时确定,但C99标准引入了可变长度数组(VLA),允许在运行时指定数组大小。

由于时间不充裕和本人水平有限,文章内容存在借鉴的情况,如有冒犯,请联系yxun43以删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值