C语言学习笔记(13)

1. 动态内存分配

先来介绍三个动态内存分配的函数:malloc,calloc和realloc。说来惭愧,以前只知道malloc。现在来看下他们的区别:

malloc:最常用的分配内存块,但是不对内存进行初始化。

calloc:分配内存块,但是对内存块进行清零操作,这就造成此函数的效率要比malloc要低。

realloc:调整(增加或者减少)之前分配内存块的大小。

由于上面的函数只是开辟了一段内存,因此无法知道你要利用这段内存来存储什么类型的数据,因此只是返回一个void *类型的值,当然,void *可以和任何指针类型互相转换。

当分配内存失败时(可能是内存不足或者其他原因),以上的函数都会返回一个空指针,那么我们安全期间,在我们使用这块分配的内存前,都应该进行一次空指针的验证。

int main (void)
{
    int *p;
    p=(int *)malloc(sizeof(*p)*1000);
    if(p==NULL)
    {
        exit(EXIT_FAILURE);
    }
    printf("success");
    return 0;
}

注意上面的两点,一个是空指针的验证,另外一个是分配内存的大小,这里我们常常是用类型的大小乘以存储成员的数量来计算分配。如果是字符串,我们就不要忘记了加1,来存储\0。

#include <stdio.h>
#include <stdlib.h>

int main (void)
{
    char *p;
    p=(char *)malloc(sizeof(*p)*1000+1);
    if(p==NULL)
    {
        exit(EXIT_FAILURE);
    }
    printf("success");
    return 0;
}

 

接下来我们看一下calloc元素的原型:

void * __cdecl calloc(_In_ size_t _Count, _In_ size_t _Size);

从上面我们可以看到calloc函数有两个参数,分配是,数量和大小。由此可以说明,calloc是C语言用来分配数组空间最好的选择。那么我们就把第一段代码改一下:

int main (void)
{
    int *p;
    p=(int *)calloc(1000,sizeof(int));
    if(p==NULL)
    {
        exit(EXIT_FAILURE);
    }
    printf("success");
    return 0;
}

这样就更合适了。

最后是realloc,我们也来看一下realloc元素的原型:

void * __cdecl realloc(_Post_ptr_invalid_ void * _Memory, _In_ size_t _NewSize);

 

当我们之前分配了一个数组的大小,但是后来我们却发现这个大小不够用了,或者是太大了,那么我们就可以利用realloc来调整我们的占用内存大小:

int main (void)
{
    int *p;
    p=(int *)calloc(1000,sizeof(int));
    if(p==NULL)
    {
        exit(EXIT_FAILURE);
    }
    realloc(p,sizeof(int)*100);
    if(p==NULL)
    {
        exit(EXIT_FAILURE);
    }
    printf("success");
    return 0;
}

也别忘了检验p是否为空指针的情况。

在C标准中,并没有对realloc的实现做以规定,但是对于大部分编译器来说,如果是把原地址空间缩小,他会尽量地不去移动原来的数据。如果是把空间增大,那么他会尽量首先在原地址的末尾去分配内存,如果不足以分配,那么编译器才会去寻找新的地址块,并且把原地址空间内的数据转移到新的地址上。

2. 释放空间

习惯了Java/C#的我们,似乎已经忘记了要回收垃圾的习惯,在C/C++中,是没有GC的,因此我们要记得,当我们在堆上分配了一块内存,并且不在使用时,我们要使用free函数来释放掉空间。看下free的原型:

void   __cdecl free(_Post_ptr_invalid_ void * _Memory);

很简单,不再赘述。

3. 指向指针的指针

在读大学时,我一直对这个概念不是很理解,现在我更愿意这样去理解指针。

当我们声明了int *p=malloc(1000)的时候,我们可以这样来理解:

image

其实我更愿意把p就理解成一个地址的值,p=0x1111(0x1111是分配的1000字节内存的首地址)。那么什么是指向指针的指针呢?

image

这里的q就是指向指针的指针,q的值就是0x0004,也就是p所在的地址。

以此类推,我们还可以知道指向指针的指针的指针。

4. 函数指针

我们来看C语言里提供了qsort函数:

_CRTIMP void __cdecl qsort(_Inout_bytecap_x_(_NumOfElements * _SizeOfElements) void * _Base, 
    _In_ size_t _NumOfElements, _In_ size_t _SizeOfElements, 
        _In_ int (__cdecl * _PtFuncCompare)(const void *, const void *));

 

最后一个参数就是一个函数指针,其实不用这么麻烦,我们来看个简单的函数指针的原型:

double (*function)(int);

这个就是最简单的函数指针的原型,与返回指针类型的函数相比,他们相差的只是*和函数名之间要用括号括起来。

当传进来一个函数指针时,我们便可以在函数中适用这个传进来的参数(函数指针)了。例如在qsort里,我们便可以自己制定比较规则,不再多说。

posted @ 2010-04-16 00:40  飞林沙  阅读(650)  评论(0编辑  收藏  举报