指针是C语言最难的部分,是C语言的灵魂所在,C语言中常见的有一级指针和二级指针,一维数组和二维数组,今天就来简单的总结一下区别和用法。
1、说指针之前,先来看看下面这个例子。
#include
#include
int malloc_pointer(char *p)
{
if(p == NULL)
{
p = (char *)malloc(10);
}
return 0;
}
int free_pointer(char *p)
{
if(p != NULL)
{
free(p);
p = NULL;
}
return 0;
}
int main()
{
char* pointer1 = NULL;
malloc_pointer(pointer1);
printf("pointer1 is %s\n", pointer1 == NULL? "NULL":"NOT NULL");
char *pointer2 = malloc(10);
free_pointer(pointer2);
printf("pointer2 is %s\n", pointer2 == NULL? "NULL":"NOT NULL");
return 0;
}
最后打印的结果呢?当然是:
pointer1 is NULL
pointer2 is NOT NULL
如果对这个结果你感到奇怪的话,那么你很有必要接着看下去。
这里有必要对以下两点作一个简单的说明:
1、一级指针和二级指针的定义:
下面通过一幅图可以更清楚的理解他们之间的区别。
2、函数传递的参数会存在一个副本:
那么要是想在函数里去给指针分配内存或者释放内存,需要怎么做呢?当然就需要用二级指针。
将上面的函数变成如下函数:
#include
#include
int malloc_pointer(char **p)
{
if(*p == NULL)
{
*p = (char *)malloc(10);
}
return 0;
}
int free_pointer(char **p)
{
if(*p != NULL)
{
free(*p);
*p = NULL;
}
return 0;
}
int main()
{
char* pointer1 = NULL;
malloc_pointer(&pointer1);
printf("pointer1 is %s \n", pointer1 == NULL? "NULL":"NOT NULL");
char *pointer2 = malloc(10);
free_pointer(&pointer2);
printf("pointer2 is %s\n", pointer2 == NULL? "NULL":"NOT NULL");
return 0;
}
打印结果为:
pointer1 is NOT NULL
pointer2 is NULL
由此可见,传入二级指针来分配和释放内存是可行的。
因为虽然函数会给二级指针分配一个副本,但是*p不是副本,
*p跟传入的pointer1和pointer2就是同一个变量,操作*p即可达到目的。
一维数组在C语言里也是非常常见的,一维数组占用的是内存中一串连续的地址空间,可以通过" 变量名[下标] "来访问相应的值,也可以通过指针来访问。一维数组名代表数组首地址,也就是说一维数组名也就是一个一级指针。假设有数组char array[10] = {0},那么:
二维数组占用的也是内存中一串连续的地址空间,只不过维度上比一维数组多了一个,可以通过" 变量名[下标][下标] "来访问相应的值,也可以通过指针来访问,不过二维数组名和二级指针有所不同。假设有数组char array[10][10] = {0};
#include
#include
int array2(char (*p)[])
{
return 0;
}
int main()
{
char array[10][10] = {0};
int i = 0, j = 0;
for(i = 0; i < 10; i++)
{
for(j = 0; j < 10; j++)
array[i][j] = j;
}
printf("array = %X, array[0] = %X, &array[0] = %X\n", array, array[0], &array[0]);
printf("array[2]+5 = %d, *(*(array+2)+5) = %d\n", *(array[2] + 5),*(*(array+2)+5) );
array2(array);
return 0;
}
打印情况为:
array = 62FDA0, array[0] = 62FDA0, &array[0] = 62FDA0
array[2]+5 = 5, *(*(array+2)+5) = 5
二维数组寻址方式比较多,也比较容易搞混淆,还需多加理解和记忆。
数组指针,顾名思义就是指向数组的指针,定义方式:int (*p)[10] ;
这个括号不能省去,若写成int *p[10]就变成了接下来要说的指针数组。
int (*p)[10] 代表p是指向一个含有10个元素的一维数组,若给p分配内存,p = (int (*)[10])malloc(10); 那么p也就变成了一个含有十个元素的一位数组,此时等价于p[10][10]。这也就是上面二维数组讲到的,可按这种参数形式接收二维数组指针。
顾名思义,就是包含元素为指针的数组,定义方式:int *p[10]; 注意这里没有括号
int *p[10] 代表有十个元素的一维数组p , 每个元素里都是一个一级指针,例如p[0]、p[1]、… 、p[9]都是一个指向int型数据的一级指针。既然是指针,当然也就可以分配内存,分配之后就又可以当二维数组用了。
不过使用指针的写法会更好一点,有助于自己理解指针的用法,用数组的写法虽然比较方便,但是不利于消化指针的用法。
通过以上数组指针和指针数组的概念,引出函数指针和指针函数的概念
函数指针:指向函数的指针
正常函数指针的写法:
int (*pfun)(int a,int b);
定义一个函数指针pfun,可以指向一个int型的函数,参数也是两个int类型。比如:
int fun1(int a,int b)
{
printf("a=%d,b=%d\n");
return 0;
}
int main()
{
int (*pfun)(int a,int b);
pfun = fun1;
pfun(2,3); //会打印a=2,b=3
return 0;
}
使用typedef定义函数指针,是给函数指针取了一个别名。例如
typedef int (*pfun)(int a,int b);
这时候pfun相当于一个类型,而不是一个指针了。比如:
typedef int (*pfun)(int a,int b);
int fun1(int a,int b)
{
printf("a=%d,b=%d\n");
return 0;
}
int main()
{
pfun p1,p2;
p1 = fun1;
p2 = fun1;
p1(2,3);
p2(3,4);
return 0;
}
typedef给函数指针起别名,多用于回调函数,比如:
typedef int (*pfun)(int a,int b);
int fun1(int a,int b)
{
printf("a=%d,b=%d\n");
return 0;
}
int fun2(pfun cb)
{
cb(5,6); //会打印a=5,b=6.
return 0;
}
int main()
{
fun2(fun1);
return 0;
}
指针函数没有什么特别的,就比如定义一个函数:int *pfun(int a ,int b); 就代表一个指针函数,表明这个函数返回值是一个int 型指针,只是需要注意这个是不带括号的,记得和函数指针带括号区分开。
No pains, no gains.