(嵌入式八股)第1章 C语言(四)
1.31 使用指针需要注意什么?
指针是一个强大工具,但如果使用不当,可能会导致严重的程序错误,如内存泄漏、程序崩溃等。
指针定义后要初始化,未指向时指向 NULL
- 问题:如果指针没有初始化,它将指向一个随机的地址,可能会导致访问未定义的内存。
- 解决方法:定义指针时,最好将其初始化为 NULL,确保它不指向任何地址。
- 原因:通过将指针初始化为 NULL,可以在之后检查指针是否已经指向有效内存。访问 NULL 指针通常会导致程序崩溃或错误,而这比访问未初始化的指针更容易诊断。
指针赋值时要保证类型匹配
- 问题:指针的类型确定了指针所指向的内存数据类型。如果指针类型和它所指向的数据类型不匹配,可能导致程序运行时错误或内存损坏。
- 解决方法:在给指针赋值时,确保指针的类型与其指向的对象的类型匹配。
- 原因:指针的类型决定了内存访问的大小和方式,类型不匹配可能会导致数据错误和内存访问异常。
释放指针内存后要将该指针置空
- 问题:当使用 free() 释放动态分配的内存时,指针仍然持有释放前的地址,称为“悬挂指针”。访问这些指针可能导致程序崩溃或错误。
- 解决方法:在调用 free() 后,应将指针置为 NULL,避免使用已释放的内存。
- 原因:通过将指针设置为 NULL,可以避免后续的错误访问,NULL 指针可以通过简单的检查确保指针有效性。
指针分配成功后才可以使用,注意分配后的大小,不要越界
- 问题:指针在内存分配后,必须确保分配成功并且不会越界。访问超出分配内存范围的地址会导致程序崩溃或内存泄漏。
- 解决方法:在使用指针之前,始终检查内存分配是否成功,并确保操作不会越界。
- 原因:越界访问内存不仅会导致程序崩溃,还可能破坏内存中的其他数据,造成数据丢失或难以察觉的错误。
总结
NULL
,避免指向随机地址。1.32 初始化为0的全局变量在BSS还是Data?
全局变量和静态变量的存储方式取决于它们的初始化值。变量存储在不同的段(如BSS段和Data段)中,具体取决于其是否被初始化以及初始化的值。
BSS段
- BSS(Block Started by Symbol)段用于存放那些未初始化或初始化为0的全局变量和静态变量。
- 在程序加载时,操作系统或加载器会自动将BSS段中的变量初始化为0,以保证它们在程序启动时具有正确的初始值。
Data段
- Data段用于存放那些已初始化的全局变量和静态变量。这些变量在程序编译时就已经有了明确的初始化值。
- 已经明确赋非零值的全局变量会被存储在Data段中。
初始化为0的全局变量:BSS段
- 如果一个全局变量被初始化为0,那么它会被分配到BSS段。这些变量在程序加载时会被自动初始化为0,因为操作系统为BSS段分配的内存会预先设置为0。
在这个示例中,globalVar
被初始化为0,所以它将存储在BSS段中。
已初始化为非零值的全局变量:Data段
- 如果一个全局变量被显式初始化为非零值,它将被分配到Data段。
在这个示例中,globalVar
被初始化为10,它会被存储在Data段。
因此,初始化为0的全局变量会被存储在BSS段,而初始化为非零值的全局变量会被存储在Data段。
1.33 大端和小端
基本概念
这两种字节序的选择取决于计算机系统架构的约定。大部分个人电脑和服务器采用小端字节序,例如 x86 架构的计算机。而一些嵌入式系统和网络协议则常使用大端字节序。
1. 大端字节序:将多字节数据的高位字节存储在低地址位置,低位字节存储在高地址位置。类比:高位字节在前,低位字节在后。举例:整数值 0x12345678 在大端字节序中存储为 12 34 56 78,即高位字节 12 存储在低地址位置,低位字节 78 存储在高地址位置。
2. 小端字节序:将多字节数据的低位字节存储在低地址位置,高位字节存储在高地址位置。类比:低位字节在前,高位字节在后。举例:整数值 0x12345678 在小端字节序中存储为 78 56 34 12,即低位字节 78 存储在低地址位置,高位字节 12 存储在高地址位置。
识别大小端方法
定义变量int i=1;将 i 的地址拿到,强转成char*型,这时候就取到了 i 的低地址,这时候如果是1就是小端存储,如果是0就是大端存储。
1.34 (内存)堆和栈的区别
堆栈空间分配不同:
• 栈由操作系统自动进行分配和释放,用于存放函数的参数值、局部变量的值等,具有高效性。
• 堆一般由程序员手动进行分配和释放,效率比栈低很多。
堆栈缓存方式不同:
• 栈使用一级缓存,存储在处理器核心中,调用完成后立即释放,速度较快。
• 堆存储在二级缓存或主存中,速度相对较慢。
生长方向:
• 堆:堆的分配方向是向上的,即向地址较大的方向分配。当堆需要扩展时,会向高地址方向增长。
• 栈:栈的分配方向是向下的,即向地址较小的方向分配。当栈需要扩展时,会向低地址方向增长。
生命周期:
• 堆:堆上的内存在分配时并不会被立即释放,需要手动进行内存释放操作。堆上的数据可以在程序的任意位置进行访问,不受函数的调用关系限制。
• 栈:栈上的内存分配和释放是自动进行的,随着函数的调用和返回进行相应的操作。栈上的数据只在特定的作用域内有效,函数执行完成后会自动释放。
空间大小:
• 栈的空间大小一般较小,通常最多为2MB,超过则会报溢出错误。
• 堆的空间比较大,理论上可以接近3GB(对于32位程序来说)。
能否产生碎片:
• 栈操作遵循"后进先出"的原则,不会有内存块从栈中弹出,因此不会产生碎片。
• 堆是通过动态分配内存的方式进行分配和释放,频繁的申请和释放内存可能会引发内存碎片问题。
栈的大小通常是在程序启动时由操作系统分配的,并且可能在程序运行时动态调整(例如通过栈溢出保护机制)。它不是在编译时确定的。相对而言,堆的内存分配是动态的,可以在运行时调整。
1.35 在函数中申请堆内存注意点是什么?
当我们在函数中使用 malloc()
或其他内存分配函数分配堆内存时,必须特别小心内存管理问题。
避免内存泄漏
问题:
内存泄漏指的是分配的内存没有被释放,导致程序运行过程中不断消耗内存资源,最终可能导致系统崩溃。
解决方案:
确保在堆内存使用完后及时调用 free
(C语言)或 (C++)来释放内存,避免内存泄漏。
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
作者简介:仅用几个月时间0基础天坑急转嵌入式开发,逆袭成功拿下华为、vivo、小米等15个offer,面试经验100+,收藏20+面经,分享求职历程与学习心得。 专栏内容:这是一份覆盖嵌入式求职过程中99%问题指南,详细讲解了嵌入式开发的学习路径、项目经验分享、简历优化技巧、面试心得及实习经验,从技术面,HR面,AI面,主管面,谈薪一站式服务,助你突破技术瓶颈、打破信息差,争取更多大厂offer。