【维生素C语言】第十三章 - 动态内存管理

 🔥 CSDN 累计订阅量破千的火爆 C/C++ 教程的 2023 重制版,C 语言入门到实践的精品级趣味教程。
了解更多: 👉 "不太正经" 的专栏介绍 试读第一章
订阅链接: 🔗《C语言趣味教程》 ← 猛戳订阅!

分手后还打电话骚扰?ptr=NULL解决!动态内存管理【C语言】

前言:

本章将讲解C语言动态内存管理,由浅到深的讲解动态内存管理。学习完本章后可以做一下动态内存分配的练习加深巩固,降低踩动态内存分配坑的概率:

🚪 传送门:动态内存分配笔试题题目+答案+详解)


一、动态内存分配

0x00 引入

📚 目前我们已经掌握了以下两种开辟内存的方式:

// 在栈上开辟4个字节
int val = 20;

// 在栈空间上开辟10个字节的连续空间
char arr[10] = {0};

📚 上述开辟空间的方式有两个特点:

     ① 空间开辟的大小是固定的。

     ② 数组在声明时必须指定数组的长度,在编译时会开辟并分配其所需要的内存空间。

0x01 定义

🔍 [百度百科] 动态分配内存

所谓动态内存分配(Dynamic Memory Allocation) 就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

0x02 存在的原因

❓ 为什么会存在动态内存开辟?

💡 有时我们需要的空间大小在程序运行的时候才能知道,这时在数组编译时开辟空间的方式就不能满足了,这时我们就需要动态内存开辟来解决问题。

二、动态内存函数介绍

0x00 malloc 函数

📜 头文件:stdlib.h

 📚 介绍:malloc 是C语言提供的一个动态内存开辟的函数,该函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。具体情况如下:

      ① 如果开辟成功,则返回一个指向开辟好空间的指针。

      ② 如果开辟失败,则返回一个 NULL 指针。

      ③ 返回值的类型为 void*malloc 函数并不知道开辟空间的类型,由使用者自己决定。

      ④ 如果 size 为 0(开辟0个字节),malloc 的行为是标准未定义的,结果将取决于编译器。

🔍 官方介绍:malloc - C++ Reference

0x01 free 函数

📜 头文件:stdlib.h

 📚 介绍:free 函数用来释放动态开辟的内存空间。具体情况如下:

      ① 如果参数 ptr 指向的空间不是动态开辟的,那么 free 函数的行为是未定义的。

      ② 如果参数 ptrNULL 指针,那么 free 将不会执行任何动作。

📌 注意事项:

      ① 使用完之后一定要记得使用 free 函数释放所开辟的内存空间。

      ② 使用指针指向动态开辟的内存,使用完并 free 之后一定要记得将其置为空指针。

🔍 官方介绍:http://www.cplusplus.com/reference/cstdlib/malloc/?kw=free

💬 代码演示:动态内存开辟10个整型空间(完整步骤)

#include <stdio.h>
#include <stdlib.h>

int main(void) 
{
    // 假设开辟10个整型空间
    int arr[10]; // 在栈区上开辟

    // 动态内存开辟
    int* p = (int*)malloc(10*sizeof(int)); // 开辟10个大小为int的空间

    // 使用这些空间的时候
    if (p == NULL) {
        perror("main"); // main: 错误信息
        return 0;
    }
    
    // 使用
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i) = i;
    }
    for (i = 0; i < 10; i++) {
        printf("%d ", p[i]);
    }

    // 回收空间
    free(p);
    p = NULL; // 需要手动置为空指针

    return 0;
}

🚩  0 1 2 3 4 5 6 7 8 9

 ❗  动态内存开辟失败的情况:(perror 函数)

#include <stdio.h>
#include <stdlib.h>

int main(void) 
{
    ...
    int* p = (int*)malloc(9999999999*sizeof(int)); // 狮子大开口。拿来吧你
    ...

}

🚩   main: Not enough space

❓ 为什么 free 之后,一定要把 p 置为空指针?

🔑 解析:因为 free 之后那块开辟的内存空间已经不在了,它的功能只是把开辟的空间回收掉,但是 p 仍然还指向那块内存空间的起始位置,这合理吗?这不合理。所以我们需要使用 p = NULL 把他置成空指针。为了加深印象,举一个形象的例子:

❓ 为什么 malloc 前面要进行强制类型转换呢?

int* p = (int*)malloc(10*sizeof(int));

🔑 解析:为了和 int* p 类型相呼应,所以要进行强制类型转换。你可以试着把强转删掉,其实也不会有什么问题。但是因为有些编译器要求强转,所以最好进行一下强转,避免不必要的麻烦。

 0x02 calloc 函数

📜 头文件:stdlib.h

📚 介绍:calloc 函数的功能实为 num 个大小为 size 的元素开辟一块空间,并把空间的每个字节初始化为 0 ,返回一个指向它的指针。

⭕ 对比:

      ① malloc 只有一个参数,而 calloc 有两个参数,分别为元素的个数和元素的大小。

      ② 与函数 malloc 的区别在于 calloc 会在返回地址前把申请的空间的每个字节初始化为 0 。

🔍 官方介绍:http://www.cplusplus.com/reference/cstdlib/malloc/?kw=calloc

💬 验证: calloc 会对内存进行初始化

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // malloc
    int* p = (int*)malloc(40); // 开辟40个空间
    if (p == NULL)
        return 1;
    int i = 0;
    for (i = 0; i < 10; i++)
        printf("%d ", *(p + i));
    free(p);
    p = NULL;

    return 0;
}

 🚩  (运行结果是10个随机值)

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // calloc
    int* p = (int*)calloc(10, sizeof(int)); // 开辟10个大小为int的空间,40
    if (p == NULL)
        return 1;
    int i = 0;
    for (i = 0; i < 10; i++)
        printf("%d ", *(p + i));
    free(p);
    p = NULL;

    return 0;
}

 🚩  0 0 0 0 0 0 0 0 0 0

🔺 总结:说明 calloc 会对内存进行初始化,把空间的每个字节初始化为 0 。如果我们对于申请的内存空间的内容,要求其初始化,我们就可以使用 calloc 函数来轻松实现。

0x03 realloc 函数

📜 头文件:stdlib.h

📚 介绍:realloc 函数,让动态内存管理更加灵活。用于重新调整之前调用 malloccalloc 所分配的 ptr 所指向的内存块的大小,可以对动态开辟的内存进行大小的调整。具体介绍如下:

      ① ptr 为指针要调整的内存地址。

      ② size 为调整之后的新大小。

      ③ 返回值为调整之后的内存起始位置,请求失败则返回空指针。

      ④ realloc 函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

📌 realloc 函数在调整内存空间时存在的三种情况:

      情况1:原有空间之后有足够大的空间。

      情况2:原有空间之后没有足够大的空间。

      情况3realloc 有可能找不到合适的空间来调整大小。

情况1:当原有空间之后没有足够大的空间时,直接在原有内存之后直接追加空间,原来空间的数组不发生变化。

情况2:当原有空间之后没有足够大的空间时,会在堆空间上另找一个合适大小的连续的空间来使用。函数的返回值将是一个新的内存地址。

情况3:如果找不到合适的空间,就会返回一个空指针。

🔍 官方介绍:http://www.cplusplus.com/reference/cstdlib/malloc/?kw=realloc

💬 代码演示:realloc 调整内存大小

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL) {
        perror("main");
        return 1;
    }
    // 使用
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i)  = 5;
    }
    // 此时,这里需要p指向的空间更大,需要20个int的空间
    // realloc 调整空间
    p = (int*)realloc(p, 20*sizeof(int)); // 调整为20个int的大小的空间

    // 释放
    free(p);
    p = NULL;
}

 ❗  刚才提到的第三种情况,如果 realloc 找不到合适的空间,就会返回空指针。我们想让它增容,他却存在返回空指针的危险,这怎么行?

💡 解决方案:不要拿指针直接接收 realloc,可以使用临时指针判断一下。

#include <stdio.h>
#include <stdlib.h>

int main() 
{
    int* p = (int*)calloc(10, sizeof(int));
    if (p == NULL) {
        perror("main");
        return 1;
    }
    // 使用
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i)  = 5;
    }
    // 此时,这里需要 p 指向的空间更大,需要 20 个int的空间
    // realloc 调整空间
    int* ptmp = (int*)realloc(p, 20*sizeof(int));
    // 如果ptmp不等于空指针,再把p交付给它
    if (ptmp != NULL) {
        p = ptmp;
    }

    // 释放
    free(p);
    p = NULL;
}

 📚 有趣的是,其实你可以把 realloc 当 malloc 用:

// 在要调整的内存地址部分,传入NULL:
int* p = (int*)realloc(NULL, 40); // 这里功能类似于malloc,就是直接在堆区开辟40个字节

三、常见的动态内存错误

0x00 对空指针的解引用操作

❌ 代码演示:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int* p = (int*)malloc(9999999999);
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i) = i; // 对空指针进行解引用操作,非法访问内存
    }

    return 0;
}

💡 解决方案:对 malloc 函数的返回值做判空处理

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int* p = (int*)malloc(9999999999);
    // 对malloc函数的返回值做判空处理
    if (p == NULL) {
        perror("main")
        return 1;
    }
    int i = 0;
    for (i = 0; i < 10; i++) {
        *(p + i) = i; // 对空指针进行解引用操作,非法访问内存
    }

    return 0;
}

0x01 对动态开辟空间的越界访问

❌ 代码演示:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = (int*)malloc(10*sizeof(int)); // 申请10个整型的空间
    if (p == NULL) {
        perror("main");
        return 1;
    }
    int i = 0;
    // 越界访问 - 指针p只管理10个整型的空间,根本无法访问40个
    for (i = 0; i < 40; i++) {
        *(p + i) = i;
    }

    free(p);
    p = NULL;

    return 0;
}

💡 提醒:为了防止越界访问,使用空间时一定要注意开辟的空间大小。

0x02 对非动态开辟的内存使用free释放

❌ 代码演示:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int arr[10] = {0}; // 在栈区上开辟
    int* p = arr;
    // 使用  略

    free(p); // 使用free释放非动态开辟的空间
    p = NULL;

    return 0;   
}

 

 💡 提醒:不要对非动态开辟的内存使用 free,否则会出现难以意料的错误。

0x03 使用 free 释放一块动态开辟内存的一部分

❌ 代码演示:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = malloc(10*sizeof(int));
    if (p == NULL) {
        return 1;
    }
    int i = 0;
    for (i = 0; i < 5; i++) {
        *p++ = i; // p指向的空间被改变了
    }

    free(p);
    p = NULL;
  
    return 0;
}

📌 注意事项:这么写代码会导致 p 只释放了后面的空间。没人记得这块空间的起始位置,再也没有人找得到它了,这是很件很可怕的事情,会存在内存泄露的风险。

 💡 提醒:释放内存空间的时候一定要从头开始释放。

0x04 对同一块动态内存多次释放

❌ 代码演示:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int* p = malloc(10*sizeof(int));
    if (p == NULL) {
        return 1;
    }
    int i = 0;
    for (i = 0; i < 10; i++) {
        p[i] = i;
    }

    // 释放
    free(p);
    // 一时脑热,再一次释放
    free(p);
  
    return 0;
}

💡 解决方案:在第一次释放后紧接着将 p 置为空指针

// 释放
free(p);
p = NULL;

free(p); // 此时p为空,free什么也不做

0x05 动态开辟内存忘记释放导致内存泄漏

❌ 代码演示:

#include <stdio.h>
#include <stdlib.h>

void test()
{
    int* p = (int*)malloc(100);
    if (p == NULL) {
        return;
    }
    // 使用 略
    
    // 此时忘记释放了
}

int main()
{
    test();
    
    free(p); // 此时释放不了了,没人知道这块空间的起始位置在哪了
    p = NULL;
}

动态开辟的内存空间有两种回收方式:  1. 主动释放(free)      2. 程序结束

如果这块程序在服务器上 7x24 小时运行,如果你不主动释放或者你找不到这块空间了,最后就会导致内存泄漏问题。内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

💡 提醒:malloc 这一系列函数 和 free 一定要成对使用,记得及时释放。你自己申请的空间,用完之后不打算给别人用,就自己释放掉即可。如果你申请的空间,想传给别人使用,传给别人时一定要提醒别人用完之后记得释放。


参考资料:

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

比特科技. C语言进阶[EB/OL]. 2021[2021.8.31]. .

📌 本文作者: 王亦优

📃 更新记录: 2021.8.3

勘误记录:

💬 参考资料: 百度百科、比特科技、www.cplusplus.com、MSDN

📜 本文声明: 由于作者水平有限,本文有错误和不准确之处在所难免,本人也很想知道这些错误,恳望读者批评指正!

本章完。

  • 64
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
本书系统深入地介绍了各种代码优化编程技术。全书分为4章。第1章集中介绍如何确定程序中消耗CPU时钟最多的热点代码的所谓程序剖析技术以及典型部分工具的实用知识。第2,3章分别全面介绍RAM了系统与高速缓存子系统的代码优化知识。第4章主要介绍了机器代码优化技术。各章在讨论基本原理的同时详细给出了代码实例,并对优化性能进行了定量的分析。该书特别适合于作为应用程序员及系统程序员的学习与开发之用。同时,本书对在硬件方面的专业人员与技术工作者有一定的参考价值。 图书目录: 第1章程序剖分 1.1剖分的目标与目的 1.1.1总执行时间 1.1.2执行时间的类型 1.1.3处罚信息 1.1.4调用次数 1.1.5覆盖层次 1.2微剖分的基本问题 1.2.1流水作业或者吞吐量与等待时间 1.2.2测不准 1.2.3硬件优化 1.2.4低分辨率 1.3宏剖分的基本问题 1.3.1运行时间的不一致性 1.3.2二度运行问题 1.3.3负面效应 1.3.4单台机器的代码优化问题 1.4最新剖分软件概述 1.4.1IntelVTune 1.4.2AMDCodeAnalyst 1.4.3Microsoft的prOflle.exe 1.5开发自己的剖分软件 1.6VTune实用剖分知识 1.6.1第一步:删除prinff函数 1.6.2第二步:将strlen函数体移出循环 1.6.3第三步:对齐数据 1.6.4第四步:删除strlen函数 1.6.5第五步:删除除法操作 1.6.6第六步:删除性能监测代码 1.6.7第七步:函数组合 1.6.8第八步:减少内存访问操作的次数 1.6.9第九步:把VTune当做私人教练 1.6.10第十步:下结论 1.6.11结果与预测 第2章RAM子系统 2.1RAM概述 2.2RAM的层次结构 2.3随机存取存储器 2.4RAM的设计与工作原理 2.4.1内核部分 2.4.2传统DRAM(页面模式的DRAM) 2.4.3DRAM的发展 2.4.4快速页面模式的DRAM(FPMDRAM) 2.4.5存储器时序 2.4.6扩展数据输出DRAM(EDODRAM) 2.4.7突发式EDODRAM(BEDODRAM) 2.4.8同步DRAM(SDRAM) 2.4.9倍速SDRAM(DDRSDRAM)或者SDRAMⅡ 2.4.10直接RambusDRAM(直接RDRAM) 2.4.11不同存储器类型的比较 2.5存储器与处理器之间的交互操作 2.5.1计算全存取时间 2.6DRAM物理地址到逻辑地址的映射 2.7内存优化操作 2.7.1建议 2.7.2展开循环 2.7.3消除数据相关性— 2.7.4数据并行处理 2.7.5优化引用数据结构 2.7.6减小数据结构的尺寸 2.7.7DRAM板块上的数据分布策略 2.7.8规划数据流 2.7.9按字节、双字与四字进行内存处理 2.7.10数据对齐 2.7.11内存访问与计算的组合 2.7.12读写操作的组合 2.7.13只在必要时才访问内存 2.7.14内置C内存处理函数的优化 2.7.15内存处理函数的优化质量 2.7.16C字符串库函数的优化 2.7.17字符串处理函数的质量优化 2.7.18块处理算法的优化 2.7.19大型数组排序的优化 2.8RAM测试问题 第3章高速缓存子系统 3.1SRAM的工作原理 3.1.1历史概况 3.1.2内核 3.1.3触发器的设计 3.1.4逻辑非元件(取反器)的设计 3.1.5SRAM阵列的设计 3.1.6封装接口的设计 3.1.7读写时序图 3.1.8静态存储器的类型 3.2高速缓存的工作原理+ 3.2.1起源 3.2.2高速缓存的目标与任务 3.2.3高速缓存的组织 3.3高速缓存与存储器存取的优化— 3.3.1处理数据的尺寸对性能的影响 3.3.2可执行代码的尺寸对性能的影响 3.3.3数据对齐效率 3.3.4数据在高速缓存板块上的分布 3.3.5使用有限联合数目的高速缓存 3.3.6维数组的处理 3.3.7写缓冲机制的详细说明 3.3.8新一代x86处理器的高速缓存管理 3.3.9预取机制的实际应用 3.3.10内存拷贝内幕或者PentiumIII与Pentium4的新命令 第4章机器优化 4.1C/C4++编译器的比较分析 4.1.1常量表达式 4.1.2代数表达式 4.1.3算术运算 4.1.4分支语句 4.1.5switch运算符 4.1.6循环 4.1.7函数调用 4.1.8变量分布 4.1.9字符串初始化 4.1.10死码 4.1.11常量条件 4.1.12确定优胜者 4.2汇编器与编译器的对决 4.2.1历史回顾——汇编语言使春天永驻 4.2.2评价机器优化质量的指标 4.2.3评价机器优化质量的方法 4.2.4对主要编译器进行比较分析 4.2.5测试结果的讨论 4.2.6机器优化质量的示例 4.2.7用汇编语言创建保护代码 4.2.8用汇编语言编程是一种创造性活动 4.2.9结束语 4.2.10源代码
目录 第1章 嵌入式系统基础知识 .1 1.1 嵌入式系统概述 1 1.1.1 嵌入式系统的发展史 2 1.1.2 嵌入式系统的定义与特点 3 1.1.3 嵌入式系统的特点 4 1.2 嵌入式系统的组成 5 1.2.1 嵌入式系统的硬件架构 6 1.2.2 嵌入式操作系统 9 1.2.3 嵌入式应用软件 11 1.3 arm处理器平台介绍 12 1.3.1 arm处理器简介 12 1.3.2 arm处理器系列 13 1.3.3 arm体系结构简介 17 1.3.4 s3c2410处理器简介 18 1.4 嵌入式系统硬件平台选型 22 1.4.1 硬件平台的选择 22 1.4.2 arm处理器选型 23 1.5 嵌入式系统开发概述 25 1.5.1 嵌入式系统开发流程 25 1.5.2 嵌入式软件开发流程 26 .本章小结 31 动手练练 31 第2章 嵌入式linux c语言开发工具 32 2.1 嵌入式linux下c语言概述 32 2.1.1 c语言简史 33 2.1.2 c语言特点 33 2.1.3 嵌入式linux c语言编程环境 34 2.2 嵌入式linux编辑器vi的使用 35 2.2.1 vi的基本模式 35 2.2.2 vi的基本操作 36 2.2.3 vi的使用实例分析 40 2.3 嵌入式linux编译器gcc的使用 41 2.3.1 gcc概述 41 2.3.2 gcc编译流程分析 42 2.3.3 gcc警告提示 45 2.3.4 gcc使用库函数 47 2.3.5 gcc代码优化 49 2.4 嵌入式linux调试器gdb的使用 49 2.4.1 gdb使用实例 50 2.4.2 设置/删除断点 53 2.4.3 数据相关命令 54 2.4.4 调试运行环境相关命令 55 2.4.5 堆栈相关命令 55 2.5 make工程管理器 55 2.5.1 makefile基本结构 56 2.5.2 makefile变量 58 2.5.3 makefile规则 61 2.5.4 make使用 62 2.6 emacs综合编辑器 63 2.6.1 emacs的启动与退出 63 2.6.2 emacs的基本编辑 64 2.6.3 emacs的c模式 66 2.6.4 emacs的shell模式 69 本章小结 70 动手练练 70 第3章 构建嵌入式linux系统 71 3.1 嵌入式系统开发环境的构建 71 3.1.1 嵌入式交叉编译环境搭建 71 3.1.2 minicom和超级终端配置及使用 76 3.1.3 宿主机服务配置 83 3.2 bootloader 87 3.2.1 bootloader的概念 88 3.2.2 bootloader启动流程分析 89 3.2.3 u-boot概述 89 3.2.4 u-boot源码导读 90 3.3 编译嵌入式linux内核 91 3.4 linux内核目录结构 95 3.5 制作文件系统 95 本章小结 97 动手练练 97 第4章 嵌入式linux c语言基础——数据、表达式 98 4.1 嵌入式linux c语言概述 98 4.2 基本数据类型 100 4.2.1 整型家族 100 4.2.2 实型家族 102 4.2.3 字符型家族 103 4.2.4 枚举家族 104 4.2.5 指针家族 105 4.3 变量与常量 107 4.3.1 变量的定义 107 4.3.2 typedef 113 4.3.3 常量定义 114 4.3.4 arm-linux基本数据类型综合应用实例 115 4.4 运算符与表达式 118 4.4.1 算术运算符和表达式 119 4.4.2 赋值运算符和表达式 121 4.4.3 逗号运算符和表达式 123 4.4.4 位运算符和表达式 124 4.4.5 关系运算符和表达式 126 4.4.6 逻辑运算符和表达式 127 4.4.7 sizeof操作符 129 4.4.8 条件(?)运算符 130 4.4.9 运算符优先级总结 131 4.4.10 arm-linux运算符 综合实例 133 本章小结 137 动手练练 137 第5章 嵌入式linux c语言基础——控制语句及函数 138 5.1 嵌入式linux c语言程序结构概述 138 5.1.1 嵌入式linux c语言3种程序结构 138 5.1.2 嵌入式linux c语言基本语句 139 5.2 选择语句 142 5.2.1 if语句 142 5.2.2 switch语句 145 5.2.3 arm-linux选择语句应用实例 147 5.3 循环语句 148 5.3.1 while和do-while语句 148 5.3.2 for循环语句 149 5.3.3 break和continue语句 151 5.3.4 arm-linux循环语句应用实例 152 5.4 goto语句 154 5.4.1 goto语句语法 154 5.4.2 arm-linux中goto语句应用实例 154 5.5 函数的定义与声明 155 5.5.1 c语言函数概述 155 5.5.2 函数定义 157 5.5.3 函数声明 157 5.5.4 arm-linux函数定义与声明实例 158 5.6 函数的参数、值和基本调用 160 5.6.1 函数的参数 160 5.6.2 函数的值 161 5.6.3 函数的基本调用 161 5.7 函数的嵌套、递归调用 162 5.7.1 函数的嵌套调用 162 5.7.2 函数的递归调用 162 5.7.3 arm-linux函数调用应用实例 165 本章小结 167 动手练练 ..167 第6章 嵌入式linux c语言基础——数组、指针与结构 168 6.1 数组 169 6.1.1 一维数组 169 6.1.2 字符串 172 6.1.3 二维数组 174 6.2 指针 175 6.2.1 指针的概念 175 6.2.2 指针变量的操作 177 6.2.3 指针和数组 184 6.2.4 指针高级议题 191 6.3 结构体与联合 196 6.3.1 结构体 196 6.3.2 联合 200 6.3.3 arm-linux指针、结构体使用实例 201 本章小结 203 动手练练 203 第7章 嵌入式linux c语言基础——高级议题 204 7.1 预处理 204 7.1.1 预处理的概念 204 7.1.2 预定义 205 7.1.3 文件包含 211 7.1.4 条件编译 212 7.2 c语言中的内存分配 214 7.2.1 c语言程序所占内存分类 214 7.2.2 堆和栈的区别 215 7.3 嵌入式linux可移植性考虑 216 7.3.1 字长和数据类型 216 7.3.2 数据对齐 218 7.3.3 字节顺序 218 7.4 c和汇编的接口 219 7.4.1 内嵌汇编的语法 219 7.4.2 编译器优化介绍 221 7.4.3 c语言关键字volatile 222 7.4.4 memory描述符 222 7.4.5 gcc对内嵌汇编语言的处理方式 223 本章小结 224 动手练练 224 第8章 嵌入式linux c语言基础——arm linux内核常见数据结构 225 8.1 链表 226 8.1.1 链表概述 226 8.1.2 单向链表 226 8.1.3 双向链表 233 8.1.4 循环链表 234 8.1.5 arm linux中链表使用实例 235 8.2 树、二叉树、平衡树 237 8.2.1 树 237 8.2.2 二叉树 238 8.2.3 平衡树 245 8.2.4 arm linux中红黑树使用实例 247 8.3 哈希表 249 8.3.1 哈希表的概念及作用 249 8.3.2 哈希表的构造方法 250 8.3.3 哈希表的处理冲突方法 252 8.3.4 arm linux中哈希表使用实例 253 本章小结 255 动手练练 255 第9章 文件i/o相关实例 256 9.1 linux系统调用及用户编程接口(api) 257 9.1.1 系统调用 257 9.1.2 用户编程接口(api) 257 9.1.3 系统命令 258 9.2 arm linux文件i/o系统概述 258 9.2.1 虚拟文件系统(vfs) 258 9.2.2 通用文件模型 259 9.2.3 arm linux的设备文件 264 9.3 文件i/o操作 265 9.3.1 不带缓存的文件i/o操作 265 9.3.2 标准i/o开发 276 9.4 嵌入式linux串口应用开发 279 9.4.1 串口概述 279 9.4.2 串口设置详解 280 9.4.3 串口使用详解 284 本章小结 287 动手练练 287 第10章 arm linux进程线程开发实例 288 10.1 arm linux进程线程管理 289 10.1.1 进程描述符及任务结构 289 10.1.2 进程的调度 291 10.1.3 linux中的线程 293 10.1.4 linux中进程间通信 293 10.2 arm linux进程控制相关api 294 10.3 arm linux进程间通信api 301 10.3.1 管道通信 301 10.3.2 信号通信 303 10.3.3 共享内存 308 10.3.4 消息队列 309 10.4 arm linux线程相关api 312 10.5 linux守护进程 317 10.5.1 守护进程概述 317 10.5.2 编写规则 318 10.5.3 守护进程实例 319 本章小结 321 动手练练 321 第11章 arm linux网络开发实例 322 11.1 tcp/ip协议简介 322 11.1.1 tcp/ip的分层模型 322 11.1.2 tcp/ip分层模型特点 324 11.1.3 tcp/ip核心协议 325 11.2 网络基础编程 328 11.2.1 socket概述 328 11.2.2 地址及顺序处理 328 11.2.3 socket基础编程 333 11.3 web服务器 339 11.3.1 web服务器功能 339 11.3.2 web服务器协议 341 11.3.3 web服务器协议 342 11.3.4 运行web服务器 347 11.4 traceroute程序实例 347 11.4.1 traceroute原理简介 347 11.4.2 traceroute实例与分析 348 11.4.3 traceroute实例运行结果 354 本章小结 354 动手练练 354 第12章 嵌入式linux设备驱动开发 355 12.1 设备驱动概述 355 12.1.1 设备驱动简介 355 12.1.2 设备驱动程序的特点 356 12.2 模块编程 357 12.2.1 模块编程简介 357 12.2.2 模块相关命令 357 12.2.3 模块编程流程 358 12.3 字符设备驱动编写 360 12.4 块设备驱动编写 369 12.4.1 块设备驱动程序描述符 369 12.4.2 块设备驱动编写流程 369 12.5 简单的skull驱动实例 375 12.5.1 驱动简介 375 12.5.2 驱动编写流程 376 12.5.3 结果分析 379 12.6 lcd驱动编写实例 379 12.6.1 lcd工作原理 379 12.6.2 lcd驱动实例 382 本章小结 389 动手练练 389 第13章 视频监控系统  390 13.1 视频监控系统概述 390 13.1.1 系统组成 390 13.1.2 音视频服务器 391 13.1.3 音视频客户端 392 13.1.4 通信传输控制协议 393 13.2 基本数据结构 395 13.3 功能实现 398 13.3.1 传输控制 398 13.3.2 用户检验 401 13.3.3 控制命令处理 403 13.3.4 云台转动控制 404 13.3.5 线程相关 407 本章小结 408 动手练练 ...408
C语言教程 C语言概述 7 C语言的发展过程 7 当代最优秀的程序设计语言 7 C语言版本 7 C语言的特点 7 面向对象的程序设计语言 8 C和C++ 8 简单的C程序介绍 8 输入和输出函数 9 C源程序的结构特点 10 书写程序时应遵循的规则 10 C语言的字符集 11 C语言词汇 11 Turbo C 2.0集成开发环境的使用 12 Turbo C 2.0简介和启动 12 Turbo C 2.0集成开发环境 13 File菜单 13 Edit菜单 14 Run菜单 15  Compile菜单 16 Project菜单 18 Options菜单 19 Debug菜单 23 Break/watch菜单 24 Turbo C 2.0的配置文件 24 程序的灵魂—算法 26 算法的概念 26 简单算法举例 26 算法的特性 29 怎样表示一个算法 29 用自然语言表示算法 29 用流程图表示算法 29 三种基本结构和改进的流程图 33 用N-S流程图表示算法 34 用伪代码表示算法 35 用计算机语言表示算法 35 结构化程序设计方法 36 数据类型、运算符与表达式 37 C语言的数据类型 37 常量与变量 39 常量和符号常量 39 变量 40 整型数据 40 整型常量的表示方法 40 整型变量 41 实型数据 44 实型常量的表示方法 44 实型变量 45 实型常数的类型 46 字符型数据 46 字符常量 46 转义字符 46 字符变量 47 字符数据在内存中的存储形式及使用方法 47 字符串常量 48 变量赋初值 48 各类数值型数据之间的混合运算 49 算术运算符和算术表达式 51 C运算符简介 51 算术运算符和算术表达式 51 赋值运算符和赋值表达式 53 逗号运算符和逗号表达式 55 小结 55 C的数据类型 55 基本类型的分类及特点 55 常量后缀 56 常量类型 56 数据类型转换 56 运算符优先级和结合性 56 表达式 56 最简单的C程序设计—顺序程序设计 57 C语句概述 57 赋值语句 58 数据输入输出的概念及在C语言中的实现 59 字符数据的输入输出 60 putchar 函数(字符输出函数) 60 getchar函数(键盘输入函数) 60 格式输入与输出 61 printf函数(格式输出函数) 61 scanf函数(格式输入函数) 63 顺序结构程序设计举例 67 分支结构程序 69 关系运算符和表达式 69 关系运算符及其优先次序 69 关系表达式 69 逻辑运算符和表达式 70 逻辑运算符极其优先次序 70 逻辑运算的值 70 逻辑表达式 71 if语句 71 if语句的三种形式 71 if语句的嵌套 75 条件运算符和条件表达式 77 switch语句 77 程序举例 79 循环控制 81 概述 81 goto语句以及用goto语句构成循环 81 while语句 81 do-while语句 83 for语句 86 循环的嵌套 88 几种循环的比较 88 break和continue语句 88 break语句 88 continue 语句 89 程序举例 90 数组 94 一维数组的定义和引用 94 一维数组的定义方式 94 一维数组元素的引用 95 一维数组的初始化 96 一维数组程序举例 97 二维数组的定义和引用 98 二维数组的定义 98 二维数组元素的引用 98 二维数组的初始化 99 二维数组程序举例 101 字符数组 101 字符数组的定义 101 字符数组的初始化 101 字符数组的引用 101 字符串和字符串结束标志 102 字符数组的输入输出 102 字符串处理函数 104 程序举例 106 本章小结 109 函 数 109 概述 110 函数定义的一般形式 111 函数的参数和函数的值 112 形式参数和实际参数 113 函数的返回值 114 函数的调用 114 函数调用的一般形式 114 函数调用的方式 115 被调用函数的声明和函数原型 115 函数的嵌套调用 116 函数的递归调用 118 数组作为函数参数 121 局部变量和全局变量 125 局部变量 125 全局变量 127 变量的存储类别 128 动态存储方式与静态动态存储方式 128 auto变量 129 用static声明局部变量 129 register变量 130 用extern声明外部变量 131 预处理命令 131 概述 132 宏定义 132 无参宏定义 132 带参宏定义 135 文件包含 138 条件编译 139 本章小结 141 指针 141 地址指针的基本概念 142 变量的指针和指向变量的指针变量 142 定义一个指针变量 143 指针变量的引用 143 指针变量作为函数参数 147 指针变量几个问题的进一步说明 150 数组指针和指向数组的指针变量 153 指向数组元素的指针 153 通过指针引用数组元素 154 数组名作函数参数 156 指向多维数组的指针和指针变量 162 字符串的指针指向字符串的针指变量 165 字符串的表示形式 165 使用字符串指针变量与字符数组的区别 168 函数指针变量 169 指针型函数 170 指针数组和指向指针的指针 171 指针数组的概念 171 指向指针的指针 174 main函数的参数 176 有关指针的数据类型和指针运算的小结 177 有关指针的数据类型的小结 177 指针运算的小结 177 void指针类型 178 共用体 178 定义一个结构的一般形式 179 结构类型变量的说明 179 结构变量成员的表示方法 181 结构变量的赋值 181 结构变量的初始化 182 结构数组的定义 182 结构指针变量的说明和使用 185 指向结构变量的指针 185 指向结构数组的指针 186 结构指针变量作函数参数 187 动态存储分配 188 链表的概念 189 枚举类型 191 枚举类型的定义和枚举变量的说明 191 枚举类型变量的赋值和使用 192 类型定义符typedef 193 位运算 195 位运算符C语言提供了六种位运算符: 195 按位与运算 195 按位或运算 195 按位异或运算 196 求反运算 196 左移运算 196 右移运算 196 位域(位段) 197 本章小结 199 文件 199 C文件概述 200 文件指针 200 文件的打开与关闭 201 文件的打开(fopen函数) 201 文件关闭函数(fclose函数) 202 文件的读写 202 字符读写函数fgetc和fputc 202 字符串读写函数fgets和fputs 206 数据块读写函数fread和fwtrite 207 格式化读写函数fscanf和fprintf 209 文件的随机读写 210 文件定位 210 文件的随机读写 211 文件检测函数 211 文件结束检测函数feof函数 212 读写文件出错检测函数 212 文件出错标志和文件结束标志置0函数 212 C库文件 212 本章小结 213

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬叶子C

喜欢的话可以支持下我的付费专栏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值