您的位置:首页 > 其它

C和指针(二)

2015-10-22 11:28 281 查看
7、动态内存分配

数组的元素存储于内存中连续的位置上。当一个数组被声明时,它所需要的内存在编译时就被分配,即数组大小已经被限定。但是,数组的长度常常在运行时才知道,这时可以通过采用动态内存分配的方式解决这类问题。
C函数库有四个函数用于动态内存分配和释放:malloc、free、calloc和realloc。

这些函数维护一个可用内存池。当一个程序另外需要一些内存时,它就调用malloc函数,malloc从内存池中提取一块合适的内存,并向该程序返回一个指向这块内存的指针。这块内存在此时并没有以任何方式进行初始化。当一块以前分配的内存不再使用时,程序调用free函数把它归还给内存池供以后之需。
函数原型如下,它们都在头文件stdlib.h中声明。
void *malloc( seze_t size );
void free( void *pointer );

malloc的参数就是需要分配的内存字节(字符)数。如果内存池中的可用内存可以满足这个需求,malloc就返回一个指向被分配的内存块起始位置的指针。malloc所分配的是一块连续的内存。如果不能满足,malloc就返回一个NULL指针。因此,对每个malloc返回的指针都进行检查,确保它并非MULL是非常重要的。malloc返回一个类型为
void * 的指针,可以转换为其他类型的指针。
另外两个内存分配函数,calloc和realloc。它们的原型如下:
void *calloc( size_t num_elements, size_t element_size
);
void realloc( void *ptr, size_t new_size );

calloc也用于分配内存。malloc和calloc之间的主要区别是后者在返回指向内存的指针之前把它初始化为0。

realloc函数用于修改一个原先已经分配的内存的大小。使用这个函数,你可以使一块内存扩大或者缩小。如果原先的内存无法改变大小,realloc将分配另一块正确大小的内存,并把原先那块内存的内容复制到新的块上。因此,在使用realloc之后,你就不能再使用指向旧内存的指针,而是应该改用realloc所返回的新指针。

在使用动态内存分配的程序中,常常会出现许多错误。这些错误包括对NULL指针进行解引用操作、对分配的内存进行操作时越过边界、释放并非动态分配的内存、试图释放一块动态分配的内存的一部分以及一块动态内存被释放之后被继续使用。

内存泄露:当动态分配的内存不再需要使用时,它应该被释放,这样它以后可以被重新分配使用。分配内存但在使用完毕后不释放将引起内存泄露(memory
leak)。

8、高级指针话题
指针的指针:
高级声明:
在使用更高级的指针类型之前,我们必须观察它们是如何声明的。首先看几个简单的例子:
int   f; //一个整型变量
int   *f; //一个指向整型类型的指针
第2个声明是这样工作的:它把表达式 *f 声明为一个整数。根据这个事实,你肯定能推断出 f
是个指向整型的指针。C声明的这种解释方法可以通过下面的声明得到验证。
int   *f, g;
星号 * 是作用于 f 的,只有 f 才是一个指针。g 只是一个普通的整型变量。

下面是另一个例子,你以前曾见过:
int   f();
它把 f 声明为一个函数,它的返回值是一个整数。
下面是一个新例子:
int   *f();
要想推断出它的含义,你必须确定表达式 *f()
是如何进行求值的。首先执行的是函数调用操作符(),因为它的优先级高于间接访问操作符。因此,f
是一个函数,它的返回值类型是一个指向整型的指针。(用于声明变量的表达式和普通的表达式在求值时所使用的规则相同)
接下来的一个声明更为有趣:
int   (*f)();

确定括号的含义是分析这个声明的一个重要步骤。第2对括号是函数调用操作符,但第1对括号只起到聚组的作用。它迫使间接访问在函数调用之前进行,使
f
成为一个函数指针,它所指向的函数返回一个整型值。(程序中的每个函数都位于内存中的某个位置,所以存在指向那个位置的指针是完全可能的。??)
现在,下面这个声明应该是比较容易弄懂了:
int   *(*f)();
它和之前一个声明基本相同,f
也是一个函数指针,只是所指向的函数的返回值是一个整型指针,必须对其进行间接访问操作才能得到一个整型值。

现在,把数组也考虑进去。
int   f[];
这个声明表示 f 是个整型数组。
下面这个,
int   *f[];
下标的优先级更高,所以 f 是一个数组,它的元素类型是指向整型的指针。
int   f()[];
int   f[]();
以上两个都是非法的。但下面这个声明是合法的:
int   (*f[])();
括号内的表达式 *f[] 首先进行求值,所以 f 是一个元素为某种类型的指针的数组。表达式末尾的 ()
是函数调用操作符,所以 f 肯定是一个数组,数组元素的类型是函数指针,它所指向的函数的返回值是一个整型值。
搞清楚了上面最后一个声明,下面这个应该是比较容易了。
int   *(*f[])();
这个声明创建了一个指针数组,指针所指向的类型是返回值为整型指针的函数。

函数指针:
最常见的两个用途是转换表(jump table)和作为参数传递给另一个函数。
对函数指针执行间接访问之前必须把它初始化为指向某个函数:
int   f( int );
int   ( *pf )( int ) = &f;
初始化表达式中的 &
操作符是可选的,因为函数名被使用时总是由编译器把它转换为函数指针。&操作符只是显示地说明了编译器将隐式执行的任务。
在函数指针被声明并且初始化之后,我们就可以使用三种方式调用函数:
int   ans;
ans = f( 25 );
ans = ( *pf )( 25 );
ans = pf( 25 );

函数指针的应用实例:
回调函数、转移表

命令行参数:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: