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

1. 野指针

(1)指针变量中的值是非法的内存地址,进而形成野指针

(2)野指针不是NULL指针,是指向不可用内存地址的指针

(3)NULL指针并无危害,很好判断,也很好调试

(4)C语言中无法判断一个指针所保存的地址是否合法

2. 野指针的由来

(1)局部指针变量没有被初始化

(2)指针所指向的变量在指针之前被销毁

(3)使用己经释放过的指针

(4)进行了错误的指针运算

(5)进行了错误的强制类型转换

【实例分析】野指针初探

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

int main()
{
    int* p1 = (int*)malloc(40); //这里只分配40字节
    int* p2 = (int*)1234567;    //该地址是非法的,进行了错误的强制转换
    int i = 0;

    for(i = 0;i < 40; i++)
    {
        *(p1 + i) = 40 - i; //越界访问,p1所指内存总共才40字节,而这里要写入40*4字节
    }

    free(p1);

    for(i = 0; i < 40; i++)
    {
       p1[i] = p2[i]; //p1越界访问,p2非法内存
    }

    return 0;
}

3. 基本原则

(1)绝不返回局部变量局部数组的地址

(2)任何变量在定义后必须用0初始化

(3)字符数组必须确认0结束符后才能成为字符串

(4)任何使用与内存操作相关的函数必须指定长度信息

【实例分析】无处不在的野指针

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

struct Student
{
    char* name;
    int number;
};

char* func()
{
    char p[] = "Hello World!";
    return p; //返回栈中数组,这是很危险的。
}

void del(char* p)
{
    printf("%s\n", p);
    free(p);  //不符谁申请谁释放的原则!
}

int main()
{
    struct Student s; //危险,结构体应先初始化,因为结构体里有指针!
    char* p = func(); //危险,返回局部的数组地址

    //strcpy(s.name, p);//s结构体的name指针是野指针!

    s.number = 99;//合法

    p = (char*)malloc(5);

    strcpy(p, "Hello World!");//越界,源字符串比目标内存大!

    del(p);

    return 0;
}

4. 小结

(1)内存错误是实际产品开发中最常见的问题,然而绝大多数的bug都可以通过遵循基本的编程原则和规范来避免。

(2)在学习的时候要牢记和理解内存操作的基本原则、目的和意义。

时间: 2024-10-21 14:47:33

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

第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=

第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]); }

第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

C++--第14课 - 专题二经典问题解析

第14课 - 专题二经典问题解析 1. malloc与free和new与delete有什么区别? malloc和free是函数,new和delete是关键字. #include <cstdlib> #include <iostream> using namespace std; class Test { private: int i; public: Test() { cout<<"Test()"<<endl; i = 0; } Test

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

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

[Spark內核] 第41课:Checkpoint彻底解密:Checkpoint的运行原理和源码实现彻底详解

本课主题 Checkpoint 运行原理图 Checkpoint 源码解析 引言 Checkpoint 到底是什么和需要用 Checkpoint 解决什么问题: Spark 在生产环境下经常会面临 Transformation 的 RDD 非常多(例如一个Job 中包含1万个RDD) 或者是具体的 Transformation 产生的 RDD 本身计算特别复杂和耗时(例如计算时常超过1个小时) , 可能业务比较复杂,此时我们必需考虑对计算结果的持久化. Spark 是擅长多步骤迭代,同时擅长基于

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   命名行格式