- + P A

- 大内高手—常见内存错误

      C语言 2008-10-28 20:52

随着诸如代码重构和单元测试等方法引入实践,调试技能渐渐弱化了,甚至有人主张废除调试器。这是有道理的,原因在于调试的代价往往太大了,特别是调试系统集成之后的BUG,一个BUG花了几天甚至数周时间并非罕见。

 

而这些难以定位的BUG基本上可以归为两类:内存错误和并发问题。而又以内存错误最为普遍,即使是久经沙场的老手,也有时也难免落入陷阱。前事不忘,后世之师,了解这些常见的错误,在编程时就加以注意,把出错的概率降到最低,可以节省不少时间。

 

这些列举一些常见的内存错误,供新手参考。

 

1.         内存泄露。

大家都知道,在堆上分配的内存,如果不再使用了,应该把它释放掉,以便后面其它地方可以重用。在C/C++中,内存管理器不会帮你自动回收不再使用的内存。如果你忘了释放不再使用的内存,这些内存就不能被重用,就造成了所谓的内存泄露。

 

把内存泄露列为首位,倒并不是因为它有多么严重的后果,而因为它是最为常见的一类错误。一两处内存泄露通常不至于让程序崩溃,也不会出现逻辑上的错误,加上进程退出时,系统会自动释放该进程所有相关的内存,所以内存泄露的后果相对来说还是比较温和的。当然了,量变会产生质变,一旦内存泄露过多以致于耗尽内存,后续内存分配将会失败,程序可能因此而崩溃。

 

现在的PC机内存够大了,加上进程有独立的内存空间,对于一些小程序来说,内存泄露已经不是太大的威胁。但对于大型软件,特别是长时间运行的软件,或者嵌入式系统来说,内存泄露仍然是致命的因素之一。

 

不管在什么情

标签集:TAGS:
我要留言To Comment 阅读全文Read All | 回复Comments() 点击Count()

- 总结:三种传递动态分配内存的方法

      C语言 2008-10-28 20:33
在项目中,遇到到需要专门做一个函数来实现动态内存的分配,然后其他的函数都可以使用这块内存进行读写。但是比较怪异的是,在其他函数中对该块内存的标识是通过一个unsigned long的变量,而并非通过一个指针指向该块内存。思索良久,发现它的动态内存的传递可能是采用了不大常用的方法。
借着这个提示, 总结了三种方法实现动态分配内存的传递。当然,有的符合我们平时写程序的习惯,可能有些不大常用。
以下是程序代码,三个子程序以及测试方法:

#include <stdio.h>

#include

标签集:TAGS:
我要留言To Comment 阅读全文Read All | 回复Comments() 点击Count()

- 参数传递之指针传递

      C语言 2008-10-28 20:32

(1)基础知识
C课本上的一个程序:

void swap(int *p1,int *p2)
{
  int t=*p1;
  *p1=*p2;
  *p2=t;
}

void main()
{
  int a=1,b=2;
 
  swap(&a,&b);
 
  ...
}

a,b的值交换了是因为a,b的地址被传递给了函数swap,使得p1=&a,p2=&b。
为了看得更清楚些,更改一些:

void swap(int *p1,int *p2)
{
  int t=*p1;
  *p1=*p2;
  *p2=t;
}

void main()
{
  int a=1,b=2;
  int *pa=&a,*pb=&b;
 
  swap(pa,pb);
 
  ...
}

在上面,pa,pb是形参,p1,p2是实参。pa=&a,p1=&a;pb=&b,p2=&b。但pa与p1不是同一个内存区域的指针,pb与p2也一样。


(2)问题

再看一个程序:


为程序分配内存区域,你可以这样做:

#define SIZE 10

标签集:TAGS:
我要留言To Comment 阅读全文Read All | 回复Comments() 点击Count()

- typedef

      C语言 2008-10-13 21:56

typedef在编译时解析,define在预处理时解析(只是简单替代)

typedef char * p;

const p; (= 编译变量从右char * const p);

  下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?
>1:int *(*a[5])(int, char*);
>2:void (*b[10]) (void (*)());
>3. doube(*)() (*pa)[9];
  答案与分析:
  对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。
>1:int *(*a[5])(int, char*);
//pFun是我们建的一个类型别名
typedef int *(*pFun)(int, char*);
//使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
pFun a[5];
>2:void (*b[10]) (void (*)());
//首先为上面表达式蓝色部分声明一个新类型
typedef void (*pFunParam)();
//整体声明一个新类型
typedef void (*pFun)(pFunParam);
//使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());
pFun b[10];
>3. doube(*)() (*pa)[9];
//首先为上面表达式蓝色部分声明一个新

标签集:TAGS:
我要留言To Comment 阅读全文Read All | 回复Comments() 点击Count()

- (转)(精)const用法小结

      C语言 2008-10-13 21:54
前两天看代码的时候,发现很奇怪的const用法,于是在网上搜了一下,原来有这么多用法,不敢独享,拿上来和与我一样菜的小生们学习~

1.       const常量,如const int max = 100;  
优点:const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)

2.       const 修饰类的数据成员。如:
class A

{

    const int size;

    … 

}

const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。如

class A

{

 const int size = 100;    //错误

 int array[size]; 

标签集:TAGS:
我要留言To Comment 阅读全文Read All | 回复Comments() 点击Count()