Chinaunix首页 | 论坛 | 博客
  • 博客访问: 42042
  • 博文数量: 7
  • 博客积分: 76
  • 博客等级: 民兵
  • 技术积分: 105
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-29 20:03
文章分类

全部博文(7)

文章存档

2013年(7)

我的朋友

分类: C/C++

2013-01-22 18:29:46

指针是一个非常灵活且强大的编程工具,有非常广泛的应用。大多数C程序都在某程度上使用了指针。C语言还进一步增强了指针的功能,为在代码中使用指针提供了很强的激励机制,它允许在执行程序时动态分配内存。只有使用指针,才能动态分配内存。

在程序的执行期间分配内存时,内存区域中的这个空间称为堆(heap)。还有另一个内存区域,称为堆栈(stack),其中的空间分配给函数的参数和本地变量。在执行完该函数的,存储参数和本地变量的内存空间就会释放。堆中的内存是由程序员控制的,在分配堆上的内存时,由程序员跟踪所分配的内存何时不再需要,并释放这些空间,以便于以后重用它们。

1、动态内存分配: malloc()函数

在运行时分配内存的最简单的标准库函数是malloc()。使用这个函数时需要在程序中包含头文件。使用malloc()函数需要指定要分配的内存字节数作为参数。这个函数返回所分配内存的第一个字节的地址。因为返回的是一具地址,所以这里可以使用指针。

例如:int *pNumber = (int *)malloc(100);

这条语句请求100个字节的内存,并把这个内存的地址赋予pNumber.只要不修改它,任何时间使用这个变量pNumber,它都会指向所分配的100个字节的第一个int的位置。这个内存块能保存25个int值,每个int占4个字节。

注意,类型转换(int *)将函数返回的地址转换成int类型的指针。这是因为malloc()是一般用途的,可以为任何类型的数据分配内存。这个函数不知道要这个内存作什么用,所以它返回一个void类型的指针,写做void*。类型void*的指针可以指向任意类型的数据,然而不能取消对void指针的引用,因为它指向未具体说明的对象,许多编译器会把malloc()返回的地址自动转换成适当的类型,且不会伤害具体于说明的对象。

在应用动态分配内存之后要做相应的检查,所分配的内存是否已分配,再使用它。如:if(pNumber == NULL)

2、分配内存时使用sizeof运算符

为不同类型的数据分配内存可以使用sizeof运算符

pNumber = (int *) malloc(75*sizeof(int));

这一句请求分配足以存储75个int类型的内存。sizeof是一个运算符,它返回一个size_t 类型的无符号整数,该整数是存储它的参数需要的字节数。它把关键字如int或float等作为参数,返回存储该类型的数据项所需的字节数。它的参数也可以是变量或数组名。把数组名作为参数时,sizeof返回存储整个数组所需的字节数。使用sizeof,可以根据不同的C编译器为int类型的值自动调整所需的存储空间。

下面来看一个例子来实际应用一下:


#include 
#include 
#include 

int main(void)
{
  unsigned long *primes = NULL;
  unsigned long trial = 0;
  bool found = false;
  size_t total = 0;
  size_t count = 0;
  size_t i = 0;

  printf("How many primes would you like - you'll get at least 4?");
  scanf("%u", &total);
  total = total < 4U ? 4U : total;

  primes = (unsigned long *)malloc(total * sizeof(unsigned long));

  if(primes == NULL)
  {
    printf("\\nNot enough memory. Hasta la Vista, baby.\\n");
    return 1;
  }

  *primes = 2UL;
  *(primes + 1) = 3UL;
  *(primes +2) = 5UL;
  count = 3U;
  trial = 5U;

  while(count < total)
  {
    trial += 2UL;
    for(i = 0; i < count; i++)
      if(!(found = (trial % *(primes+i))))
        break;
    if(found)
      *(primes + count++) = trial;
  }

  for(i = 0; i < total; i++)
  {
    if(!(i % 5U))
      printf("\\n");
    printf("%12lu", *(primes + i));
  }
  printf("\\n");
  return 0;
}


代码说明:

这个例子是计算质数,运行这段代码可以输入要程序产生的质数个数。指针变量primes引用一块用于存储所计算的质数的内存区。在程序中没有一开始就定义内存,而是在输入质数个数后分配的。然后使用malloc()来分配内存,内存分配好就把已经知道的前三个质数定义好,将它们放在primes指针指向的内存区的前三个位置。

有了三个质数,就把count变量设定为3,用最后一个质数5初始化变量trial.

开始查找下一个质数时,给trial中的值加2,得到下一个要测试的数字,所有的质数都在while循环内查找。

在while循环中用一个for循环来判断这个数是不是质数,


for(i = 0; i < count; i++)
      if(!(found = (trial % *(primes+i))))
        break;


在这个循环中trial除以每个质数的余数存放在found中,如果能除尽,余数是0,因此found设置为false。如果余数是0,就表示trial不是质数,可以中断for循环,测试下一个。如果余数不是0则found的值是false.当遍历整个循环后没有可以除尽的,说明这个数是质数就把这个数加入到内存区域的下一个位置中。

下面就是输出所得到的质数了,用for循环来每行显示5个质数。

3、用calloc()函数分配内存

calloc()函数与malloc()函数相比有两个优点:

一是:它把内存分配为给定大小的数组,

二是:它初始化了所分配的内存,所有的位都是0。

calloc()函数需要两个参数:数组的元素个数和数组元素占用的字节数,这两个参数的类型都是size_t。该函数也不知道数组元素的类型,所以所分配区域的地址返回为void*类型。如下所示:

int *pNumber = (int *) calloc(75, sizeof(int));

使用calloc()给包含75个int元素的数组分配内存。如此,把上面的例子修改一下,用calloc()这个函数来分配所需的内存,只要修改如下代码就行其他的不用改变。


primes = (unsigned long *)calloc(total, sizeof(unsigned long));


4、释放动态分配的内存

在动态分配内存时,应总是在不需要该内存时释放它们。堆上分配的内存会在程序结束时自动释放,甚至是在退出程序之前,也应立即释放。在比较复杂的情况下,很容易出现内存泄漏。当动态分配了一些内存时,没有保留对它们的引用,就会出现内存泄漏,此时无法释放内存。这常常发生在循环内部,由于没有释放不再需要的内存,程序会使用越来越多的内存,最终占用所有内存。

当然要释放malloc()或calloc()分配的内存,必须使用函数返回的引用内存块的地址。要释放动态分配的内存,而该内存地址存储在pNumber指针中,可以使用下面的语句:

free(pNumber);

free()函数的形参是void*类型,所有指针类型都可以自动转换为这个类型,所以可以把任意类型的指针作为参数传送给这个函数。只要pNumber包含分配内存时malloc()或calloc()返回的地址,就会释放所分配的整个内存块,以备以后使用。

如果给free()函数传送一个空指针,该函数就什么也不做。应避免两次释放相同的内存域,因为在这种情况下,free()函数的操作是不确定的,因此无法预料。如果多个指针变量引用已分配的内存,就有可能两次释放相同的内存,所以要特别小心。

5、重新分配内存 ——realloc()

realloc()函数可以重用前面通过malloc()或calloc()分配的内存。函数需要两个参数:一个是指针,它包含前面调用malloc()、calloc()或realloc()返回的地址,另一个是要分配的新内存字节数。

realloc()函数释放第一个指针参数引用的之前分配的内存,然后重新分配该内存区域,以满足第二个参数指针的新请求。显然,第二个参数的值不应超过以前分配的字节数。否则,新分配的内存将与以前分配的内存区域大小不相同。

动态分配的内存的基本规则:

a、避免分配大量的小内存块。分配堆上的内存有一些系统开销,所分配许多小的内存块比分配几个大内存块的系统开销大。

b、仅在需要时分配内存。只要使用完堆上的内存块,就释放它。

c、总是确保释放已分配的内存。在编写分配内存的代码时,就要确定在代码的什么地方释放内存。

d、在释放内存之前,确保不会无意中覆盖堆上分配的内存的地址,否则程序就会出现内存泄漏。在循环中分配内存时,要特别小心。








阅读(1769) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~