错误内存【读书笔记】C程序中常见的内存操作有关的典型编程错误

题记:写这篇博客要主是加深自己对错误内存的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢。

         对C/C++程序员来讲,内存管理是个不小的挑战,绝对值得慎之又慎,否则让由上万行代码构成的模块跑起来后才出现内存崩溃,是很让人痛苦的。因为崩溃的位置在时间和空间上,通常是在距真正的错误源一段距离以后才表现出来。头几天线上模块因堆内存写越界1个字节引起各种诡异崩溃,定位问题过程当中的折腾仍历历在目,今天读到《深刻理解计算机系统》第9章-虚拟存储器,发明书中总结了C程序中常见的内存操纵有关的10种典型编程错误,总结的比拟全面。故作为笔记,记载于此。

    1. 间接引用无效指针
        进程虚拟地址空间的某些地址范围可能没有映射到任何有意义的数据,如果我们试图间接引用一个指向这些地址的指针,则操纵系统会以Segment Fault终止进程。而且,虚拟存储器的某些区域是只读的(如.text或.rodata),试图写这些区域会以掩护异常中止当前进程。
        如从stdin读取一个int变量时,scanf("%d", &val)是准确用法,若误写为scanf("%d", val)时,val的值会被解释为一个地址,并试图向该地址写数据。在最好的情况下,进程立即异常中止。在最坏的情况下,val的值恰好对应于虚拟存储器的某个正当的具有读/写权限的内存区域,于是该内存单元会被改写,而这通常会在相当长的一段时间后形成灾难性的、令人困惑的后果。

    2. 读未初始化的存储器
        C语言的malloc并不负责初始化申请到的内存区域,因此,常见的错误是假设堆存储器被初始化为0,例如:

int * foo(int **A, int *x, int n){int i, j;int * y = (int *)Malloc(n * sizeof(int));for(i = 0; i < n; i++) {for(j = 0; j < n; j++){y[i] += A[i][j] * x[j];}}return y;}

           上述代码中,错误地假设了y被初始化为0。准确的实现方式是显式将y[i]置为0或者应用calloc。

    3. 栈缓冲区溢出
         例如:

char buf[5];sprintf(buf, "%s", "hello world");

           

    上面的代码致使栈缓冲区溢出,安全的做法是:1)根据需求定义适合的buffer;2)采取snprintf(buf, sizeof(buf), "%s", "hello world")来实时截断。

    4. 误以为指针与其指向的对象是雷同巨细的
        例如:

int **makeArray(int n, int m){int i;int **A = (int **)Malloc(n*sizeof(int));   // 这里错误地以为int *与int两种变量类型具有雷同的sizefor(i = 0; i < n; i++) {A[i] = (int *)Malloc(m * sizeof(int));}return A;}

            上述代码目的是创立一个由n个指针构成的数组,每一个指针均指向一个包含m个int的数组,但误将sizeof(int *)写成sizeof(int)。这段代码只有在int和int *的size雷同的机器上运行良好。如果在像Core i7这样的机器上运行这段代码,由于指针变量的size大于sizeof(int),则会引发代码中的for循环写越界。因为这些字中的一个很多是已分配块的边界标记脚部,所以我们可能不会立即发明这个错误,直到进程运行很久释放这个内存块时,此时,分配器中的合并代码会戏剧性地失败,而没有任何明显的原因。这是"在远处起作用"(action at distance)的一个隐秘示例,这类"在远处起作用"是与存储器有关的编程错误的典型情况。

    5. 形成错位错误
         错位(Off-by-one)错误是另一种常见的覆盖错误来源:

每日一道理
全部世界,因为有了阳光,城市有了生机;细小心灵,因为有了阳光,内心有了舒畅。明媚的金黄色,树丛间小影成像在叶片上泛有的点点破碎似的金灿,海面上直射反映留有的随波浪层层翻滚的碎片,为这大自然创造了美景,惹人醉的温馨之感,浓浓暖意中夹杂着的明朗与柔情,让雨过天晴后久违阳光的心灵重新得到了滋润!
int ** makeArray(int n, int m){int i;int **A = (int **)Malloc(n * sizeof(int *));for(i = 0; i <= n; i++) {A[i] = (int *)Malloc(m * sizeof(int));}return A;}

         

    很明显,for循环次数分歧预期,致使写越界。荣幸的话,进程会立即崩溃;不幸的话,运行很长时间才抛出各种诡异问题。

    6. 引用指针,而不是它所指向的对象
        如果不注意C操纵符的优先级和结合性,就会错误地操纵指针,而不是指针所指向的对象。
        比如上面的函数,其目的是删除一个有*size项的二叉堆里的第一项,然后对剩下的*size-1项重建堆:

int * binheapDelete(int **binheap, int *size){int *packet = binheap[0];binheap[0] = binheap[*size - 1];*size--;  // 此处应该为(*size)--heapify(binheap, *size, 0);return (packet);}

           上述代码中,由于--和*优先级雷同,从右向左结合,所以*size--其实增加的是指针自己的值,而非其指向的整数的值。因此,服膺:当你对优先级和结合性有疑问时,就应该应用括号。

    7. 误解指针运算
        在C/C++中,指针的算术操纵是以它们指向的对象的巨细为单位来进行的。例如上面函数的功能是扫描一个int的数组,并返回一个指针,指向val的初次出现:

int * search(int *p, int val){while(*p && *p != val) {p += sizeof(int); // 此处应该为p++,否则p += 4会致使大部分元素被跳过}}

    8. 引用不存在的变量

           

    C/C++新手不理解栈的规矩时,可能会引用不再正当的当地变量,例如:

int * stackref(){int val;return &val;}

            函数返回的指针(假设为p)指向栈中的局部变量,但该变量在函数返回后随着stackref栈帧的销毁已经不再有效。也即:尽管函数返回的指针p仍然指向一个正当的存储器地址,但它已经不再指向一个正当的变量了。当程序后续调用其它函数时,存储器将重用刚才销毁栈帧处的存储器区域。再后来,如果程序分配某个值给*p,那么它可能实际上正在修改另一个函数栈帧中的数据,从而潜在地带来灾难性的、令人困惑的后果。

    9. 引用闲暇堆块中的数据
        典型的错误为:引用已经被释放了的堆块中的数据,例如:

int * heapref(int n, int m){int i;int *x, *y;x = (int *)Malloc(n * sizeof(int));/*  各种操纵 */free(x);y = (int *)Malloc(m * sizeof(int));for(i = 0; i < m; i++) {y[i] = x[i]++;  // 此处的x之前已经被释放了!}}

    10. 内存泄露
       内存泄露是迟缓、隐性的杀手,当程序员忘记释放已分配块时会产生这类问题,例如:

void leak(int n){int *x = (int *)Malloc(n * sizeof(int));return;}

           如果leak在程序全部生命周期内只调用数次,则问题还不是很严峻(但还是会浪费存储器空间),因为随着进程结束,操纵系统会回收这些内存空间。但如果leak()被经常调用,那就会产生严峻的内存泄露,最坏的情况下,会占用全部虚拟地址空间。对于像守护进程和服务器这样的程序来讲,内存泄露是严峻的bug,必须加以看重。

    【参考资料】
《深刻理解计算机系统》第9章 — 虚拟存储器

    ============== EOF ==================

    

文章结束给大家分享下程序员的一些笑话语录: 一个合格的程序员是不会写出 诸如 “摧毁地球” 这样的程序的,他们会写一个函数叫 “摧毁行星”而把地球当一个参数传进去。

--------------------------------- 原创文章 By
错误和内存
---------------------------------

转载于:https://www.cnblogs.com/xinyuyuanm/p/3150400.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring_HelloWord

环境&#xff1a;IntelliJ 14 &#xff1b; jdk1.8 Spring操作步骤 1.新建项目---Spring Batch2.IntelliJ会自动加载jar包3.现在就可以在src目录下写Java类文件了4.将相应的类部署在XML配置文件spring-config.xml中 &#xff08;Eclipse需要手动创建&#xff0c;貌似名为bean.x…

java 按钮组_java中创建一个按钮组,有10个按钮,分别写着0,1,2,,3。。。。9

展开全部用Java创建按钮组32313133353236313431303231363533e59b9ee7ad9431333332643966的程序如下:import java.awt.GridLayout;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JButton;import javax.swing.JFrame;public class…

Starling 2D框架简介

本系列是对Introducing Starling pdf的翻译&#xff0c;下文是对adobe开发人员中心的一片日志的转载&#xff0c;地址为http://www.adobe.com/cn/devnet/flashplayer/articles/introducing_Starling.html Starling 是在 Stage3D APIs 基础上开发的一种 ActionScript 3 2D 框架&…

基本数据结构——栈

栈的特征是后进先出&#xff08;last-in, first-out, LIFO&#xff09;。栈上的插入操作称为压入&#xff08;PUSH&#xff09;&#xff0c;删除操作称为弹出&#xff08;POP&#xff09;。 下面使用一个数组S[n]来实现一个最多容纳n个元素的栈。定义一个属性指向最新插入的元素…

Android AutoCompleteTextView控件实现类似百度搜索提示,限制输入数字长度

Android AutoCompleteTextView 控件实现类似被搜索提示&#xff0c;效果如下 1.首先贴出布局代码 activity_main.xml&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res…

Centos/RHEL上查看主板型号

老是搞忘记&#xff0c;专门做个记录&#xff1a; [rootmedia ~]# dmidecode | grep "Product Name" Product Name: To be filled by O.E.M.Product Name: B75M-D3V 修改默认语言&#xff1a;[chenshouyongmedia ~]$ cat /etc/sysconfig/i18n LANG"en_US.UTF-8…

java即时聊天系统毕业_(完整版)基于Java即时聊天系统的设计与实现毕业论文设计...

目录1 前言...................................................................................................................................1.1 课题选题背景...................................................................................................…

杭电 1284 钱币兑换问题【完全背包求方案总数】

解题思路&#xff1a;因为对于完全背包的状态转移方程f[v]max(f[v],f[v-c[i]]w[i])已经记录了所有背包组成的方案&#xff0c;只不过通常问的是求最大值&#xff0c;现在要求方案总数 即为 f[v]sum(f[v],f[v-c[i]w[i]]), Problem Description在一个国家仅有1分&#xff0c;2分&…

java与算法_Java与算法之(1) - 冒泡排序

冒泡排序法的原理是&#xff0c;每次比较相邻的两个元素&#xff0c;如果它们的顺序错误就把它们交换过来。例如对4 3 6 2 7 1 5这7个数字进行从小到大的排序&#xff0c;从最左侧开始&#xff0c;首先比较4和3因为是从小到大排序&#xff0c;4和3的顺序显然是错误的&#xff0…

Js+XML 操作

我的xml文件Login.xml如下. <?xml version"1.0" encoding"utf-8" ?><Login><Character><C Text"热血"Value"0"></C><C Text"弱气"Value"1"></C><C Text"激情…

Java(Android)线程池

1、new Thread的弊端执行一个异步任务你还只是如下new Thread吗&#xff1f; [java] view plaincopy new Thread(new Runnable() { Override public void run() { // TODO Auto-generated method stub } }).start(); 那你就out太多了&#xff0c;n…

JQuery链式操作简单的菜单列表

看到这个简单的菜单demo&#xff0c;也是为了再看看JQuery对DOM的操作&#xff0c;一直都记不牢&#xff0c;特别是siblings&#xff08;&#xff09;这个总是想不起来。 这次再过一遍JQuery&#xff0c;不管简单的还是复杂的demo 还是坚持练习一遍吧&#xff01;只为记录&…

java 网络编程实验_Java网络编程入门实验一涉及点

1.http://www.cr173.com/html/20128_all.html 【wireshark怎么抓包、wireshark抓包详细图文教程】2.http://blog.csdn.net/huangjin0507/article/details/51678858 【HTTP协议1&#xff1a;工作原理】3.https://www.cnblogs.com/1666818961-lxj/p/7210021.html 【网络常用端口号…

node.js async流程控制器--queue(队列)

queue流程控制器是一个并行的流程控制器,但是它与parallel的区别在于queue可以控制一次执行几个函数,而parallel只是让所有函数并行执行. 例子如下: var q async.queue(function (obj,cb) {setTimeout(function () {console.log(obj);cb(); },obj.time) },1)for (var i 0; i&…

利用JS实现点击上一周或下一周却换

1.页面加载显示当前年份的第几周 效果如图&#xff1a; html代码&#xff1a; <font size"2" color"black"> <input id"btnweek5" type"button" class"btn" value"上周" οnclick"EduCommissio…

centos7网卡编辑_CentOS7修改网卡为eth0

1.编辑网卡信息[rootlinux-node2~]#cd /etc/sysconfig/network-scripts/ #进入网卡目录[rootlinux-node2network-scripts]# mv ifcfg-eno16777728 ifcfg-eth0 #重命名网卡名称[rootlinux-node2 network-scripts]#cat ifcfg-eth0 #编辑网卡信息TYPEEthernetBOOTPROTOstaticDEFR…

C# 微支付退款申请接口 V3.3.6

/// <summary>/// 微支付退款申请/// </summary>/// <param name"context"></param>/// <param name"returnMsg"></param>/// <returns></returns>public bool Refund(HttpContext context, ref string r…

[转] 英语、计算机、互联网与全球化

http://davidzhao.blog.51cto.com/4548102/1225732 转载于:https://www.cnblogs.com/wowk/p/3169638.html

APNIC IP 库

http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest转载于:https://www.cnblogs.com/dlwj/p/6388162.html

java reference 传引用_Java的引用(reference)---Roni

摘自《Java面向对象编程》一书,作者:孙卫琴 来源:www.javathinker.org在JDK1.2以前的版本中&#xff0c;当一个对象不被任何变量引用&#xff0c;那么程序就无法再使用这个对象。也就是说&#xff0c;只有对象处于可触及状态&#xff0c;程序才能使用它。这就像在日常生活中&am…