指针
# 1 动态内存分配
# 1.1 malloc 和 free
分别用于执行动态内存分配和释放。这些函数维护一个可用内存池。malloc从内存池提取一块合适的内存,并返回一个指向这块内存的指针,可以自己初始化,也可以使用calloc函数;free把它还给内存池。
#inlude <stdlib.h>
void *malloc(size_t size); // 参数是需要分配的字节数,对每个从malloc返回的指针需要进行检查,确保不是NULL
void free(void *pointer);
free之后要记得将指针置零,指针p被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p成了“野指针”。如果此时不把p设置为NULL,会让人误以为p是个合法的指针。
如何杜绝野指针?
“野指针”不是NULL 指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if 语句很容易判断。但是“野指针”是很危险的,if 语句对它不起作用。
“野指针”的成因主要有两种:
(1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL 指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如
char *p = NULL;
char *str = (char *) malloc(100);
(2)指针p 被free 或者delete 之后,没有置为NULL,让人误以为p 是个合法的指针。
# 1.2 calloc 和 realloc
// 内存分配函数
void *calloc(size_t num_elements, size_t element_size); // 返回指向内存的指针之前把它初始化为0,参数为所需元素的数量和每个元素的字节数
void *realloc(void *ptr, size_t new_size); // 修改一个原先已经分配的内存块的大小
警告:
必须检查所请求的内存是否分配成功。
# 2 高级声明
int f; /* 一个整型变量 */
int *f; /* 一个指向整型的指针 */ // 表达式*f声明为一个整数
int f(); /* f声明为一个函数,它的返回值是一个整数 */
int *f(); /* f是一个函数,它的返回值类型是一个指向整型的指针 */
int (*f)(); /* f是一个函数指针,所指向的函数返回一个整型值 */
// 程序中每个函数都位于内存中的某个位置,所以存在指向那个位置的指针是完全可能的
int *(*f)(); /* f是一个函数指针,所指向的函数的返回值一个整型指针,必须对其进行间接访问操作才能得到一个整型值 */
int f[]; /* 一个整型数组 */
int *f[]; /* f是一个数组,它的元素类型是指向整型的指针 */
int f()[]; /* f是一个函数,它的返回值是一个整型数组,但这个声明是非法的,函数只能返回标量值,不能返回数组 */
int (*f[])();/* f是一个元素为某种类型的指针的数组,数组元素的类型是函数的指针,它所指向的函数的返回值是一个整型值 */
int *(*f[])(); /* 指针数组,指针所指向的类型是返回值为整型指针的函数 */
int (*f)(int, folat); /* f是函数指针,所指的函数接收两个参数,分别是一个整型值和浮点型值,并返回一个整型值 */
int *(*g[])(int, float); /* g是一个数组,数组的元素类型是一个函数指针,它所指向的函数接收两个参数,分别是一个整型值和浮点型值,并返回一个整型指针 */
# 3 函数指针
两个用途:转换表和作为参数传递给另一个函数
int f(int);
int (*fp)(int) = &p;
// 初始化表达式中的&操作符是可选的,因为函数名被使用时总是由编译器把它转换成函数指针。&操作符只是显式地说明了编译器将隐式执行的任务
int ans;
ans = f(25); /* 函数名f首先被转换成一个函数指针,该指针指定函数在内存中的位置,然后,函数调用操作符调用该函数,执行开始于这个地址的代码 */
ans = (*fp)(25); /* 对fp执行间接访问操作,它把函数指针转换成一个函数名,这个转换并不是真正需要的,因为编译器在执行函数调用操作符之前又会把它转换回去,不过这条语句的效果和第1条语句是完全一样的 */
ans = fp(25); /* 与前两条语句的效果是一样的,间接访问符并非必需,因为编译器需要的是一个函数指针,这个例子显示了函数指针通常是如何使用的 */
# 3.1 回调函数
// 在一个单链表中查找一个值
Node *
search_list(Node *node, int const value)
{
while(node != NULL)
{
if(node->value == value)
break;
node = node->link;
}
return node;
}
// 一种更为通用的方法是使查找函数与类型无关。不同类型的比较使用函数指针实现,调用者编写一个函数用于比较两个值,然后把一个指向这个函数的指针作为参数传递给查找函数,然后查找函数调用这个函数来执行值的比较。
// 回调函数:用户把一个函数指针作为参数传递给其他函数,后者将“回调”用户的函数
// void * 表示"一个指向未知类型的指针",使用时,它必须被强制转换成正确的类型。
// 在这个例子中,回调函数比较两个值。查找函数向比较函数传递两个指向需要进行比较的值的指针,并检查比较函数的返回值,例如。零表示相等的值,非零值表示不相等的值。
#include <stdio.h>
#include "node.h"
Node *
search_list(Node *node, void const *value,
int (*compare)(void const *, void const *))
{
while(node != NULL)
{
if(compare(&node->value, value) == 0)
break;
node = node->link;
}
return node;
}
// 比较函数
int
compare_ints(void const *a, void const *b)
{
if( *(int *)a == *(int *)b)
return 0;
else
return 1;
}
// 调用
desired_node = search_list(root, &desired_value, compare_ints);
desired_node = search_list(root, "desired_value", strcmp);
# 3.2 转换表/转移表
#include <stdio.h>
#include <string.h>
#define M 4
int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);
int div(int a, int b);
int (*oper_func[])(int, int) = {
add, sub, mul, div
};
char oper_sequence[M][10] = {
"add", "sub", "mul", "div"
};
int main()
{
char oper[10];
int seq;
int a,b;
int result;
int i;
printf("Operator:");
scanf("%s",oper);
printf("a:");
scanf("%d",&a);
printf("b:");
scanf("%d",&b);
for(i=0; i<M; i++)
{
if(strncmp(oper_sequence[i], oper, 3) == 0)
seq = i;
}
result = oper_func[seq](a, b);
printf("result is %d/n", result);
return 0;
}
int add(int a, int b)
{
return a+b;
}
int sub(int a, int b)
{
return a-b;
}
int mul(int a, int b)
{
return a*b;
}
int div(int a, int b)
{
return a/b;
}