动态分配内存

8)“我想用malloc”、“我用不好malloc”
  来看看一个变态程序:

/* xx.c:xx模块实现文件 */
int *pInt;
/* xx模块的初始化函数 */
xx_intial()
{
pInt = ( int * ) malloc ( sizeof( int ) );
...
}
/* xx模块的其他函数(仅为举例)*/
xx_otherFunction()
{
*Int = 10;
...
}


  这个程序定义了一个全局整型变量指针,在xx模块的初始化函数中对此指针动态申请内存,并将pInt指向该内存首地址,并在xx模块的其他函数中都使用pInt指针对其指向的整数进行读取和赋值。

  这个程序让我痛不欲生了好多天,扼腕叹息!这是我母校计算机系一位硕士的作品!作者为了用上malloc,拼命地把本来应该用一个全局整型变量摆平的程序活活弄成一个全局整型指针并在初始化函数中“动态”申请内存,自作聪明而正好暴露自己的无知!我再也不要见到这样的程序。

  那么malloc究竟应该怎么用?笔者给出如下规则:
   规则1 不要为了用malloc而用malloc,malloc不是目的,而是手段;

   规则2 malloc的真正内涵体现在“动态”申请,如果程序的特性不需动态申请,请不要用malloc;

  上面列举的变态程序完全不具备需要动态申请的特质,应该改为:

/* xx.c:xx模块实现文件 */
int example;
/* xx模块的初始化函数 */
xx_intial()
{
...
}
/* xx模块的其他函数(仅为举例) */
xx_otherFunction()
{
example = 10;
...
}

规则3 什么样的程序具备需要动态申请内存的特质呢?包含两种情况:
  (1)不知道有多少要来,来了的又走了
  不明白?这么说吧,譬如你正在处理一个报文队列,收到的报文你都存入该队列,处理完队列头的报文后你需要取出队列头的元素。

  你不知道有多少报文来(因而你不知道应该用多大的报文数组),这些来的报文处理完后都要走(释放),这种情况适合用malloc和free。

  (2)慢慢地长大
  譬如你在资源受限的系统中编写一文本编辑器程序,你怎么做,你需要这样定义数组吗?

  char str[10000];

  不,你完全不应该这么做。即使你定义了一个10000字节大的字符串,用户如果输入10001个字符你的程序就完完了。

  这个时候适合用malloc,因为你根本就不知道用户会输入多少字符,文本在慢慢长大,因而你也应慢慢地申请内存,用一个队列把字符串存放起来。

  那么是不是应该这样定义数据结构并在用户每输入一个字符的情况下malloc一个CharQueue空间呢?

typedef struct tagCharQueue
{
char ch;
struct tagCharQueue *next;
}CharQueue;


  不,这样做也不对!这将使每个字符占据“1+指针长度”的开销。

  正确的做法是:

typedef struct tagCharQueue
{
char str[100];
struct tagCharQueue *next;
}CharQueue;


  让字符以100为单位慢慢地走,当输入字符数达到100的整数倍时,申请一片CharQueue空间。

规则4 malloc与free要成对出现
  它们是一对恩爱夫妻,malloc少了free就必然会慢慢地死掉。成对出现不仅体现在有多少个malloc就应该有多少个free,还体现在它们应尽量出现在同一函数里,“谁申请,就由谁释放”,看下面的程序:

char * func(void)
{
char *p;
p = (char *)malloc(…);
if(p!=NULL)
…; /* 一系列针对p的操作 */
return p;
}
/*在某处调用func(),用完func中动态申请的内存后将其free*/
char *q = func();

free(q);


  上述代码违反了malloc和free的“谁申请,就由谁释放”原则,代码的耦合度大,用户在调用func函数时需确切知道其内部细节!正确的做法是:

/* 在调用处申请内存,并传入func函数 */
char *p=malloc(…);
if(p!=NULL)
{
func(p);

free(p);
p=NULL;
}
/* 函数func则接收参数p */
void func(char *p)
{
… /* 一系列针对p的操作 */
}


  规则5 free后一定要置指针为NULL,防止其成为“野”指针


你可能感兴趣的:(数据结构,struct,null,文本编辑)