seo怎样才能优化网站,兰州怎么提高网站的排名,卫计网站建设工作计划,kratos wordpress面试大保健-C语言-变量day01
1. C语言的重要性
大家好#xff01;今天我们来聊一聊 C 语言。作为嵌入式开发的基础#xff0c;C语言在面试中必定是一个重点#xff0c;虽然具体会问到哪些问题不好预测#xff0c;但可以肯定的是#xff0c;基础知识绝对不会少问。所以今天我们来聊一聊 C 语言。作为嵌入式开发的基础C语言在面试中必定是一个重点虽然具体会问到哪些问题不好预测但可以肯定的是基础知识绝对不会少问。所以大家要特别注意不要在这些基础知识上出错。因为如果你答不对这些基础的东西面试官可能会觉得你对整个技术栈的掌握还不够。而如果你没能答对一些复杂的高级知识反倒会显得理解还不深但不至于大幅扣分。
2. C语言的编译流程
流程概览
在学习 C 语言时了解编译流程是非常重要的面试中也经常会问到这个问题。C 语言从源码到可执行文件的整个过程包括四个主要步骤
预处理编译汇编链接
详细过程
预处理 这一阶段处理了所有的预编译指令比如 #include 和 #define。在预处理过程中会把引入的头文件内容插入到当前的源文件中宏定义也会进行替换注释和多余的空格也会被去除最终生成一个干净的源代码。编译 这一阶段的任务是将源代码翻译成汇编代码生成 .asm 文件。汇编 汇编阶段将汇编代码转化为机器代码也就是二进制文件生成目标文件 .obj。链接 最后链接阶段会把多个目标文件和可能依赖的库文件链接成一个完整的可执行文件。这时我们就可以运行程序了。
编译流程总结
整个流程如图所示 #mermaid-svg-JpHRsv7DIcqWfQlt {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JpHRsv7DIcqWfQlt .error-icon{fill:#552222;}#mermaid-svg-JpHRsv7DIcqWfQlt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JpHRsv7DIcqWfQlt .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-JpHRsv7DIcqWfQlt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JpHRsv7DIcqWfQlt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JpHRsv7DIcqWfQlt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JpHRsv7DIcqWfQlt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JpHRsv7DIcqWfQlt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JpHRsv7DIcqWfQlt .marker.cross{stroke:#333333;}#mermaid-svg-JpHRsv7DIcqWfQlt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JpHRsv7DIcqWfQlt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JpHRsv7DIcqWfQlt .cluster-label text{fill:#333;}#mermaid-svg-JpHRsv7DIcqWfQlt .cluster-label span{color:#333;}#mermaid-svg-JpHRsv7DIcqWfQlt .label text,#mermaid-svg-JpHRsv7DIcqWfQlt span{fill:#333;color:#333;}#mermaid-svg-JpHRsv7DIcqWfQlt .node rect,#mermaid-svg-JpHRsv7DIcqWfQlt .node circle,#mermaid-svg-JpHRsv7DIcqWfQlt .node ellipse,#mermaid-svg-JpHRsv7DIcqWfQlt .node polygon,#mermaid-svg-JpHRsv7DIcqWfQlt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JpHRsv7DIcqWfQlt .node .label{text-align:center;}#mermaid-svg-JpHRsv7DIcqWfQlt .node.clickable{cursor:pointer;}#mermaid-svg-JpHRsv7DIcqWfQlt .arrowheadPath{fill:#333333;}#mermaid-svg-JpHRsv7DIcqWfQlt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JpHRsv7DIcqWfQlt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JpHRsv7DIcqWfQlt .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-JpHRsv7DIcqWfQlt .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-JpHRsv7DIcqWfQlt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JpHRsv7DIcqWfQlt .cluster text{fill:#333;}#mermaid-svg-JpHRsv7DIcqWfQlt .cluster span{color:#333;}#mermaid-svg-JpHRsv7DIcqWfQlt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JpHRsv7DIcqWfQlt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 源代码 预处理 编译 汇编 链接 可执行文件 确保你能流利地描述每个阶段的具体内容尤其是预处理阶段处理的内容不仅仅是名称更要知道其背后的作用。
3. C语言的内存模型
C语言程序的内存模型将程序在内存中划分为几个区域每个区域的功能不同具体如下
堆区 存放动态分配的内存通常通过 malloc 等函数来分配。栈区 存放局部变量和函数调用信息。静态区或数据区 存放全局变量和静态变量注意初始化过的全局变量和静态变量存放在一部分未初始化的则存放在另一部分。代码区 存放程序的执行代码也就是最终编译生成的机器码。
内存模型示意图 #mermaid-svg-2OORlYzSMp15g5H3 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-2OORlYzSMp15g5H3 .error-icon{fill:#552222;}#mermaid-svg-2OORlYzSMp15g5H3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2OORlYzSMp15g5H3 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-2OORlYzSMp15g5H3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2OORlYzSMp15g5H3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2OORlYzSMp15g5H3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2OORlYzSMp15g5H3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2OORlYzSMp15g5H3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2OORlYzSMp15g5H3 .marker.cross{stroke:#333333;}#mermaid-svg-2OORlYzSMp15g5H3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2OORlYzSMp15g5H3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2OORlYzSMp15g5H3 .cluster-label text{fill:#333;}#mermaid-svg-2OORlYzSMp15g5H3 .cluster-label span{color:#333;}#mermaid-svg-2OORlYzSMp15g5H3 .label text,#mermaid-svg-2OORlYzSMp15g5H3 span{fill:#333;color:#333;}#mermaid-svg-2OORlYzSMp15g5H3 .node rect,#mermaid-svg-2OORlYzSMp15g5H3 .node circle,#mermaid-svg-2OORlYzSMp15g5H3 .node ellipse,#mermaid-svg-2OORlYzSMp15g5H3 .node polygon,#mermaid-svg-2OORlYzSMp15g5H3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2OORlYzSMp15g5H3 .node .label{text-align:center;}#mermaid-svg-2OORlYzSMp15g5H3 .node.clickable{cursor:pointer;}#mermaid-svg-2OORlYzSMp15g5H3 .arrowheadPath{fill:#333333;}#mermaid-svg-2OORlYzSMp15g5H3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2OORlYzSMp15g5H3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2OORlYzSMp15g5H3 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-2OORlYzSMp15g5H3 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-2OORlYzSMp15g5H3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2OORlYzSMp15g5H3 .cluster text{fill:#333;}#mermaid-svg-2OORlYzSMp15g5H3 .cluster span{color:#333;}#mermaid-svg-2OORlYzSMp15g5H3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2OORlYzSMp15g5H3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 栈区 局部变量 堆区 动态分配内存 静态区 全局变量 代码区 程序代码 理解这些内存区域以及它们的作用非常重要。特别是在涉及到静态和全局变量时记住它们的存储位置以及生命周期。
4. 标识符和命名规范
标识符是用来命名变量、函数、数组等的名字。我们有两个层面的规范需要遵守
硬性规范 标识符由字母、数字和下划线组成但不能以数字开头。标识符不能是关键字。 命名习惯 一般有两种命名风格 下划线命名法使用下划线连接每个单词如 my_variable。驼峰命名法第一个单词小写后续单词首字母大写如 myVariable。
规范示意图 #mermaid-svg-29RxYEcmVeDthsYW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-29RxYEcmVeDthsYW .error-icon{fill:#552222;}#mermaid-svg-29RxYEcmVeDthsYW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-29RxYEcmVeDthsYW .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-29RxYEcmVeDthsYW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-29RxYEcmVeDthsYW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-29RxYEcmVeDthsYW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-29RxYEcmVeDthsYW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-29RxYEcmVeDthsYW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-29RxYEcmVeDthsYW .marker.cross{stroke:#333333;}#mermaid-svg-29RxYEcmVeDthsYW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-29RxYEcmVeDthsYW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-29RxYEcmVeDthsYW .cluster-label text{fill:#333;}#mermaid-svg-29RxYEcmVeDthsYW .cluster-label span{color:#333;}#mermaid-svg-29RxYEcmVeDthsYW .label text,#mermaid-svg-29RxYEcmVeDthsYW span{fill:#333;color:#333;}#mermaid-svg-29RxYEcmVeDthsYW .node rect,#mermaid-svg-29RxYEcmVeDthsYW .node circle,#mermaid-svg-29RxYEcmVeDthsYW .node ellipse,#mermaid-svg-29RxYEcmVeDthsYW .node polygon,#mermaid-svg-29RxYEcmVeDthsYW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-29RxYEcmVeDthsYW .node .label{text-align:center;}#mermaid-svg-29RxYEcmVeDthsYW .node.clickable{cursor:pointer;}#mermaid-svg-29RxYEcmVeDthsYW .arrowheadPath{fill:#333333;}#mermaid-svg-29RxYEcmVeDthsYW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-29RxYEcmVeDthsYW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-29RxYEcmVeDthsYW .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-29RxYEcmVeDthsYW .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-29RxYEcmVeDthsYW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-29RxYEcmVeDthsYW .cluster text{fill:#333;}#mermaid-svg-29RxYEcmVeDthsYW .cluster span{color:#333;}#mermaid-svg-29RxYEcmVeDthsYW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-29RxYEcmVeDthsYW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 标识符 字母 数字 下划线 不能以数字开头 命名规范 下划线命名 驼峰命名 牢记这些规范可以确保你的代码命名整洁且符合标准。
5. 变量和常量的区别
在 C 语言中变量和常量的区别主要体现在以下几个方面
是否可修改 变量的值可以在程序运行时修改。常量的值一旦被设定就不能再改变。 值的确定时间 变量的值在运行时决定。常量的值在编译时确定。 存储位置 变量的存储位置通常在栈区或堆区。常量的存储位置通常在数据区。 生命周期 变量的生命周期由其作用域决定。常量的生命周期通常与程序的生命周期一致。
变量与常量示意图 #mermaid-svg-JPE2GUVC6FUORY91 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JPE2GUVC6FUORY91 .error-icon{fill:#552222;}#mermaid-svg-JPE2GUVC6FUORY91 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JPE2GUVC6FUORY91 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-JPE2GUVC6FUORY91 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JPE2GUVC6FUORY91 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JPE2GUVC6FUORY91 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JPE2GUVC6FUORY91 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JPE2GUVC6FUORY91 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JPE2GUVC6FUORY91 .marker.cross{stroke:#333333;}#mermaid-svg-JPE2GUVC6FUORY91 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JPE2GUVC6FUORY91 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JPE2GUVC6FUORY91 .cluster-label text{fill:#333;}#mermaid-svg-JPE2GUVC6FUORY91 .cluster-label span{color:#333;}#mermaid-svg-JPE2GUVC6FUORY91 .label text,#mermaid-svg-JPE2GUVC6FUORY91 span{fill:#333;color:#333;}#mermaid-svg-JPE2GUVC6FUORY91 .node rect,#mermaid-svg-JPE2GUVC6FUORY91 .node circle,#mermaid-svg-JPE2GUVC6FUORY91 .node ellipse,#mermaid-svg-JPE2GUVC6FUORY91 .node polygon,#mermaid-svg-JPE2GUVC6FUORY91 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JPE2GUVC6FUORY91 .node .label{text-align:center;}#mermaid-svg-JPE2GUVC6FUORY91 .node.clickable{cursor:pointer;}#mermaid-svg-JPE2GUVC6FUORY91 .arrowheadPath{fill:#333333;}#mermaid-svg-JPE2GUVC6FUORY91 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JPE2GUVC6FUORY91 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JPE2GUVC6FUORY91 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-JPE2GUVC6FUORY91 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-JPE2GUVC6FUORY91 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JPE2GUVC6FUORY91 .cluster text{fill:#333;}#mermaid-svg-JPE2GUVC6FUORY91 .cluster span{color:#333;}#mermaid-svg-JPE2GUVC6FUORY91 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JPE2GUVC6FUORY91 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 变量 值可修改 值运行时确定 存储在栈区或堆区 常量 值不可修改 值编译时确定 存储在数据区 6. 常量定义的方式
在 C 语言中常量可以通过 #define 或 const 来定义。它们之间有以下区别
#define 预处理器指令进行文本替换无法进行类型检查。没有作用域限制常常替换全局范围内的所有相同名字。 const 具备类型检查和作用域控制更加安全。
定义常量示意图 #mermaid-svg-LihZkphxPxPIVELp {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-LihZkphxPxPIVELp .error-icon{fill:#552222;}#mermaid-svg-LihZkphxPxPIVELp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LihZkphxPxPIVELp .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-LihZkphxPxPIVELp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LihZkphxPxPIVELp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LihZkphxPxPIVELp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LihZkphxPxPIVELp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LihZkphxPxPIVELp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LihZkphxPxPIVELp .marker.cross{stroke:#333333;}#mermaid-svg-LihZkphxPxPIVELp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LihZkphxPxPIVELp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LihZkphxPxPIVELp .cluster-label text{fill:#333;}#mermaid-svg-LihZkphxPxPIVELp .cluster-label span{color:#333;}#mermaid-svg-LihZkphxPxPIVELp .label text,#mermaid-svg-LihZkphxPxPIVELp span{fill:#333;color:#333;}#mermaid-svg-LihZkphxPxPIVELp .node rect,#mermaid-svg-LihZkphxPxPIVELp .node circle,#mermaid-svg-LihZkphxPxPIVELp .node ellipse,#mermaid-svg-LihZkphxPxPIVELp .node polygon,#mermaid-svg-LihZkphxPxPIVELp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LihZkphxPxPIVELp .node .label{text-align:center;}#mermaid-svg-LihZkphxPxPIVELp .node.clickable{cursor:pointer;}#mermaid-svg-LihZkphxPxPIVELp .arrowheadPath{fill:#333333;}#mermaid-svg-LihZkphxPxPIVELp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LihZkphxPxPIVELp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LihZkphxPxPIVELp .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-LihZkphxPxPIVELp .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-LihZkphxPxPIVELp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LihZkphxPxPIVELp .cluster text{fill:#333;}#mermaid-svg-LihZkphxPxPIVELp .cluster span{color:#333;}#mermaid-svg-LihZkphxPxPIVELp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LihZkphxPxPIVELp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} #define 文本替换 无类型检查 const 类型检查 有作用域 根据实际情况选择合适的方式来定义常量。如果对类型安全有更高要求建议使用 const如果需要效率更高的全局替换可以使用 #define。
总结
今天我们复习了 C 语言的编译流程、内存模型、标识符命名规范、变量与常量的区别等内容。这些知识点不仅在日常编码中十分重要也是面试中常常出现的基础题目。希望大家能够牢记并在实际编码中灵活运用。如果你有任何疑问或需要进一步探讨的地方随时欢迎提问
面试大保健-C语言-宏day02
1. 宏的基础知识
大家好今天我们要聊的主题是宏。宏是 C 语言中非常重要的一部分它涉及到预处理指令广泛应用于代码优化、调试、跨平台开发等方面。可能有同学平时用得很多但突然让你列举出来几个常用的宏定义时可能就懵了。不要担心今天我们一起来复习一下。
常用宏指令
首先我们来看几个常见的预处理指令。你在日常编程中经常会用到的有
#include用来引入外部的头文件。#define用于定义宏也就是给常量、表达式等起一个别名。#if, #ifdef, #else, #endif这些是条件编译指令用来根据条件决定是否编译某段代码。#undef用来取消已经定义的宏。
宏定义
你可能常常用 #define 来定义一个宏变量。例如
#define PI 3.14159这个定义的作用就是编译器在编译过程中会把所有出现 PI 的地方替换为 3.14159。非常简单但是非常有用。
预处理的作用
宏定义的预处理操作是在编译过程开始之前进行的。这意味着在编译阶段所有的宏都已经被替换好了。所以宏指令的使用不仅能够提高效率还能够在一些特定情况下帮助我们控制程序的编译过程。
2. 条件编译
什么是条件编译
条件编译是指在编译过程中根据特定条件选择性地编译某些代码。这对于处理不同平台或调试代码非常有用。我们通常使用 #if, #ifdef, #else, #endif 等指令来实现条件编译。
举个例子
#ifdef DEBUGprintf(Debugging is enabled.\n);
#endif上面的代码只有在宏 DEBUG 被定义时才会被编译。这样我们就可以在开发阶段开启调试输出而在发布阶段关闭它从而减少不必要的代码。
条件编译的应用场景
功能开关在某些嵌入式系统中功能的开启或关闭通常通过宏来控制。例如使用 FreeRTOS 时我们可以通过定义宏来决定是否启用某些功能如信号量、队列等。这不仅能够使程序更加灵活还能够根据需要裁剪程序的大小。调试阶段的输出当我们在开发阶段进行调试时经常使用 printf 等输出调试信息。通过条件编译我们可以方便地控制这些调试信息的输出。例如
#define DEBUG
#ifdef DEBUGprintf(Debug message\n);
#endif当你不再需要调试信息时只需要去掉 #define DEBUG 这一行所有的调试信息就会消失。
跨平台开发在跨平台开发时不同的操作系统可能有不同的系统库和 API。通过条件编译我们可以根据不同的平台选择不同的代码路径。例如在 Windows 上使用某些函数在 Linux 上使用其他函数
#ifdef _WIN32// Windows specific code
#else// Linux specific code
#endif这样你就可以在同一代码库中管理多个平台的差异。
条件编译流程图 #mermaid-svg-V7sCRv6FDdhuqEau {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-V7sCRv6FDdhuqEau .error-icon{fill:#552222;}#mermaid-svg-V7sCRv6FDdhuqEau .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-V7sCRv6FDdhuqEau .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-V7sCRv6FDdhuqEau .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-V7sCRv6FDdhuqEau .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-V7sCRv6FDdhuqEau .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-V7sCRv6FDdhuqEau .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-V7sCRv6FDdhuqEau .marker{fill:#333333;stroke:#333333;}#mermaid-svg-V7sCRv6FDdhuqEau .marker.cross{stroke:#333333;}#mermaid-svg-V7sCRv6FDdhuqEau svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-V7sCRv6FDdhuqEau .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-V7sCRv6FDdhuqEau .cluster-label text{fill:#333;}#mermaid-svg-V7sCRv6FDdhuqEau .cluster-label span{color:#333;}#mermaid-svg-V7sCRv6FDdhuqEau .label text,#mermaid-svg-V7sCRv6FDdhuqEau span{fill:#333;color:#333;}#mermaid-svg-V7sCRv6FDdhuqEau .node rect,#mermaid-svg-V7sCRv6FDdhuqEau .node circle,#mermaid-svg-V7sCRv6FDdhuqEau .node ellipse,#mermaid-svg-V7sCRv6FDdhuqEau .node polygon,#mermaid-svg-V7sCRv6FDdhuqEau .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-V7sCRv6FDdhuqEau .node .label{text-align:center;}#mermaid-svg-V7sCRv6FDdhuqEau .node.clickable{cursor:pointer;}#mermaid-svg-V7sCRv6FDdhuqEau .arrowheadPath{fill:#333333;}#mermaid-svg-V7sCRv6FDdhuqEau .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-V7sCRv6FDdhuqEau .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-V7sCRv6FDdhuqEau .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-V7sCRv6FDdhuqEau .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-V7sCRv6FDdhuqEau .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-V7sCRv6FDdhuqEau .cluster text{fill:#333;}#mermaid-svg-V7sCRv6FDdhuqEau .cluster span{color:#333;}#mermaid-svg-V7sCRv6FDdhuqEau div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-V7sCRv6FDdhuqEau :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 定义宏 编译条件判断 选择性编译代码 生成最终代码 3. 宏的优势与挑战
优势
提高效率宏常常用于常量和代码块的替换避免了重复编写相同的代码提高了代码的复用性。代码简洁宏能够用简洁的方式定义复杂的操作比如常见的数学运算、数组操作等。条件编译帮助我们在不同环境或平台下选择性地编译代码极大地增强了代码的灵活性。
挑战
调试困难宏是通过文本替换的方式工作这意味着调试时你很难追踪宏的值和行为。这就要求我们在使用宏时要格外小心。命名冲突宏的命名容易与其他标识符冲突特别是在大型项目中容易造成一些难以发现的错误。为了避免这种情况建议使用有意义且独特的宏名称。
4. 宏和函数的区别
宏和函数虽然看起来相似但实际上它们有很多区别
替换方式宏是通过预处理器进行文本替换而函数是通过程序执行时调用的。参数传递宏的参数是直接插入到文本中因此没有类型检查而函数的参数有类型检查。计算结果宏在每次调用时都会重新计算而函数只会计算一次并返回结果。
示例
#define SQUARE(x) ((x) * (x))上面是一个宏定义它会在每次出现 SQUARE(x) 时进行文本替换。这就可能会带来副作用特别是当 x 是一个复杂表达式时
SQUARE(a b) // 宏展开后变为 ((a b) * (a b))这可能导致计算错误。为了避免这种情况我们通常会使用小括号确保表达式的正确性。
总结
宏是 C 语言中强大的工具可以在编译阶段进行文本替换和条件编译。它能帮助我们实现代码的灵活性和高效性尤其在嵌入式开发、调试和跨平台开发中有着广泛应用。虽然宏使用起来简单但也要注意避免宏带来的副作用如命名冲突、调试困难等问题。
希望今天的讲解能帮助你更好地理解宏的概念和应用记得在编程过程中熟练运用它让你的代码更加简洁、灵活
面试大保健-C语言-数据类型day03
1. C语言中的数据类型
大家好今天我们要学习 C 语言中的数据类型。C 语言的基本数据类型分为两大类整数类型和浮点类型。我们常见的整数类型包括 char、short、int、long 以及 long long而浮点类型有 float、double 和 long double。
整数类型
char通常用于存储字符占用1个字节。short较小的整数类型占用2个字节。int标准整数类型通常占用4个字节。long比 int 更大的整数类型占用4个字节具体取决于编译器和平台。long long更大的整数类型占用8个字节。
浮点类型
float用于存储单精度浮点数占用4个字节。double用于存储双精度浮点数占用8个字节。long double用于存储扩展精度浮点数通常占用16个字节。
注意事项
数据类型的大小实际上会因平台和编译器的不同而有所不同。例如32位和64位系统上的 int 类型可能会有所不同甚至某些特定平台如嵌入式系统上int 和 long 可能占用不同的字节数。
流程图C语言数据类型示意 #mermaid-svg-PRLKhnhTKFzrgEKo {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-PRLKhnhTKFzrgEKo .error-icon{fill:#552222;}#mermaid-svg-PRLKhnhTKFzrgEKo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PRLKhnhTKFzrgEKo .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-PRLKhnhTKFzrgEKo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PRLKhnhTKFzrgEKo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PRLKhnhTKFzrgEKo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PRLKhnhTKFzrgEKo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PRLKhnhTKFzrgEKo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PRLKhnhTKFzrgEKo .marker.cross{stroke:#333333;}#mermaid-svg-PRLKhnhTKFzrgEKo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PRLKhnhTKFzrgEKo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PRLKhnhTKFzrgEKo .cluster-label text{fill:#333;}#mermaid-svg-PRLKhnhTKFzrgEKo .cluster-label span{color:#333;}#mermaid-svg-PRLKhnhTKFzrgEKo .label text,#mermaid-svg-PRLKhnhTKFzrgEKo span{fill:#333;color:#333;}#mermaid-svg-PRLKhnhTKFzrgEKo .node rect,#mermaid-svg-PRLKhnhTKFzrgEKo .node circle,#mermaid-svg-PRLKhnhTKFzrgEKo .node ellipse,#mermaid-svg-PRLKhnhTKFzrgEKo .node polygon,#mermaid-svg-PRLKhnhTKFzrgEKo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PRLKhnhTKFzrgEKo .node .label{text-align:center;}#mermaid-svg-PRLKhnhTKFzrgEKo .node.clickable{cursor:pointer;}#mermaid-svg-PRLKhnhTKFzrgEKo .arrowheadPath{fill:#333333;}#mermaid-svg-PRLKhnhTKFzrgEKo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PRLKhnhTKFzrgEKo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PRLKhnhTKFzrgEKo .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-PRLKhnhTKFzrgEKo .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-PRLKhnhTKFzrgEKo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PRLKhnhTKFzrgEKo .cluster text{fill:#333;}#mermaid-svg-PRLKhnhTKFzrgEKo .cluster span{color:#333;}#mermaid-svg-PRLKhnhTKFzrgEKo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PRLKhnhTKFzrgEKo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 整数类型 char short int long long long 浮点类型 float double long double 2. 整数和浮点数的存储原理
整数的存储原理
整数在计算机内存中的存储采用二进制补码的形式。补码能够简化加法与减法操作因为正负数的运算可以统一使用同一套硬件电路而无需特别处理符号位。
正数存储
正数直接转换为二进制表示补码与原码相同。
负数存储
负数的存储过程包括三步
先将数字转换为二进制原码。然后取反得到反码。最后加1得到补码。
补码的使用使得加减法操作更为简便可以用相同的电路进行处理。
浮点数的存储原理
浮点数在内存中采用标准的科学计数法表示。每个浮点数被分为三个部分
符号位表示数字的正负。指数部分表示数字的幂次。尾数部分存储数字的有效位。
浮点数存储的最大挑战是如何高效地处理指数部分它采用了偏移量的方式存储。例如对于 float 类型指数部分使用8位存储偏移量为127而 double 类型使用11位存储偏移量为1023。
科学计数法存储示例
假设我们有浮点数 1.23 × 10^3它将被转换为标准的二进制科学计数法存储
尾数存储数字的有效部分小数部分。指数存储数字的幂次。
浮点数的存储流程图 #mermaid-svg-7eaNqgbtk4heXIPx {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-7eaNqgbtk4heXIPx .error-icon{fill:#552222;}#mermaid-svg-7eaNqgbtk4heXIPx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7eaNqgbtk4heXIPx .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-7eaNqgbtk4heXIPx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7eaNqgbtk4heXIPx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7eaNqgbtk4heXIPx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7eaNqgbtk4heXIPx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7eaNqgbtk4heXIPx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7eaNqgbtk4heXIPx .marker.cross{stroke:#333333;}#mermaid-svg-7eaNqgbtk4heXIPx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7eaNqgbtk4heXIPx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7eaNqgbtk4heXIPx .cluster-label text{fill:#333;}#mermaid-svg-7eaNqgbtk4heXIPx .cluster-label span{color:#333;}#mermaid-svg-7eaNqgbtk4heXIPx .label text,#mermaid-svg-7eaNqgbtk4heXIPx span{fill:#333;color:#333;}#mermaid-svg-7eaNqgbtk4heXIPx .node rect,#mermaid-svg-7eaNqgbtk4heXIPx .node circle,#mermaid-svg-7eaNqgbtk4heXIPx .node ellipse,#mermaid-svg-7eaNqgbtk4heXIPx .node polygon,#mermaid-svg-7eaNqgbtk4heXIPx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7eaNqgbtk4heXIPx .node .label{text-align:center;}#mermaid-svg-7eaNqgbtk4heXIPx .node.clickable{cursor:pointer;}#mermaid-svg-7eaNqgbtk4heXIPx .arrowheadPath{fill:#333333;}#mermaid-svg-7eaNqgbtk4heXIPx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7eaNqgbtk4heXIPx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7eaNqgbtk4heXIPx .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-7eaNqgbtk4heXIPx .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-7eaNqgbtk4heXIPx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7eaNqgbtk4heXIPx .cluster text{fill:#333;}#mermaid-svg-7eaNqgbtk4heXIPx .cluster span{color:#333;}#mermaid-svg-7eaNqgbtk4heXIPx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7eaNqgbtk4heXIPx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 浮点数存储 符号位 指数部分 尾数部分 8位/11位 存储小数部分 3. 数据类型自动转换规则
C语言支持数据类型之间的自动转换特别是在不同类型的运算中。转换遵循一定的规则
整型与整型运算较小的数据类型会转换为较大的数据类型。例如short 类型和 int 类型运算时short 会自动转换为 int。整型与浮点型运算整型会转换为浮点型进行运算结果也会是浮点型。浮点型与浮点型运算较小的浮点类型如 float会转换为较大的浮点类型如 double。
有符号与无符号数的转换
当有符号数与无符号数进行运算时有符号数会被转换为无符号数。这是因为无符号数不能表示负数。
自动转换流程图 #mermaid-svg-ujrQezKxVUuDpdKw {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ujrQezKxVUuDpdKw .error-icon{fill:#552222;}#mermaid-svg-ujrQezKxVUuDpdKw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ujrQezKxVUuDpdKw .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-ujrQezKxVUuDpdKw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ujrQezKxVUuDpdKw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ujrQezKxVUuDpdKw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ujrQezKxVUuDpdKw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ujrQezKxVUuDpdKw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ujrQezKxVUuDpdKw .marker.cross{stroke:#333333;}#mermaid-svg-ujrQezKxVUuDpdKw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ujrQezKxVUuDpdKw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ujrQezKxVUuDpdKw .cluster-label text{fill:#333;}#mermaid-svg-ujrQezKxVUuDpdKw .cluster-label span{color:#333;}#mermaid-svg-ujrQezKxVUuDpdKw .label text,#mermaid-svg-ujrQezKxVUuDpdKw span{fill:#333;color:#333;}#mermaid-svg-ujrQezKxVUuDpdKw .node rect,#mermaid-svg-ujrQezKxVUuDpdKw .node circle,#mermaid-svg-ujrQezKxVUuDpdKw .node ellipse,#mermaid-svg-ujrQezKxVUuDpdKw .node polygon,#mermaid-svg-ujrQezKxVUuDpdKw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ujrQezKxVUuDpdKw .node .label{text-align:center;}#mermaid-svg-ujrQezKxVUuDpdKw .node.clickable{cursor:pointer;}#mermaid-svg-ujrQezKxVUuDpdKw .arrowheadPath{fill:#333333;}#mermaid-svg-ujrQezKxVUuDpdKw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ujrQezKxVUuDpdKw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ujrQezKxVUuDpdKw .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-ujrQezKxVUuDpdKw .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-ujrQezKxVUuDpdKw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ujrQezKxVUuDpdKw .cluster text{fill:#333;}#mermaid-svg-ujrQezKxVUuDpdKw .cluster span{color:#333;}#mermaid-svg-ujrQezKxVUuDpdKw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ujrQezKxVUuDpdKw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 类型转换 整型与整型 整型与浮点型 浮点型与浮点型 整型转换为浮点型 较小类型转换为较大类型 4. 枚举类型的使用场景与优点
使用场景
状态机枚举类型非常适合用来表示状态机中的各种状态。例如在开发无人机项目时可以使用枚举来定义不同的状态如 IDLE、FLYING、LANDING 等。方法返回值在某些方法中返回值可能是多个预定义状态的其中之一这时枚举可以提高代码的可读性和安全性。
优点
提高代码可读性使用有意义的枚举值可以让代码更加清晰易懂。例如enum State { IDLE, FLYING, LANDING }比起用整数值 0, 1, 2 来表示状态要直观得多。类型安全枚举类型确保只有枚举值中定义的值可以作为状态传入避免了不合法值的出现从而提高代码的安全性。
流程图枚举类型的优势 #mermaid-svg-MeaM2o85GuPLpFFO {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-MeaM2o85GuPLpFFO .error-icon{fill:#552222;}#mermaid-svg-MeaM2o85GuPLpFFO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MeaM2o85GuPLpFFO .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-MeaM2o85GuPLpFFO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MeaM2o85GuPLpFFO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MeaM2o85GuPLpFFO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MeaM2o85GuPLpFFO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MeaM2o85GuPLpFFO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MeaM2o85GuPLpFFO .marker.cross{stroke:#333333;}#mermaid-svg-MeaM2o85GuPLpFFO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MeaM2o85GuPLpFFO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MeaM2o85GuPLpFFO .cluster-label text{fill:#333;}#mermaid-svg-MeaM2o85GuPLpFFO .cluster-label span{color:#333;}#mermaid-svg-MeaM2o85GuPLpFFO .label text,#mermaid-svg-MeaM2o85GuPLpFFO span{fill:#333;color:#333;}#mermaid-svg-MeaM2o85GuPLpFFO .node rect,#mermaid-svg-MeaM2o85GuPLpFFO .node circle,#mermaid-svg-MeaM2o85GuPLpFFO .node ellipse,#mermaid-svg-MeaM2o85GuPLpFFO .node polygon,#mermaid-svg-MeaM2o85GuPLpFFO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MeaM2o85GuPLpFFO .node .label{text-align:center;}#mermaid-svg-MeaM2o85GuPLpFFO .node.clickable{cursor:pointer;}#mermaid-svg-MeaM2o85GuPLpFFO .arrowheadPath{fill:#333333;}#mermaid-svg-MeaM2o85GuPLpFFO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MeaM2o85GuPLpFFO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MeaM2o85GuPLpFFO .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-MeaM2o85GuPLpFFO .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-MeaM2o85GuPLpFFO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MeaM2o85GuPLpFFO .cluster text{fill:#333;}#mermaid-svg-MeaM2o85GuPLpFFO .cluster span{color:#333;}#mermaid-svg-MeaM2o85GuPLpFFO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MeaM2o85GuPLpFFO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 枚举类型 提高可读性 增强类型安全 使用有意义的名字 只能使用定义的值 5. typedef与#define的区别
typedef 和 #define 都可以用来为类型创建别名但两者有明显的区别
typedef在编译时会进行类型检查并且支持作用域限制。它是C语言中的一种正式语法。#define在预处理阶段进行文本替换不能进行类型检查也没有作用域限制。
typedef 示例
typedef unsigned int uint;#define 示例
#define UINT unsigned inttypedef 更安全因为它会检查类型而 #define 只是简单的文本替换容易出现潜在的错误。
总结
今天我们复习了 C 语言中常见的数据类型包括整数类型和浮点类型并深入探讨了它们的存储原理、自动类型转换规则以及常用的 typedef 和 enum 类型。理解这些基本概念对于编写高效、安全的代码至关重要。
希望大家能够将这些知识点巩固并在日后的编程实践中灵活运用
面试大保健-C语言-数组day04
1. C语言数组的特点
大家好今天我们来讨论 C 语言中的数组。作为 C 语言的基础概念之一数组在面试中非常常见。对于数组你需要了解它的一些基本特点。
数组的基本特点
内存连续性 数组中的元素在内存中是连续排列的。也就是说数组占用的是一块连续的内存空间这对于快速访问数组元素非常有利。固定长度 一旦定义了数组的长度数组的长度是固定的无法在运行时动态改变。如果你在使用数组时发现原有长度不够用那么唯一的办法就是创建一个新的数组并将原数组的元素拷贝到新的数组中增加其长度。通过下标访问 数组中的元素可以通过下标直接访问访问速度非常快。编译器会根据数组的起始地址和元素的大小计算出具体元素的内存地址直接定位到该位置。
计算数组的长度
数组的长度可以通过 sizeof 运算符来计算。假设我们有一个整数数组 arr可以通过以下方式来获取数组的元素个数
sizeof(arr) / sizeof(int)这里 sizeof(arr) 获取整个数组占用的字节数sizeof(int) 获取每个元素的字节数通过二者相除就能得到数组的长度。
数组存储示意图 #mermaid-svg-0IyIQ2nlVvI5Kboq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-0IyIQ2nlVvI5Kboq .error-icon{fill:#552222;}#mermaid-svg-0IyIQ2nlVvI5Kboq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0IyIQ2nlVvI5Kboq .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-0IyIQ2nlVvI5Kboq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0IyIQ2nlVvI5Kboq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0IyIQ2nlVvI5Kboq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0IyIQ2nlVvI5Kboq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0IyIQ2nlVvI5Kboq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0IyIQ2nlVvI5Kboq .marker.cross{stroke:#333333;}#mermaid-svg-0IyIQ2nlVvI5Kboq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0IyIQ2nlVvI5Kboq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0IyIQ2nlVvI5Kboq .cluster-label text{fill:#333;}#mermaid-svg-0IyIQ2nlVvI5Kboq .cluster-label span{color:#333;}#mermaid-svg-0IyIQ2nlVvI5Kboq .label text,#mermaid-svg-0IyIQ2nlVvI5Kboq span{fill:#333;color:#333;}#mermaid-svg-0IyIQ2nlVvI5Kboq .node rect,#mermaid-svg-0IyIQ2nlVvI5Kboq .node circle,#mermaid-svg-0IyIQ2nlVvI5Kboq .node ellipse,#mermaid-svg-0IyIQ2nlVvI5Kboq .node polygon,#mermaid-svg-0IyIQ2nlVvI5Kboq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0IyIQ2nlVvI5Kboq .node .label{text-align:center;}#mermaid-svg-0IyIQ2nlVvI5Kboq .node.clickable{cursor:pointer;}#mermaid-svg-0IyIQ2nlVvI5Kboq .arrowheadPath{fill:#333333;}#mermaid-svg-0IyIQ2nlVvI5Kboq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0IyIQ2nlVvI5Kboq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0IyIQ2nlVvI5Kboq .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-0IyIQ2nlVvI5Kboq .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-0IyIQ2nlVvI5Kboq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0IyIQ2nlVvI5Kboq .cluster text{fill:#333;}#mermaid-svg-0IyIQ2nlVvI5Kboq .cluster span{color:#333;}#mermaid-svg-0IyIQ2nlVvI5Kboq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0IyIQ2nlVvI5Kboq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数组元素 内存连续存储 固定长度 通过下标访问 2. 字符串的本质
在 C 语言中字符串本质上是一个字符数组。每个字符串都是以一个特殊的字符 \0 结尾的这个字符被称为“空字符”它用于标记字符串的结束。
字符串为何需要结束标志
你可能会好奇为什么 C 语言中的字符串需要加一个结尾的 \0。这主要是为了在我们使用函数比如 printf时能够正确地判断字符串的结束位置。如果没有这个结尾标志程序就无法知道字符串的长度从而无法正确处理它。
字符数组赋值与字符串长度
如果你有一个字符串自变量 str hello world并将其赋值给一个字符数组C 编译器会自动在字符串的末尾加上一个 \0因此这个数组的长度会比字符串本身多一个字节。
示例sizeof 与 strlen
char str[] hello world;
printf(Sizeof str: %zu\n, sizeof(str)); // 12
printf(Strlen(str): %zu\n, strlen(str)); // 11这里sizeof(str) 返回的是数组的总字节数包括 \0而 strlen(str) 返回的是字符串的长度不包括结尾的 \0。
字符串存储示意图 #mermaid-svg-1JvMPdma8y9UUrU5 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1JvMPdma8y9UUrU5 .error-icon{fill:#552222;}#mermaid-svg-1JvMPdma8y9UUrU5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1JvMPdma8y9UUrU5 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-1JvMPdma8y9UUrU5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1JvMPdma8y9UUrU5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1JvMPdma8y9UUrU5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1JvMPdma8y9UUrU5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1JvMPdma8y9UUrU5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1JvMPdma8y9UUrU5 .marker.cross{stroke:#333333;}#mermaid-svg-1JvMPdma8y9UUrU5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1JvMPdma8y9UUrU5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1JvMPdma8y9UUrU5 .cluster-label text{fill:#333;}#mermaid-svg-1JvMPdma8y9UUrU5 .cluster-label span{color:#333;}#mermaid-svg-1JvMPdma8y9UUrU5 .label text,#mermaid-svg-1JvMPdma8y9UUrU5 span{fill:#333;color:#333;}#mermaid-svg-1JvMPdma8y9UUrU5 .node rect,#mermaid-svg-1JvMPdma8y9UUrU5 .node circle,#mermaid-svg-1JvMPdma8y9UUrU5 .node ellipse,#mermaid-svg-1JvMPdma8y9UUrU5 .node polygon,#mermaid-svg-1JvMPdma8y9UUrU5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1JvMPdma8y9UUrU5 .node .label{text-align:center;}#mermaid-svg-1JvMPdma8y9UUrU5 .node.clickable{cursor:pointer;}#mermaid-svg-1JvMPdma8y9UUrU5 .arrowheadPath{fill:#333333;}#mermaid-svg-1JvMPdma8y9UUrU5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1JvMPdma8y9UUrU5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1JvMPdma8y9UUrU5 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-1JvMPdma8y9UUrU5 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-1JvMPdma8y9UUrU5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1JvMPdma8y9UUrU5 .cluster text{fill:#333;}#mermaid-svg-1JvMPdma8y9UUrU5 .cluster span{color:#333;}#mermaid-svg-1JvMPdma8y9UUrU5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1JvMPdma8y9UUrU5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 字符串 字符数组 以\0结尾 正确判断字符串结束 3. 多维数组及其内存组织
多维数组是数组的一种扩展表示数组的每个元素本身还是一个数组。比如二维数组就是一个由多个一维数组构成的数组。
多维数组的存储形式
多维数组在内存中的存储仍然是连续的。举个例子假设我们有一个二维数组 int arr[3][4]它实际上就是一个含有 3 个元素每个元素是一个含有 4 个整数的一维数组。虽然它看起来像是一个二维数组但在内存中它被存储为一个连续的内存块。
示例二维数组的存储
假设我们定义一个二维数组 int arr[3][4]它在内存中的存储方式如下
arr[0][0] arr[0][1] arr[0][2] arr[0][3]
arr[1][0] arr[1][1] arr[1][2] arr[1][3]
arr[2][0] arr[2][1] arr[2][2] arr[2][3]内存中存储的顺序是arr[0][0], arr[0][1], arr[0][2], arr[0][3], arr[1][0], arr[1][1], arr[1][2], arr[1][3], arr[2][0], arr[2][1], arr[2][2], arr[2][3]。
示例代码二维数组
int arr[3][4] {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}
};for (int i 0; i 3; i) {for (int j 0; j 4; j) {printf(%d , arr[i][j]);}printf(\n);
}多维数组存储示意图
graph LRA[二维数组] -- B[连续存储]B -- C[存储顺序行优先]C -- D[内存连续访问]4. 数组与字符串的常见操作
在 C 语言中数组和字符串的操作是经常出现的任务。你应该熟练掌握以下几项基本操作
数组初始化数组必须在声明时初始化否则会造成未定义行为。字符串赋值通过字符数组来接收字符串字面量并记得加上 \0。数组访问数组通过下标来访问元素速度非常快但要确保不会越界。多维数组操作对于多维数组你可以通过嵌套循环来遍历所有元素。
通过理解这些基本概念你可以更有效地操作 C 语言中的数组和字符串。
总结
今天我们学习了 C 语言中的数组、字符串和多维数组。通过掌握它们的基本特点、内存组织形式以及常见操作你将能够更加高效地进行 C 语言编程特别是在面试中你会遇到很多与数组和字符串相关的题目。希望大家多加练习熟能生巧
面试大保健-C语言-指针day05
1. 指针的基本运算
大家好今天我们要深入了解 C 语言中的指针。指针是 C 语言中一个非常重要的概念掌握指针对理解底层内存操作和提高编程能力非常有帮助。让我们一步步解析指针的基本运算。
1.1. 两个同类型指针相减的结果
两个指针相减得到的结果是它们之间元素的个数而不是它们所指向的地址之间的差值。换句话说指针相减的结果是“元素的数量”而不是地址的字节差。
举个例子
int arr[] {10, 20, 30, 40};
int *ptr1 arr[0];
int *ptr2 arr[2];
printf(%ld\n, ptr2 - ptr1); // 输出2在这个例子中ptr2 - ptr1 结果为 2表示 ptr2 和 ptr1 之间相隔了两个 int 类型的元素。
1.2. 两个同类型指针相加
两个指针不能相加这是不允许的操作。指针相加是没有意义的因为它们指向的是内存中的特定位置而两个指针合并为一个指向某个位置的指针并没有实际意义。
1.3. 指针与整数相加减的结果
指针加减一个整数会得到指向新位置的指针。这个新位置是根据指针当前指向的元素类型大小来计算的。如果你加 1指针会指向下一个元素减 1则指向前一个元素。
举个例子
int arr[] {10, 20, 30};
int *ptr arr[0];
ptr; // 指向 arr[1]
printf(%d\n, *ptr); // 输出20在这个例子中ptr 让指针指向了数组中的下一个元素。
1.4. 指针比较大小
指针是可以进行比较大小的比较的是指针的地址值。比如你可以比较两个指针哪个指向的内存地址更大。
示例代码
int arr[] {10, 20, 30};
int *ptr1 arr[0];
int *ptr2 arr[2];
if (ptr2 ptr1) {printf(ptr2 指向的地址比 ptr1 大\n);
}这里的 ptr2 ptr1 比较的是它们指向的地址的大小。
指针运算流程图 #mermaid-svg-WyjxFF2y0b55Chf5 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-WyjxFF2y0b55Chf5 .error-icon{fill:#552222;}#mermaid-svg-WyjxFF2y0b55Chf5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WyjxFF2y0b55Chf5 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-WyjxFF2y0b55Chf5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WyjxFF2y0b55Chf5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WyjxFF2y0b55Chf5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WyjxFF2y0b55Chf5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WyjxFF2y0b55Chf5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WyjxFF2y0b55Chf5 .marker.cross{stroke:#333333;}#mermaid-svg-WyjxFF2y0b55Chf5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WyjxFF2y0b55Chf5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WyjxFF2y0b55Chf5 .cluster-label text{fill:#333;}#mermaid-svg-WyjxFF2y0b55Chf5 .cluster-label span{color:#333;}#mermaid-svg-WyjxFF2y0b55Chf5 .label text,#mermaid-svg-WyjxFF2y0b55Chf5 span{fill:#333;color:#333;}#mermaid-svg-WyjxFF2y0b55Chf5 .node rect,#mermaid-svg-WyjxFF2y0b55Chf5 .node circle,#mermaid-svg-WyjxFF2y0b55Chf5 .node ellipse,#mermaid-svg-WyjxFF2y0b55Chf5 .node polygon,#mermaid-svg-WyjxFF2y0b55Chf5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WyjxFF2y0b55Chf5 .node .label{text-align:center;}#mermaid-svg-WyjxFF2y0b55Chf5 .node.clickable{cursor:pointer;}#mermaid-svg-WyjxFF2y0b55Chf5 .arrowheadPath{fill:#333333;}#mermaid-svg-WyjxFF2y0b55Chf5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WyjxFF2y0b55Chf5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WyjxFF2y0b55Chf5 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-WyjxFF2y0b55Chf5 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-WyjxFF2y0b55Chf5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WyjxFF2y0b55Chf5 .cluster text{fill:#333;}#mermaid-svg-WyjxFF2y0b55Chf5 .cluster span{color:#333;}#mermaid-svg-WyjxFF2y0b55Chf5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WyjxFF2y0b55Chf5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 指针比较大小 得到元素的个数 不允许 指向新的内存位置 比较地址值 2. 数组名与指针的关系
2.1. 数组名是指针吗
数组名本质上是一个标识符它代表了数组实体的位置。在某些情况下数组名会被隐式地转换为指向第一个元素的指针。关键在于数组名和指针的区别数组名是数组的标识符而指针是一个变量存储了内存地址。
什么时候数组名表示整个数组
只有在使用 sizeof 计算数组大小时数组名表示的是整个数组。只有在使用取地址操作符 时数组名表示的是整个数组。
数组名转为指针的情况
在其他情况下例如数组名参与运算数组名会隐式地转换为指向第一个元素的指针。
2.2. 数组名与指针的比较
int arr[] {1, 2, 3};
int *p arr;
printf(%p\n, arr); // 输出数组起始地址
printf(%p\n, p); // 输出与 arr 相同的地址这两个输出是相同的因为 arr 在大多数情况下被当作指向第一个元素的指针。
2.3. 数组指针与指针数组的区别
数组指针指向整个数组的指针。数组指针的类型是“指向数组的指针”。指针数组是一个数组其中每个元素都是指针。指针数组的元素是指针类型。
示例
int *arr_ptr arr; // 数组指针
int *ptr_arr[3]; // 指针数组数组指针指向的是整个数组而指针数组则包含多个指针。
3. 常量指针与指针常量
3.1. 常量指针
常量指针是指针本身不可修改但可以修改它所指向的内容。换句话说常量指针的“指针”部分是常量。
int x 10;
int y 20;
int * const ptr x; // 常量指针ptr 不可修改
*ptr 20; // 可以修改 ptr 所指向的值3.2. 指针常量
指针常量是指指针本身不可修改但可以修改它所指向的内容。换句话说指针常量的“值”部分是常量。
int x 10;
int y 20;
const int *ptr x; // 指针常量可以修改 ptr但不能修改 ptr 所指向的内容
ptr y; // 可以修改指针 ptr
*ptr 30; // 错误不能修改 ptr 所指向的内容3.3. 常量指针与指针常量的区别
常量指针指针的地址不可以改变但可以修改它指向的内容。指针常量指针指向的内容不可改变但指针的地址可以改变。
3.4. 写法区别
int * const ptr x; // 常量指针
const int * ptr x; // 指针常量4. 空指针、悬空指针与野指针
4.1. 空指针
空指针是指一个未指向任何有效地址的指针。在 C 语言中空指针通常被初始化为 NULL。
int *ptr NULL;4.2. 悬空指针
悬空指针是指一个原本指向某块内存的指针在该内存被释放后指针仍然指向这块已经释放的内存变成了悬空指针。
4.3. 野指针
野指针是指一个没有被初始化或者指向非法地址的指针。野指针访问时可能会导致程序崩溃或未定义行为。
4.4. 如何避免这些问题
初始化指针始终将指针初始化为 NULL以避免空指针。释放内存后置空指针释放内存后及时将指针设为 NULL避免悬空指针。越界访问检查避免指针越界访问数组确保访问的地址是有效的。
总结
今天我们学习了 C 语言中的指针运算、指针和数组的关系、常量指针与指针常量、空指针、悬空指针与野指针等基础概念。指针是 C 语言中非常强大的工具掌握了指针的使用可以让你更深入理解内存管理和优化代码。
希望大家能够牢记这些关键点熟练运用指针提高编程能力
面试大保健-C语言-结构体day06
1. 结构体变量和指针访问成员的区别
大家好今天我们要聊聊结构体首先来看一下结构体变量和结构体指针访问成员的区别。你可能知道用结构体变量和结构体指针访问成员最大的不同就是符号的使用。
结构体变量使用点操作符 . 来访问成员。结构体指针使用箭头操作符 - 来访问成员。
举个例子
假设有如下结构体定义
struct Person {char name[20];int age;
};struct Person p1 {Alice, 25};
struct Person *p2 p1;通过结构体变量 p1 访问成员
printf(%s\n, p1.name); // 输出Alice
printf(%d\n, p1.age); // 输出25通过结构体指针 p2 访问成员
printf(%s\n, p2-name); // 输出Alice
printf(%d\n, p2-age); // 输出25总结
p1.name 使用点操作符直接访问成员。p2-name 使用箭头操作符通过指针访问成员。
结构体变量与指针的内存传递差异
当我们在函数参数中传递结构体时如果直接传递结构体变量它会进行值传递即复制结构体内容。而如果传递的是结构体指针它会进行引用传递即传递结构体的地址内存开销更小。
流程图结构体成员访问方式
graph LRA[结构体变量] -- B[使用点操作符访问成员]A[结构体指针] -- C[使用箭头操作符访问成员]B -- D[内存复制较大开销]C -- E[内存地址传递较小开销]2. 结构体的长度计算
2.1. 结构体的内存大小
结构体的长度计算并不简单它并不是将各个成员的大小直接相加。因为结构体成员的存储是受到字节对齐的影响的。
举个例子
struct Example {char a;short b;int c;
};直观上char 占 1 字节short 占 2 字节int 占 4 字节直接相加的话总共应该占 7 字节但实际上由于字节对齐的关系结构体的总大小是 12 字节。
2.2. 字节对齐的概念
字节对齐是为了让内存访问更加高效尤其是在 CPU 访问内存时它通常一次读取一个字节8 位。为了提高效率编译器会将变量存放在内存地址对齐到特定字节边界的位置。
对于 short 类型起始地址必须是 2 的倍数。对于 int 类型起始地址必须是 4 的倍数。
2.3. 如何计算结构体的大小
假设我们定义如下结构体
struct Example {char a;short b;int c;
};char a 占 1 字节short b 占 2 字节但为了满足字节对齐的要求b 会被放置到地址 2a 后的第一个空字节1 字节将会被跳过。int c 占 4 字节它会被放置在地址 44 字节的对齐要求。
最终结构体的大小为 12 字节而不是 7 字节。因为 short 和 int 都需要对齐。
2.4. 结构体内存对齐示意图 #mermaid-svg-Ld4Rmi5f1z8OFRAu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .error-icon{fill:#552222;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .marker.cross{stroke:#333333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .cluster-label text{fill:#333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .cluster-label span{color:#333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .label text,#mermaid-svg-Ld4Rmi5f1z8OFRAu span{fill:#333;color:#333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .node rect,#mermaid-svg-Ld4Rmi5f1z8OFRAu .node circle,#mermaid-svg-Ld4Rmi5f1z8OFRAu .node ellipse,#mermaid-svg-Ld4Rmi5f1z8OFRAu .node polygon,#mermaid-svg-Ld4Rmi5f1z8OFRAu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .node .label{text-align:center;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .node.clickable{cursor:pointer;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .arrowheadPath{fill:#333333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .cluster text{fill:#333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu .cluster span{color:#333;}#mermaid-svg-Ld4Rmi5f1z8OFRAu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ld4Rmi5f1z8OFRAu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} char a 1 byte short b 2 bytes int c 4 bytes Padding 1 byte Total size 12 bytes 3. 结构体与共用体的区别
3.1. 结构体和共用体的内存使用
结构体每个成员都有独立的内存空间它们的内存互不干涉。共用体所有成员共用同一块内存空间内存大小是所有成员中占用内存最大的一个。
举个例子
union Example {char a;int b;
};在这个共用体中a 和 b 会共享同一块内存内存的总大小是 int 所需的 4 字节而不是 char 的 1 字节。
3.2. 结构体与共用体内存示意图 #mermaid-svg-5sSkSGPVCzTSxVGV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-5sSkSGPVCzTSxVGV .error-icon{fill:#552222;}#mermaid-svg-5sSkSGPVCzTSxVGV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5sSkSGPVCzTSxVGV .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-5sSkSGPVCzTSxVGV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5sSkSGPVCzTSxVGV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5sSkSGPVCzTSxVGV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5sSkSGPVCzTSxVGV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5sSkSGPVCzTSxVGV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5sSkSGPVCzTSxVGV .marker.cross{stroke:#333333;}#mermaid-svg-5sSkSGPVCzTSxVGV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5sSkSGPVCzTSxVGV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5sSkSGPVCzTSxVGV .cluster-label text{fill:#333;}#mermaid-svg-5sSkSGPVCzTSxVGV .cluster-label span{color:#333;}#mermaid-svg-5sSkSGPVCzTSxVGV .label text,#mermaid-svg-5sSkSGPVCzTSxVGV span{fill:#333;color:#333;}#mermaid-svg-5sSkSGPVCzTSxVGV .node rect,#mermaid-svg-5sSkSGPVCzTSxVGV .node circle,#mermaid-svg-5sSkSGPVCzTSxVGV .node ellipse,#mermaid-svg-5sSkSGPVCzTSxVGV .node polygon,#mermaid-svg-5sSkSGPVCzTSxVGV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5sSkSGPVCzTSxVGV .node .label{text-align:center;}#mermaid-svg-5sSkSGPVCzTSxVGV .node.clickable{cursor:pointer;}#mermaid-svg-5sSkSGPVCzTSxVGV .arrowheadPath{fill:#333333;}#mermaid-svg-5sSkSGPVCzTSxVGV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5sSkSGPVCzTSxVGV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5sSkSGPVCzTSxVGV .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-5sSkSGPVCzTSxVGV .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-5sSkSGPVCzTSxVGV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5sSkSGPVCzTSxVGV .cluster text{fill:#333;}#mermaid-svg-5sSkSGPVCzTSxVGV .cluster span{color:#333;}#mermaid-svg-5sSkSGPVCzTSxVGV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5sSkSGPVCzTSxVGV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 结构体 成员独立内存 共用体 成员共用内存 每个成员分配独立内存 所有成员共用最大内存 4. 结构体属性的顺序和内存布局优化
4.1. 调整结构体成员顺序的影响
结构体的成员顺序会影响它的内存占用。通过合理安排成员顺序可以减少内存的浪费。比如将大小相似的成员放在一起以避免编译器插入不必要的填充字节。
举个例子
struct Example {char a; // 1 bytedouble b; // 8 bytesint c; // 4 bytes
};这个结构体会占用 16 字节其中可能会有一些填充字节。
4.2. 优化结构体内存布局
通过合理调整结构体成员的顺序我们可以让结构体的内存占用更加紧凑。例如将 int 和 double 放在一起避免中间出现不必要的空隙。
struct OptimizedExample {double b; // 8 bytesint c; // 4 byteschar a; // 1 byte
};这个优化后的结构体内存使用更加紧凑减少了不必要的填充字节。
4.3. 结构体内存优化示意图 #mermaid-svg-8TxmI3NsdypnJfnv {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-8TxmI3NsdypnJfnv .error-icon{fill:#552222;}#mermaid-svg-8TxmI3NsdypnJfnv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8TxmI3NsdypnJfnv .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-8TxmI3NsdypnJfnv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8TxmI3NsdypnJfnv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8TxmI3NsdypnJfnv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8TxmI3NsdypnJfnv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8TxmI3NsdypnJfnv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8TxmI3NsdypnJfnv .marker.cross{stroke:#333333;}#mermaid-svg-8TxmI3NsdypnJfnv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8TxmI3NsdypnJfnv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8TxmI3NsdypnJfnv .cluster-label text{fill:#333;}#mermaid-svg-8TxmI3NsdypnJfnv .cluster-label span{color:#333;}#mermaid-svg-8TxmI3NsdypnJfnv .label text,#mermaid-svg-8TxmI3NsdypnJfnv span{fill:#333;color:#333;}#mermaid-svg-8TxmI3NsdypnJfnv .node rect,#mermaid-svg-8TxmI3NsdypnJfnv .node circle,#mermaid-svg-8TxmI3NsdypnJfnv .node ellipse,#mermaid-svg-8TxmI3NsdypnJfnv .node polygon,#mermaid-svg-8TxmI3NsdypnJfnv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8TxmI3NsdypnJfnv .node .label{text-align:center;}#mermaid-svg-8TxmI3NsdypnJfnv .node.clickable{cursor:pointer;}#mermaid-svg-8TxmI3NsdypnJfnv .arrowheadPath{fill:#333333;}#mermaid-svg-8TxmI3NsdypnJfnv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8TxmI3NsdypnJfnv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8TxmI3NsdypnJfnv .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-8TxmI3NsdypnJfnv .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-8TxmI3NsdypnJfnv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8TxmI3NsdypnJfnv .cluster text{fill:#333;}#mermaid-svg-8TxmI3NsdypnJfnv .cluster span{color:#333;}#mermaid-svg-8TxmI3NsdypnJfnv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8TxmI3NsdypnJfnv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始顺序 浪费内存空间 优化顺序 紧凑内存分布 总结
今天我们学习了 C 语言中的结构体重点讲解了结构体成员的访问方式、内存计算、字节对齐、结构体与共用体的区别等。通过合理安排结构体成员的顺序可以有效减少内存占用从而提高程序的效率。
希望大家在实践中能够灵活运用结构体优化内存布局提升代码性能
面试大保健-C语言-函数day07
1. 形参与实参的区别
大家好今天我们要聊的是 C 语言中的函数。首先来看看形参和实参的区别。
形参是函数声明中在函数参数列表中声明的参数。它只是一个占位符表示函数将接收某种类型的值。实参是我们在调用函数时传入的实际参数它们会替代形参。
形参与实参的内存区别
形参在函数原型中只是声明它不会占用实际内存空间。只有当函数被调用时形参才会作为局部变量在栈中分配内存。
举个例子
假设我们有一个函数
void printSum(int a, int b) {printf(Sum: %d\n, a b);
}a 和 b 是形参它们只是声明并占位只有在函数调用时才会占用栈内存。当我们调用 printSum(3, 4); 时3 和 4 是实参它们传递给了形参 a 和 b。
2. 主函数的参数与返回值
主函数的参数和返回值规则根据应用场景会有所不同。在嵌入式开发中主函数通常没有参数和返回值而在系统应用层开发中主函数会有参数和返回值。
嵌入式开发
在嵌入式开发例如 STM32中main() 函数通常没有参数和返回值因为它直接作为程序入口执行不需要处理传入参数。
应用层开发
在应用层开发中main() 函数通常有参数和返回值。主函数的常见参数是命令行参数
int main(int argc, char *argv[]) {// argc 是参数的个数argv 是参数数组
}返回值通常是整数0 表示程序正常结束非零值表示出错。
返回值和参数的意义
返回值通常0 表示成功非零值表示错误。通过返回值可以告诉操作系统程序的执行状态。参数argc 是传入的参数个数argv 是一个字符串数组存储传入的命令行参数。
流程图主函数参数与返回值
graph LRA[主函数] -- B[没有参数和返回值嵌入式]A[主函数] -- C[有参数和返回值应用层开发]C -- D[argc参数个数]C -- E[argv参数数组]C -- F[返回值0表示成功]3. 函数原型
函数原型是函数声明它告诉编译器函数的返回类型、函数名和参数类型。函数原型不包含函数体。
举个例子
int add(int a, int b); // 函数原型这表示有一个名为 add 的函数返回类型是 int接受两个 int 类型的参数。
函数原型帮助编译器在调用函数时检查参数类型和返回值类型确保程序的正确性。
4. 常用的 C 语言系统函数
在 C 语言中有很多常用的系统函数特别是在处理输入输出、内存管理、字符串操作等方面。我们来列举几个常用的系统函数。
常用函数 输入输出 printf用于打印输出到标准输出通常是控制台。scanf用于从标准输入通常是键盘读取数据。 内存操作 malloc、calloc用于动态分配内存。free释放动态分配的内存。memcpy用于复制内存块。 字符串操作 strlen计算字符串长度。strcpy复制字符串。strcmp比较两个字符串。
示例代码
#include stdio.h
#include string.hint main() {char str[100];printf(Enter a string: );scanf(%s, str);printf(Length of the string: %lu\n, strlen(str));return 0;
}流程图常用系统函数 #mermaid-svg-cMYxdZkiWcNVE0Vu {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-cMYxdZkiWcNVE0Vu .error-icon{fill:#552222;}#mermaid-svg-cMYxdZkiWcNVE0Vu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cMYxdZkiWcNVE0Vu .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-cMYxdZkiWcNVE0Vu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cMYxdZkiWcNVE0Vu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cMYxdZkiWcNVE0Vu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cMYxdZkiWcNVE0Vu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cMYxdZkiWcNVE0Vu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cMYxdZkiWcNVE0Vu .marker.cross{stroke:#333333;}#mermaid-svg-cMYxdZkiWcNVE0Vu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cMYxdZkiWcNVE0Vu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cMYxdZkiWcNVE0Vu .cluster-label text{fill:#333;}#mermaid-svg-cMYxdZkiWcNVE0Vu .cluster-label span{color:#333;}#mermaid-svg-cMYxdZkiWcNVE0Vu .label text,#mermaid-svg-cMYxdZkiWcNVE0Vu span{fill:#333;color:#333;}#mermaid-svg-cMYxdZkiWcNVE0Vu .node rect,#mermaid-svg-cMYxdZkiWcNVE0Vu .node circle,#mermaid-svg-cMYxdZkiWcNVE0Vu .node ellipse,#mermaid-svg-cMYxdZkiWcNVE0Vu .node polygon,#mermaid-svg-cMYxdZkiWcNVE0Vu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cMYxdZkiWcNVE0Vu .node .label{text-align:center;}#mermaid-svg-cMYxdZkiWcNVE0Vu .node.clickable{cursor:pointer;}#mermaid-svg-cMYxdZkiWcNVE0Vu .arrowheadPath{fill:#333333;}#mermaid-svg-cMYxdZkiWcNVE0Vu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cMYxdZkiWcNVE0Vu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cMYxdZkiWcNVE0Vu .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-cMYxdZkiWcNVE0Vu .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-cMYxdZkiWcNVE0Vu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cMYxdZkiWcNVE0Vu .cluster text{fill:#333;}#mermaid-svg-cMYxdZkiWcNVE0Vu .cluster span{color:#333;}#mermaid-svg-cMYxdZkiWcNVE0Vu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cMYxdZkiWcNVE0Vu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 常用函数 输入输出 内存操作 字符串操作 printf scanf malloc free strlen strcpy 5. 传递指针与传递值的区别
在 C 语言中函数参数的传递有两种方式按值传递和按指针传递。这两者的主要区别在于是否改变原始数据。
传值
按值传递当你将变量传递给函数时函数接收到的是变量的副本函数内的改变不会影响原变量。适用于数据量小、需要保护原始数据不被修改的情况。
void increment(int a) {a a 1; // 只是改变了副本不会影响原始变量
}int main() {int x 10;increment(x);printf(%d\n, x); // 输出10return 0;
}传指针
按指针传递当你传递变量的地址给函数时函数直接修改原变量的值。适用于数据量大的结构体或数组能够节省内存并允许函数修改原始数据。
void increment(int *a) {*a *a 1; // 直接修改原始变量的值
}int main() {int x 10;increment(x);printf(%d\n, x); // 输出11return 0;
}流程图传递指针与值的区别 #mermaid-svg-Nqt9lizYmiigca4u {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Nqt9lizYmiigca4u .error-icon{fill:#552222;}#mermaid-svg-Nqt9lizYmiigca4u .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Nqt9lizYmiigca4u .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Nqt9lizYmiigca4u .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Nqt9lizYmiigca4u .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Nqt9lizYmiigca4u .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Nqt9lizYmiigca4u .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Nqt9lizYmiigca4u .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Nqt9lizYmiigca4u .marker.cross{stroke:#333333;}#mermaid-svg-Nqt9lizYmiigca4u svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Nqt9lizYmiigca4u .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Nqt9lizYmiigca4u .cluster-label text{fill:#333;}#mermaid-svg-Nqt9lizYmiigca4u .cluster-label span{color:#333;}#mermaid-svg-Nqt9lizYmiigca4u .label text,#mermaid-svg-Nqt9lizYmiigca4u span{fill:#333;color:#333;}#mermaid-svg-Nqt9lizYmiigca4u .node rect,#mermaid-svg-Nqt9lizYmiigca4u .node circle,#mermaid-svg-Nqt9lizYmiigca4u .node ellipse,#mermaid-svg-Nqt9lizYmiigca4u .node polygon,#mermaid-svg-Nqt9lizYmiigca4u .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Nqt9lizYmiigca4u .node .label{text-align:center;}#mermaid-svg-Nqt9lizYmiigca4u .node.clickable{cursor:pointer;}#mermaid-svg-Nqt9lizYmiigca4u .arrowheadPath{fill:#333333;}#mermaid-svg-Nqt9lizYmiigca4u .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Nqt9lizYmiigca4u .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Nqt9lizYmiigca4u .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Nqt9lizYmiigca4u .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Nqt9lizYmiigca4u .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Nqt9lizYmiigca4u .cluster text{fill:#333;}#mermaid-svg-Nqt9lizYmiigca4u .cluster span{color:#333;}#mermaid-svg-Nqt9lizYmiigca4u div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Nqt9lizYmiigca4u :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 传递方式 按值传递 按指针传递 不修改原始数据 修改原始数据 6. 回调函数
回调函数是一个通过函数指针传递给其他函数的函数。当其他函数完成特定任务时它会调用这个回调函数。
回调函数的应用
回调函数的最大好处是解耦。我们将处理逻辑与调用逻辑分开使得代码更加灵活。回调函数常常用于事件驱动编程中比如 GUI 程序或者网络请求。
示例使用回调函数
#include stdio.hvoid myCallback() {printf(Callback function called\n);
}void executeCallback(void (*callback)()) {printf(Executing callback...\n);callback();
}int main() {executeCallback(myCallback); // 传递回调函数return 0;
}流程图回调函数的使用 #mermaid-svg-kbrfFtNGYixFUpCr {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-kbrfFtNGYixFUpCr .error-icon{fill:#552222;}#mermaid-svg-kbrfFtNGYixFUpCr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kbrfFtNGYixFUpCr .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-kbrfFtNGYixFUpCr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kbrfFtNGYixFUpCr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kbrfFtNGYixFUpCr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kbrfFtNGYixFUpCr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kbrfFtNGYixFUpCr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kbrfFtNGYixFUpCr .marker.cross{stroke:#333333;}#mermaid-svg-kbrfFtNGYixFUpCr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kbrfFtNGYixFUpCr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kbrfFtNGYixFUpCr .cluster-label text{fill:#333;}#mermaid-svg-kbrfFtNGYixFUpCr .cluster-label span{color:#333;}#mermaid-svg-kbrfFtNGYixFUpCr .label text,#mermaid-svg-kbrfFtNGYixFUpCr span{fill:#333;color:#333;}#mermaid-svg-kbrfFtNGYixFUpCr .node rect,#mermaid-svg-kbrfFtNGYixFUpCr .node circle,#mermaid-svg-kbrfFtNGYixFUpCr .node ellipse,#mermaid-svg-kbrfFtNGYixFUpCr .node polygon,#mermaid-svg-kbrfFtNGYixFUpCr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kbrfFtNGYixFUpCr .node .label{text-align:center;}#mermaid-svg-kbrfFtNGYixFUpCr .node.clickable{cursor:pointer;}#mermaid-svg-kbrfFtNGYixFUpCr .arrowheadPath{fill:#333333;}#mermaid-svg-kbrfFtNGYixFUpCr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kbrfFtNGYixFUpCr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kbrfFtNGYixFUpCr .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-kbrfFtNGYixFUpCr .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-kbrfFtNGYixFUpCr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kbrfFtNGYixFUpCr .cluster text{fill:#333;}#mermaid-svg-kbrfFtNGYixFUpCr .cluster span{color:#333;}#mermaid-svg-kbrfFtNGYixFUpCr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kbrfFtNGYixFUpCr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 主程序 调用第三方库函数 传递回调函数 第三方库执行回调 执行用户定义的处理逻辑 总结
今天我们学习了 C 语言中的函数包括形参与实参的区别、主函数的参数与返回值、常用系统函数、传递指针与值的区别以及回调函数的应用。希望大家在实际编码中能灵活运用这些知识提高代码的可维护性和执行效率。
面试大保健-C语言-关键字day08
1. static 关键字的使用
大家好今天我们要讨论的是 C 语言中的一些重要关键字首先来看 static。这个关键字在 C 语言中有多个使用场景具体来说它用在变量上和函数上有不同的含义。我们先从变量开始说起。
1.1. static 用于变量 局部静态变量当 static 用在局部变量前时表示该变量的生命周期不再是局部的而是程序的整个生命周期。换句话说静态局部变量在整个程序运行期间存在但它的作用域依然局限于定义它的函数。 void func() {static int count 0;count;printf(%d\n, count);
}每次调用 func() 时count 的值会累加而不会重新初始化为 0。虽然它是局部变量但它的生命周期持续到程序结束。 全局静态变量当 static 用于全局变量时表示该变量只能在当前文件内访问外部文件无法引用它起到了封装作用。 static int global_var 100;这里的 global_var 仅在当前源文件中有效外部无法访问。
1.2. static 用于函数 当 static 用于函数时意味着该函数的作用域也被限制在当前文件内外部文件无法调用此函数。这样做通常是为了模块化代码只让函数在文件内部使用避免外部干扰。 static void helper_function() {printf(This function is only accessible within this file.\n);
}这个 helper_function() 只能在当前源文件内使用其他文件无法调用它。
2. extern 关键字
extern 是另一个重要的关键字它用于声明外部变量或函数。这个关键字告诉编译器在当前文件之外存在一个变量或函数编译器不需要为它分配内存空间。
2.1. extern 用于变量
当你在不同的文件之间共享数据时可以使用 extern 来声明一个在其他文件中定义的变量。
// file1.c
int x 5;// file2.c
extern int x;在 file2.c 中使用 extern 声明 x表示它在 file1.c 中已定义。编译时链接器会将这两个文件的 x 变量关联起来。
2.2. extern 用于函数
extern 也用于声明一个在其他文件中定义的函数。这通常用于跨文件调用函数。
// file1.c
void func() {printf(Hello from file1\n);
}// file2.c
extern void func();这里file2.c 可以调用 file1.c 中的 func()即使 func() 在 file2.c 中没有定义。
3. volatile 关键字
volatile 关键字告诉编译器不要对变量进行优化。它的主要作用是防止编译器进行缓存优化以保证每次访问变量时都从内存中读取数据。这对于多线程编程或者硬件寄存器的访问非常有用。
3.1. 禁止缓存优化
假设我们有一个变量它的值可能会被外部因素修改例如硬件中断、外部设备等在这种情况下如果编译器进行缓存优化可能会导致数据读取不及时无法获取最新值。
volatile int flag;这里flag 变量被声明为 volatile表示它的值可能会在程序执行过程中被外部改变因此每次访问 flag 时必须从内存中读取而不能从寄存器中缓存。
3.2. 应用场景
多任务系统在嵌入式系统中可能会有多个任务共享变量volatile 确保每次读取共享变量时都能获取最新值避免缓存错误。硬件寄存器访问当直接访问硬件寄存器时变量的值可能随时改变因此需要使用 volatile 来确保每次都从硬件读取。
3.3. 代码示例
volatile int counter;void ISR() {counter; // 假设在中断服务例程中修改 counter
}int main() {while (counter 10) {// 等待中断更新 counter}printf(Counter reached 10\n);return 0;
}在这个例子中counter 可能会被中断服务例程修改因此它必须被声明为 volatile否则编译器可能会优化掉对 counter 的读取导致程序无法正确运行。
总结
今天我们学习了 C 语言中的一些关键字static、extern 和 volatile。这些关键字在不同场景下有着重要作用
static用于控制变量和函数的作用域和生命周期。extern用于声明外部变量和函数帮助实现跨文件的数据共享。volatile用于禁用编译器的缓存优化确保变量每次都从内存中读取常用于多线程和硬件编程。
理解这些关键字能让你在编写高效、可靠的 C 语言代码时更好地控制内存和资源的使用。希望大家能够多加练习熟练掌握这些关键字的用法
面试大保健-C语言-内存-上day09
1. 是否能申请大于物理内存的内存
在面试中关于内存管理的问题是经常会问到的。一个常见的问题是“在一台 1GB 内存的计算机上能否申请 1.2GB 的内存”这个问题的答案并不是简单的“能”或“不能”我们需要根据不同的情况来分析。
1.1. 裸机开发
如果是裸机开发没有操作系统支持的情况下答案是“不能”。裸机环境没有操作系统的支持也就没有虚拟内存的功能。因此计算机的内存分配会受到物理内存的限制。
1.2. 操作系统环境
如果是在操作系统上开发情况就有所不同。我们需要根据是否支持虚拟内存来判断能否分配 1.2GB 的内存。操作系统如 Linux、Windows、macOS 等通常都支持虚拟内存这使得我们可以申请比物理内存更大的内存。
虚拟内存的作用
虚拟内存是由操作系统提供的一种功能它能够让程序认为自己拥有更多的内存而实际上操作系统会将部分数据暂存到硬盘上只有在需要时才从硬盘加载回内存。
1.3. 虚拟内存的作用
如果操作系统支持虚拟内存那么即使物理内存只有 1GB程序也可能成功申请到 1.2GB 的内存因为操作系统会使用硬盘空间来扩展虚拟内存。需要注意的是虚拟内存并不是物理内存的扩展它是通过硬盘交换区域如分页文件来模拟更多的内存空间。
流程图内存申请是否成功 #mermaid-svg-JPg6O1K4RaibgSNv {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JPg6O1K4RaibgSNv .error-icon{fill:#552222;}#mermaid-svg-JPg6O1K4RaibgSNv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JPg6O1K4RaibgSNv .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-JPg6O1K4RaibgSNv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JPg6O1K4RaibgSNv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JPg6O1K4RaibgSNv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JPg6O1K4RaibgSNv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JPg6O1K4RaibgSNv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JPg6O1K4RaibgSNv .marker.cross{stroke:#333333;}#mermaid-svg-JPg6O1K4RaibgSNv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JPg6O1K4RaibgSNv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JPg6O1K4RaibgSNv .cluster-label text{fill:#333;}#mermaid-svg-JPg6O1K4RaibgSNv .cluster-label span{color:#333;}#mermaid-svg-JPg6O1K4RaibgSNv .label text,#mermaid-svg-JPg6O1K4RaibgSNv span{fill:#333;color:#333;}#mermaid-svg-JPg6O1K4RaibgSNv .node rect,#mermaid-svg-JPg6O1K4RaibgSNv .node circle,#mermaid-svg-JPg6O1K4RaibgSNv .node ellipse,#mermaid-svg-JPg6O1K4RaibgSNv .node polygon,#mermaid-svg-JPg6O1K4RaibgSNv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JPg6O1K4RaibgSNv .node .label{text-align:center;}#mermaid-svg-JPg6O1K4RaibgSNv .node.clickable{cursor:pointer;}#mermaid-svg-JPg6O1K4RaibgSNv .arrowheadPath{fill:#333333;}#mermaid-svg-JPg6O1K4RaibgSNv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JPg6O1K4RaibgSNv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JPg6O1K4RaibgSNv .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-JPg6O1K4RaibgSNv .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-JPg6O1K4RaibgSNv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JPg6O1K4RaibgSNv .cluster text{fill:#333;}#mermaid-svg-JPg6O1K4RaibgSNv .cluster span{color:#333;}#mermaid-svg-JPg6O1K4RaibgSNv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JPg6O1K4RaibgSNv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 裸机开发 不能申请超过物理内存的空间 操作系统开发 虚拟内存支持 可以申请比物理内存更多的内存 内存分页管理 2. 虚拟内存的工作原理
2.1. 虚拟内存的管理
虚拟内存是通过**内存管理单元MMU**和操作系统共同协作来实现的。每个进程在执行时都有自己的虚拟地址空间虚拟地址空间从 0 开始这样每个进程都可以认为自己拥有独立的内存避免了进程之间的内存冲突。
举个例子
假设你有三个进程在运行每个进程都认为它自己有 1GB 的内存。实际上操作系统将这些虚拟地址映射到物理内存的不同部分。通过这种方式多个进程可以共享物理内存而不会相互干扰。
2.2. 虚拟内存的优势
简化应用层开发每个进程都有自己的虚拟内存空间开发者不需要担心其他进程是否占用了同一块内存。扩展内存容量通过虚拟内存操作系统能够使程序使用比物理内存更多的内存空间。
2.3. 如何实现虚拟内存
虚拟内存的实现依赖于分页机制操作系统将虚拟内存划分为固定大小的页并将这些虚拟页映射到物理内存中的不同页框。当程序访问某个虚拟地址时操作系统会通过页表找到对应的物理地址。
如果某个页面不在物理内存中操作系统会将其从硬盘中的交换空间加载到内存中这个过程被称为页面置换。
2.4. 虚拟内存的好处
隔离进程每个进程的虚拟内存是独立的进程间不会发生内存冲突。内存共享不同进程可以共享物理内存的某些部分但它们的虚拟内存空间保持独立。延迟加载操作系统可以根据需要将部分内存从物理内存移到硬盘从而节省内存空间。
2.5. 页面置换机制
当物理内存不够用时操作系统会选择一些不活跃的页面将它们从内存中移到硬盘腾出空间给新的进程。这种机制就是页面置换。当需要访问这些页面时操作系统会将它们从硬盘中读取回来。
流程图虚拟内存的工作原理 #mermaid-svg-zaewDMBRWKijw2rU {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-zaewDMBRWKijw2rU .error-icon{fill:#552222;}#mermaid-svg-zaewDMBRWKijw2rU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zaewDMBRWKijw2rU .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-zaewDMBRWKijw2rU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zaewDMBRWKijw2rU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zaewDMBRWKijw2rU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zaewDMBRWKijw2rU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zaewDMBRWKijw2rU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zaewDMBRWKijw2rU .marker.cross{stroke:#333333;}#mermaid-svg-zaewDMBRWKijw2rU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zaewDMBRWKijw2rU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zaewDMBRWKijw2rU .cluster-label text{fill:#333;}#mermaid-svg-zaewDMBRWKijw2rU .cluster-label span{color:#333;}#mermaid-svg-zaewDMBRWKijw2rU .label text,#mermaid-svg-zaewDMBRWKijw2rU span{fill:#333;color:#333;}#mermaid-svg-zaewDMBRWKijw2rU .node rect,#mermaid-svg-zaewDMBRWKijw2rU .node circle,#mermaid-svg-zaewDMBRWKijw2rU .node ellipse,#mermaid-svg-zaewDMBRWKijw2rU .node polygon,#mermaid-svg-zaewDMBRWKijw2rU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zaewDMBRWKijw2rU .node .label{text-align:center;}#mermaid-svg-zaewDMBRWKijw2rU .node.clickable{cursor:pointer;}#mermaid-svg-zaewDMBRWKijw2rU .arrowheadPath{fill:#333333;}#mermaid-svg-zaewDMBRWKijw2rU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zaewDMBRWKijw2rU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zaewDMBRWKijw2rU .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-zaewDMBRWKijw2rU .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-zaewDMBRWKijw2rU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zaewDMBRWKijw2rU .cluster text{fill:#333;}#mermaid-svg-zaewDMBRWKijw2rU .cluster span{color:#333;}#mermaid-svg-zaewDMBRWKijw2rU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zaewDMBRWKijw2rU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 每个进程的虚拟内存 操作系统映射虚拟内存到物理内存 页面置换机制 不活跃的页面移到硬盘 需要时从硬盘加载回内存 3. 虚拟内存的实际应用
3.1. 多任务处理
在操作系统中虚拟内存的使用让多个进程能够同时运行每个进程在自己的虚拟内存中运行操作系统通过调度和分页来管理这些进程保证它们不会相互干扰。
3.2. 嵌入式开发与虚拟内存
在嵌入式开发中特别是使用像 FreeRTOS 这样的轻量级操作系统时虚拟内存的支持并不像 Linux 那样完善。在这种情况下内存的管理往往由开发者自己控制物理内存和虚拟内存之间的映射更加直接。
3.3. 内存泄漏与虚拟内存
虚拟内存并不会防止内存泄漏。程序员仍然需要注意动态内存的分配和释放否则会导致内存泄漏即使虚拟内存存在也无法避免因为资源未释放导致的内存不足问题。
总结
今天我们讨论了关于内存的一些重要概念重点是虚拟内存。虚拟内存是操作系统提供的一种机制它可以让应用程序申请比物理内存更多的内存并且简化了多进程的内存管理。通过虚拟内存每个进程都拥有独立的虚拟地址空间避免了内存冲突并能更高效地利用系统资源。
了解这些概念对编写高效、可靠的程序非常重要尤其在多任务系统和嵌入式系统开发中虚拟内存的使用和管理将直接影响程序的性能和稳定性。
面试大保健-C语言-内存-下day10
1. 内存泄露与内存溢出
大家好今天我们继续讨论 C 语言中的内存管理问题特别是内存泄漏和内存溢出。首先了解这些概念对开发稳定的程序非常重要。
1.1. 什么是内存泄露
内存泄露指的是在程序运行过程中申请了内存空间但未能释放。这导致了内存的浪费最终可能导致系统内存耗尽。
内存泄漏的发生通常是因为程序员在动态内存分配后忘记释放内存或者失去对已分配内存的控制如通过错误的指针操作。
内存泄漏的常见问题是随着时间的推移内存泄漏越来越多最终可能导致系统的内存溢出。
1.2. 什么是内存溢出
内存溢出是指程序尝试使用超过操作系统为其分配的内存时发生的错误。内存溢出的常见原因有
内存泄漏导致内存不断增长最终超出了系统的内存限制。内存分配请求过大如果程序申请的内存量大于系统能够提供的物理内存内存溢出也会发生。
1.3. 如何解决内存泄漏和溢出 避免内存泄漏程序员必须确保在不再需要内存时及时释放内存。良好的内存管理和代码审查是避免内存泄漏的关键。 不用的时候free掉 解决内存溢出如果内存溢出是因为程序申请了过大的内存则需要优化内存使用或考虑使用更大的内存。硬件扩展或增加物理内存也是一种解决办法。 增大空间
流程图内存泄漏与溢出 #mermaid-svg-wO5hbCTyh1UZrATS {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-wO5hbCTyh1UZrATS .error-icon{fill:#552222;}#mermaid-svg-wO5hbCTyh1UZrATS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wO5hbCTyh1UZrATS .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-wO5hbCTyh1UZrATS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wO5hbCTyh1UZrATS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wO5hbCTyh1UZrATS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wO5hbCTyh1UZrATS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wO5hbCTyh1UZrATS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wO5hbCTyh1UZrATS .marker.cross{stroke:#333333;}#mermaid-svg-wO5hbCTyh1UZrATS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wO5hbCTyh1UZrATS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wO5hbCTyh1UZrATS .cluster-label text{fill:#333;}#mermaid-svg-wO5hbCTyh1UZrATS .cluster-label span{color:#333;}#mermaid-svg-wO5hbCTyh1UZrATS .label text,#mermaid-svg-wO5hbCTyh1UZrATS span{fill:#333;color:#333;}#mermaid-svg-wO5hbCTyh1UZrATS .node rect,#mermaid-svg-wO5hbCTyh1UZrATS .node circle,#mermaid-svg-wO5hbCTyh1UZrATS .node ellipse,#mermaid-svg-wO5hbCTyh1UZrATS .node polygon,#mermaid-svg-wO5hbCTyh1UZrATS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wO5hbCTyh1UZrATS .node .label{text-align:center;}#mermaid-svg-wO5hbCTyh1UZrATS .node.clickable{cursor:pointer;}#mermaid-svg-wO5hbCTyh1UZrATS .arrowheadPath{fill:#333333;}#mermaid-svg-wO5hbCTyh1UZrATS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wO5hbCTyh1UZrATS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wO5hbCTyh1UZrATS .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-wO5hbCTyh1UZrATS .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-wO5hbCTyh1UZrATS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wO5hbCTyh1UZrATS .cluster text{fill:#333;}#mermaid-svg-wO5hbCTyh1UZrATS .cluster span{color:#333;}#mermaid-svg-wO5hbCTyh1UZrATS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wO5hbCTyh1UZrATS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内存泄漏 内存未释放 内存浪费 系统内存耗尽 2. 堆与栈的区别
接下来我们讨论堆和栈的区别。堆和栈是程序中两种不同的内存分配区域它们各自有不同的特点和用途。
2.1. 管理方式
栈由系统或编译器自动管理内存。当函数被调用时局部变量被分配在栈上函数执行完毕后栈上的内存会自动释放。堆由程序员手动管理内存。程序员需要显式地申请和释放内存如果不及时释放就可能发生内存泄漏。
2.2. 访问效率
栈的访问效率较高栈的内存分配遵循后进先出LIFO的原则每次调用函数时只需在栈顶分配内存释放时也从栈顶弹出操作非常简单高效。堆的访问效率较低堆中的内存是动态分配的因此内存的分配和释放更复杂特别是多次分配和释放后可能会出现内存碎片化问题。
2.3. 分配方向
栈栈通常从高地址向低地址分配内存。 大小确定了, 所以从高地址向低地址分配 堆堆则通常从低地址向高地址分配内存。 内存空间分配, 大小不确定, 分配的时候才决定, 用到少, 分多少那种感觉
2.4. 举个例子
在栈中函数调用时局部变量依次入栈而在函数返回时局部变量出栈。
而在堆中内存分配是动态的需要手动管理。
int *p malloc(sizeof(int)); // 堆上分配内存
free(p); // 释放堆内存流程图堆与栈的区别 #mermaid-svg-PuUPnPUhMSGg24jK {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-PuUPnPUhMSGg24jK .error-icon{fill:#552222;}#mermaid-svg-PuUPnPUhMSGg24jK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PuUPnPUhMSGg24jK .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-PuUPnPUhMSGg24jK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PuUPnPUhMSGg24jK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PuUPnPUhMSGg24jK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PuUPnPUhMSGg24jK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PuUPnPUhMSGg24jK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PuUPnPUhMSGg24jK .marker.cross{stroke:#333333;}#mermaid-svg-PuUPnPUhMSGg24jK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PuUPnPUhMSGg24jK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PuUPnPUhMSGg24jK .cluster-label text{fill:#333;}#mermaid-svg-PuUPnPUhMSGg24jK .cluster-label span{color:#333;}#mermaid-svg-PuUPnPUhMSGg24jK .label text,#mermaid-svg-PuUPnPUhMSGg24jK span{fill:#333;color:#333;}#mermaid-svg-PuUPnPUhMSGg24jK .node rect,#mermaid-svg-PuUPnPUhMSGg24jK .node circle,#mermaid-svg-PuUPnPUhMSGg24jK .node ellipse,#mermaid-svg-PuUPnPUhMSGg24jK .node polygon,#mermaid-svg-PuUPnPUhMSGg24jK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PuUPnPUhMSGg24jK .node .label{text-align:center;}#mermaid-svg-PuUPnPUhMSGg24jK .node.clickable{cursor:pointer;}#mermaid-svg-PuUPnPUhMSGg24jK .arrowheadPath{fill:#333333;}#mermaid-svg-PuUPnPUhMSGg24jK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PuUPnPUhMSGg24jK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PuUPnPUhMSGg24jK .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-PuUPnPUhMSGg24jK .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-PuUPnPUhMSGg24jK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PuUPnPUhMSGg24jK .cluster text{fill:#333;}#mermaid-svg-PuUPnPUhMSGg24jK .cluster span{color:#333;}#mermaid-svg-PuUPnPUhMSGg24jK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PuUPnPUhMSGg24jK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 栈 系统自动管理 后进先出 高地址到低地址 堆 程序员手动管理 动态分配 低地址到高地址 3. 堆栈溢出
3.1. 栈溢出
栈溢出通常发生在递归调用过深或局部变量过大的情况下。由于栈内存是有限的如果函数调用层次太深栈空间被耗尽就会发生栈溢出。
函数调用层次太深如果递归调用太多每次调用都会在栈上分配一块内存最终可能导致栈空间耗尽。局部变量过大如果函数内声明了非常大的局部变量它们会占用较大的栈空间也可能导致栈溢出。
3.2. 堆溢出
堆溢出通常发生在内存泄漏或内存申请过大时。程序申请了超过系统可用物理内存的内存导致堆内存溢出。
内存泄漏如果程序在堆上申请了内存但没有释放那么随着程序运行堆内存将被耗尽最终导致堆溢出。申请过大的内存如果程序请求的内存超出了系统的内存限制也会发生堆溢出。
3.3. 解决方案
栈溢出优化递归调用避免过深的递归减少局部变量的大小避免占用过多栈空间。堆溢出确保释放申请的堆内存避免内存泄漏合理分配内存避免申请过大的内存。
流程图堆栈溢出的原因 #mermaid-svg-lr7lSfQOIophtbfj {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lr7lSfQOIophtbfj .error-icon{fill:#552222;}#mermaid-svg-lr7lSfQOIophtbfj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lr7lSfQOIophtbfj .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-lr7lSfQOIophtbfj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lr7lSfQOIophtbfj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lr7lSfQOIophtbfj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lr7lSfQOIophtbfj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lr7lSfQOIophtbfj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lr7lSfQOIophtbfj .marker.cross{stroke:#333333;}#mermaid-svg-lr7lSfQOIophtbfj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lr7lSfQOIophtbfj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lr7lSfQOIophtbfj .cluster-label text{fill:#333;}#mermaid-svg-lr7lSfQOIophtbfj .cluster-label span{color:#333;}#mermaid-svg-lr7lSfQOIophtbfj .label text,#mermaid-svg-lr7lSfQOIophtbfj span{fill:#333;color:#333;}#mermaid-svg-lr7lSfQOIophtbfj .node rect,#mermaid-svg-lr7lSfQOIophtbfj .node circle,#mermaid-svg-lr7lSfQOIophtbfj .node ellipse,#mermaid-svg-lr7lSfQOIophtbfj .node polygon,#mermaid-svg-lr7lSfQOIophtbfj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lr7lSfQOIophtbfj .node .label{text-align:center;}#mermaid-svg-lr7lSfQOIophtbfj .node.clickable{cursor:pointer;}#mermaid-svg-lr7lSfQOIophtbfj .arrowheadPath{fill:#333333;}#mermaid-svg-lr7lSfQOIophtbfj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lr7lSfQOIophtbfj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lr7lSfQOIophtbfj .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-lr7lSfQOIophtbfj .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-lr7lSfQOIophtbfj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lr7lSfQOIophtbfj .cluster text{fill:#333;}#mermaid-svg-lr7lSfQOIophtbfj .cluster span{color:#333;}#mermaid-svg-lr7lSfQOIophtbfj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lr7lSfQOIophtbfj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 栈溢出 递归深度过大 局部变量过大 堆溢出 内存泄漏 申请内存过大 4. 动态内存分配
在 C 语言中动态内存分配是通过一些标准库函数来实现的。最常用的函数有 malloc、calloc、realloc 和 free。
4.1. malloc 和 calloc malloc分配指定大小的内存返回一个指向该内存的指针。如果分配失败返回 NULL。 int *ptr malloc(sizeof(int) * 10); // 分配 10 个 int 大小的内存calloc分配指定数量的内存并初始化为 0。 int *ptr calloc(10, sizeof(int)); // 分配并初始化为 04.2. realloc 和 free realloc重新调整已分配内存的大小。如果新大小比原来大则会保留原来的数据并扩展内存空间。 ptr realloc(ptr, sizeof(int) * 20); // 调整内存大小free释放之前通过 malloc、calloc 或 realloc 分配的内存。 free(ptr); // 释放内存4.3. 注意事项
申请的内存必须在使用完毕后及时释放避免内存泄漏。避免频繁申请和释放小块内存可能会导致内存碎片化。
流程图动态内存管理 #mermaid-svg-qBOkMGhm5TRAUYOl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-qBOkMGhm5TRAUYOl .error-icon{fill:#552222;}#mermaid-svg-qBOkMGhm5TRAUYOl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qBOkMGhm5TRAUYOl .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-qBOkMGhm5TRAUYOl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qBOkMGhm5TRAUYOl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qBOkMGhm5TRAUYOl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qBOkMGhm5TRAUYOl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qBOkMGhm5TRAUYOl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qBOkMGhm5TRAUYOl .marker.cross{stroke:#333333;}#mermaid-svg-qBOkMGhm5TRAUYOl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qBOkMGhm5TRAUYOl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qBOkMGhm5TRAUYOl .cluster-label text{fill:#333;}#mermaid-svg-qBOkMGhm5TRAUYOl .cluster-label span{color:#333;}#mermaid-svg-qBOkMGhm5TRAUYOl .label text,#mermaid-svg-qBOkMGhm5TRAUYOl span{fill:#333;color:#333;}#mermaid-svg-qBOkMGhm5TRAUYOl .node rect,#mermaid-svg-qBOkMGhm5TRAUYOl .node circle,#mermaid-svg-qBOkMGhm5TRAUYOl .node ellipse,#mermaid-svg-qBOkMGhm5TRAUYOl .node polygon,#mermaid-svg-qBOkMGhm5TRAUYOl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qBOkMGhm5TRAUYOl .node .label{text-align:center;}#mermaid-svg-qBOkMGhm5TRAUYOl .node.clickable{cursor:pointer;}#mermaid-svg-qBOkMGhm5TRAUYOl .arrowheadPath{fill:#333333;}#mermaid-svg-qBOkMGhm5TRAUYOl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qBOkMGhm5TRAUYOl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qBOkMGhm5TRAUYOl .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-qBOkMGhm5TRAUYOl .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-qBOkMGhm5TRAUYOl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qBOkMGhm5TRAUYOl .cluster text{fill:#333;}#mermaid-svg-qBOkMGhm5TRAUYOl .cluster span{color:#333;}#mermaid-svg-qBOkMGhm5TRAUYOl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qBOkMGhm5TRAUYOl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内存申请 malloc/calloc realloc 内存释放 free 避免内存泄漏 总结
今天我们讨论了 C 语言中的内存管理问题包括内存泄漏、内存溢出、堆和栈的区别、堆栈溢出的原因以及动态内存分配的使用。掌握这些概念能够帮助我们在开发中避免常见的内存问题提升程序的稳定性和效率。希望大家在实际编程时能够灵活运用这些知识确保程序的内存管理正确无误