指针数组与动态存储分配:
1、动态存储分配:
- c语言支持 动态存储分配, 在程序执行期间分配内存单元的能力。
- 三种声明在 <stdlib.h>头文件中的内存分配函数:
- malloc函数 :分配内存块,但不对内存块进行初始化。
- calloc函数:分配内存块,并且对内存块进行清零。
- realloc 函数——调整先前分配的内存块大小。
- 内存分配函数返回 void * 类型,它的值是“通用”指针,本质上它只是内存地址。
- 空指针:不指向任何地方的指针。调用内存分配函数时,找不到需要大的内存块时返回空指针。空指针用名为NULL的宏表示。C语言中所有非空指针都为真,只有空指针为假。
2、动态分配字符串:
-
malloc函数原型为:
-
void *malloc(size_t size);
-
malloc函数分配size个字节的内存块,并返回指向该内存块的指针。size_t是c语言中定义的无符号整数类型。
-
-
用malloc函数为字符串分配内存:
-
char *p = malloc(n + 1);// 为n个字符的字符串分配内存空间。为了给空字符留空间 char *p = (char *)malloc(n+1);
-
赋值操作时会将malloc函数返回的通用指针转化为char * 类型,不需要强制类型转换。
-
使用strcpy函数对调用malloc函数分配的内存进行初始化。
-
3、动态分配数组:
-
使用malloc函数为数组分配存储空间
-
区别:任意数组元素不像字符串那样是一个字节长度,需要使用sizeof运算符计算每个元素所需要的空间数量。
-
例子:使用malloc函数动态分配n个整数构成的数组:
-
int *a; a = malloc(n * sizeof(int)); // n可以在程序执行期间计算出来
-
一旦a指向动态分配的内存块,就可以将其用作数组的名字。
-
-
使用calloc函数为数组分配内存
-
calloc函数为 nmemb 个元素的数组分配内存空间,每个元素都是 size个字节,分配内存后,calloc函数会通过将所有为设置为0的方式进行初始化。
-
void *calloc(size_t nmemb,size_t size); struct point{int x, y;} *p; p = calloc(1,sizeof(struct point)); // p 指向一个结构,结构的成员x、y设置为0
-
-
使用realloc函数调整动态分配数组大小
-
调用realloc函数时,ptr必须指向通过malloc, calloc, realloc的调用获得的内存块,size表示内存块的新尺寸。
-
void *realloc(void *ptr,size_t size);
-
c标准对realloc的规则:
- 扩展内存使用realloc时,函数不会对添加到内存块的字节初始化。
- realloc不能按要求扩大内存时,会返回空指针,内存块原有数据不会改变。
- realloc第一个参数是空指针时,效果等价于malloc函数。
- realloc第二个参数是0时,它会释放掉内存。
-
在要求减少内存时,直接在原有基础上减少,扩展内存也在原有基础上扩展,除非扩展到的内存被占用,否则是不会通过重新复制来扩展的。
-
4、释放存储空间:
-
内存分配函数分配的内存来自称为堆(heap)的存储池。
-
不再访问到的内存块称为垃圾,留有垃圾的程序存在内存泄漏现象。
-
free函数
-
定义在<stdlib.h>中
-
void free(void *ptr);
-
只需要把指向不再需要的内存块的指针传递给free函数即可。必须是由内存分配函数返回的指针。
-
-
“悬空指针”
- 调用free§函数会释放p指向的内存块。但不会改变p本身。访问或修改释放掉的内存是严重错误。
5、函数指针:
-
指向函数的指针。
-
因为函数占用内存单元,每个函数都有地址。
-
函数指针指向函数而非对象。
-
函数类型由其 返回类型 和 形参类型 共同决定,与函数名无关。所以声明一个指向该函数的指针,只需要用指针代替函数名即可。
-
// 例如有比较两个string对象长度的函数 lengthCompare bool lengthCompare(const string &, const string & ); // 其函数类型是 bool(const string &, const string &) // 所以指向 lengthComapre 函数的指针声明为: bool (*pf) (const string &, const string &);
-
-
使用函数指针:
-
将函数名作为一个值使用时,函数自动转换为指针,像数组一样。
-
// 下面两句等价。 pf = lengthCompare; pf = &lengthCompare;
-
可以直接使用指针调用函数,而不必提前解引用。
-
// 以下三句函数调用等价。 bool b1 = pf("hello", "goodbye"); bool b2 = (*pf)("hello","goodbye"); bool b3 = lengthCompare("hello","goodbye");
-
指向不同函数类型的指针间不存在类型转化规则,可以为函数指针赋一个 nullptr 或 0 表示该指针没有指向任何一个函数。
-
如果定义了指向重载函数的指针,则指针类型必须与重载函数中的一个相匹配。
-
-
函数指针形参
-
不能定义函数类型的形参,但形参可以是指向函数的指针。
-
// 函数类型自动转换为指向函数的指针。 void useBigger(const string &s1, const string &s2, bool pf(const string &, const string &)); // 等价于显示的定义指向函数的指针。 void useBigger(const string &s1, const string &s2, bool (*pf)(const string &, const string &));
-
可以直接将函数作为实参使用,会自动转化为函数指针。
-
useBigger(s1,s2,lengthCompare);
-
就是说可以像python一样直接将函数作为形参、实参使用,编译器会自动将其转化为对应的指向函数的指针。
-
-
返回指向函数的指针
-
不能返回一个函数,但是可以返回指向函数类型的指针。
-
必须将返回类型写成指针形式,编译器不会自动将函数返回类型当成对应的指针类型处理。
-
声明返回函数指针的函数,可以使用类型别名简化书写
-
using F = int(int *, int); // F是一个函数类型 using PF = int(*)(int *, int); // PF是一个指向函数的指针类型。 PF f1(int); //声明一个返回指向函数指针的函数 f1; //等价于 int (*f1(int))(int *, int);
-
还可以使用尾置返回类型的方式声明一个返回啊函数指针的函数
-
auto f1(int) -> int(*)(int *, int);
-