前言:本人是新乡一个小小二本的物联网工程大四学生,之前面试江苏的一个嵌入式开发岗位时,被面试官狠狠滴拷打了,最后也是不出意外的挂掉了面试。我痛定思痛,在当天下午就开始复习专业知识和背面试题。但是其他大佬们写的博客不是十分贴合我的情况,就导致了我的收藏夹存了几十篇博客,所以我就整理了这个嵌入式面试专栏。我按照学习时的顺序编写和发布,所以以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、指针和数组的区别
数据类型:
指针是一种数据类型,用于存储内存地址。指针可以指向不同数据类型的内存位置。
数组是一种数据结构,用于存储相同数据类型的一组连续内存单元。
大小:
指针的大小通常与系统架构相关,它存储一个内存地址,因此大小在不同系统上可能会有所不同。
数组的大小是由其包含的元素数量决定,每个元素的大小也是相同的。
初始化和赋值:
指针需要显式初始化为一个有效的内存地址,可以通过将其设置为某个变量的地址来初始化。
数组在声明时需要指定大小,而且在创建时会自动初始化,可以直接为数组元素赋值。
地址运算:
指针允许进行地址运算,例如指针加法或减法,以访问内存中的相邻位置。
数组的元素在内存中是连续存储的,因此可以通过索引来访问不同的元素。
传递给函数:
指针可以用于将变量的地址传递给函数,以便在函数内部修改变量的值。
数组在传递给函数时通常会退化为指针,因此函数接收到的是指向数组第一个元素的指针。
动态内存分配:
指针可用于动态内存分配,例如使用
malloc
或new
来分配内存,然后通过指针访问分配的内存。数组的大小通常在编译时确定,但C99标准引入了可变长度数组(VLA),允许在运行时指定数组大小。
由于时间不充裕和本人水平有限,文章内容存在借鉴的情况,如有冒犯,请联系yxun43以删除。