第42课 内存操作经典问题分析二

1. 常见内存错误

(1)结构体成员指针未初始化

(2)结构体成员指针未分配足够的内存

(3)内存分配成功,但并未初始化

(4)内存操作越界

【实例分析】常见的内存错误1

#include <stdio.h>
#include <malloc.h>

void test(int* p, int size) //对内存操作时,有带长度信息
{
    int i = 0;
    for(i = 0; i<size; i++)
    {
        printf("%d\n",p[i]);
    }

    free(p);//不合符谁申请谁释放原则
            //当p指向一个数组时,该操作是非法的,因为
            //只能free堆中的内存。
}

void func(unsigned int size)
{
    int* p = (int*)malloc(size * sizeof(int));
    int i = 0;

    if(size % 2 != 0)  //当size为奇数时会p产生内存泄漏
    {
         return;
    }

    for(i=0; i<size; i++)
    {
        p[i] = i;
        printf("%d\n", p[i]);
    }

    free(p);

}

int main()
{
    int* p = (int*)malloc(5 * sizeof(int));

    test(p, 5); //test内部释放了p所指内存,不符合谁申请谁释放原则

    free(p); //这里重复释放p,会造成程序崩溃

    func(9); //参数为奇数,会造成内存泄漏!
    func(10);//正常

    return 0;
}

2. 内存操作的交通规则

(1)动态内存申请之后,应该立即检查指针的值是否为NULL,防止使用NULL指针

int* p = (int*)malloc(56);

if (p != NULL){

     //do something here!

}

……

(2)free指针之后必须立即赋值为NULL。(如free(p);p=NULL;)

(3)任何与内存操作相头的函数都必须带长度信息

void printf(int* p, int size) //带长度信息size
{   

    int i = 0;
    snprintf(buf,sizeof(buf), "%s\n", "Hello World!");

    for(i = 0;i< size; i++){  //长度信息size
        printf("%s\n",p[i])
    }
}

(4)malloc操作和free操作必须匹配,防止内存泄露和多次释放。并且遵循谁申请谁释放的原则。

【实例分析】常见的内存错误2

#include <stdio.h>
#include <malloc.h>

struct Demo
{
    char* p;
};

int main()
{
    struct Demo d1;//结构体(内含指针变量)未被初始化
    struct Demo d2;

    char i = 0;

    for(i=‘a‘; i< ‘z‘; i++)
    {
        d1.p[i] = i; //d1.p指针未被初始化,内存空间也未分配
    }

    d2.p = (char*)calloc(5, sizeof(char));

    printf("%s\n", d2.p);

    for(i=‘a‘; i< ‘z‘; i++)
    {
        d2.p[i] = i;
    }

    free(d2.p);

    return 0;
}

3. 小结

(1)内存错误的本质源于指针保存的地址为非法值(如指针未初始化或指针运算导致的越界)

(2)内存泄漏源于malloc和free不匹配。当malloc次数多于free里产生内存泄漏。反之程序可能崩溃。

时间: 2024-10-12 00:15:30

第42课 内存操作经典问题分析二的相关文章

第42课 内存操作经典问题分析2

常见内存错误: 两次释放同一个指针是有问题的. 示例: 1 #include <stdio.h> 2 #include <malloc.h> 3 4 void test(int* p, int size) 5 { 6 int i = 0; 7 8 for(i=0; i<size; i++) 9 { 10 printf("%d\n", p[i]); 11 } 12 13 free(p); 14 } 15 16 void func(unsigned int s

第41课 内存操作经典问题分析一

1. 野指针 (1)指针变量中的值是非法的内存地址,进而形成野指针 (2)野指针不是NULL指针,是指向不可用内存地址的指针 (3)NULL指针并无危害,很好判断,也很好调试 (4)C语言中无法判断一个指针所保存的地址是否合法 2. 野指针的由来 (1)局部指针变量没有被初始化 (2)指针所指向的变量在指针之前被销毁 (3)使用己经释放过的指针 (4)进行了错误的指针运算 (5)进行了错误的强制类型转换 [实例分析]野指针初探 #include <stdio.h> #include <m

第41课 内存操作经典问题分析1

野指针: 野指针的由来: 示例程序: 1 #include <stdio.h> 2 #include <malloc.h> 3 4 5 int main() 6 { 7 int* p1 = (int*)malloc(40); 8 int* p2 = (int*)1234567; 9 int i = 0; 10 11 for(i=0; i<40; i++) 12 { 13 *(p1 + i) = 40 - i; 14 } 15 16 free(p1); 17 18 for(i=

Django rest framework 权限操作(源码分析二)

知识回顾 这一篇是基于上一篇写的,上一篇谢了认证的具体流程,看懂了上一篇这一篇才能看懂, 当用户访问是 首先执行dispatch函数,当执行当第二部时: #2.处理版本信息 处理认证信息 处理权限信息 对用户的访问频率进行限制 self.initial(request, *args, **kwargs) 进入到initial方法: def initial(self, request, *args, **kwargs): """ Runs anything that needs

完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)

构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化的方式进行配置,所以维护起来相当困难.Gradle:Gradle采用增量构建.Gradle通过Groovy编程而不是传统的XML声明进行配置.Gradle可以很好地配合Maven进行依赖管理,并且把Ant脚本当作头等公民.字节码操作 编程操作Java字节码的函数库. ASM:通用底层字节码操作及分析

C++--第24课 - 专题四经典问题解析

第24课 - 专题四经典问题解析 1. 历史的痕迹 #include <cstdlib> #include <iostream> using namespace std; template<class T>  //以前是用typename定义,现在是用class定义 T Minus(T a, T b) { return a - b; } template<class T>  //类模板 class Add { public: T add(T a, T b)

【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

[嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 ) 一. 内存 简介 1. 两大内存分类 ( 1 ) DRAM 简介 ( 定期刷新 | 速度慢 | 成本低 ) DRAM 简介 : 1.硬件描述 : DRAM 基本由一个个小电容基本原件组成, 电容的两端保留电荷; 2.优缺点描述 : ① 优点 : 成本很低, 很便宜; ② 缺点 : 需要 定期刷新数据, 速度较慢; a.

jstack(查看线程)、jmap(查看内存)和jstat(性能分析)命令

公司内部同事分享的一篇文章 周末看到一个用jstack查看死锁的例子.昨天晚上总结了一下jstack(查看线程).jmap(查看内存)和jstat(性能分析)命令.供大家参考 1.Jstack 1.1   jstack能得到运行java程序的java stack和native stack的信息.可以轻松得知当前线程的运行情况.如下图所示 注:这个和thread dump是同样的结果.但是thread dump是用kill -3 pid命令,还是服务器上面少用kill为妙 1.2   命名行格式

Java API —— IO流(数据操作流 &amp; 内存操作流 &amp; 打印流 &amp; 标准输入输出流 &amp; 随机访问流 &amp; 合并流 &amp; 序列化流 &amp; Properties &amp; NIO)

1.操作基本数据类型的流 1) 操作基本数据类型 · DataInputStream:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型.应用程序可以使用数据输出流写入稍后由数据输入流读取的数据. · DataOutputStream:数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中.然后,应用程序可以使用数据输入流将数据读入. package datastreamdemos; import java.io.*; /** * Created b