void指针和NULL指针
void指针我们把它称为通用指针,就是可以指向任意类型的数据。也就是说,任何类型的指针都可以赋值给void指针。
void类型指针,不要直接给void指针进行解引用。
对一个NULL指针进行解引用是非法的,会引起段错误。
当你还不清楚将指针初始化为什么地址时,请将它初始化NULL;在对指针解引用时,先检查该指针是否为NULL。
NULL不是NUL 。NUL是ASCII字符表中的第一个字符。
NULL用于指针和对象,表示指向一个不被使用的地址,而’\0’表示字符串的结尾。
指向指针的指针
直接看代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a = 520;
int *p = &a;
int **pp = &p;
printf("a=%d, *p=%d, **pp=%d\n",a,*p,**pp);
printf("&a:%p, p:%p, *pp:%p\n",&a,p,*pp);
printf("Hello world!\n");
return 0;
}
printf("&a:%p, p:%p, *pp:%p\n",&a,p,*pp);
代码中:这三个所打印的值都是一样的。
&a:取a的地址
p: p是指针,指向了a,p中存的就是a的地址,直接以地址的形式打印p就是打印a的地址
pp:是二重指针,指向p的指针,那么pp中存放的是p的地址,对pp解引用一次得到的就是p的地址
指针数组和指向指针的指针
看程序好好体会下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i = 0;
//这是一个指针数组,它是数组,里边存放了6个指向字符串的指针
char *CLibrary[] =
{
"《C程序设计语言》",
"《C和指针》",
"《C primer plus》",
"《C编程专家》",
"《C陷阱与缺陷》",
"《带你学C带你飞》"
};
char **lib1 ;//指向指针的指针
char **lib2[5] ;//指向指针的指针数组。数组中存放的是指向指针的指针。
//我们理解下CLibrary[5],这个变量中存放的是字符串,也就是,是字符串的首地址,也就是指针。
//那么&CLibrary[5]就是指针的指针,跟lib1 的类型一致。很完美
lib1 = &CLibrary[5];
lib2[0] = &CLibrary[0];//好好体会这句。
lib2[1] = &CLibrary[1];
lib2[2] = &CLibrary[2];
lib2[3] = &CLibrary[3];
lib2[4] = &CLibrary[4];
//这里我们要打印字符串,那么就是需要地址。其实这里是以指针的方式来访问数组。
printf("小甲鱼出的C书籍:%s\n",*lib1);
printf("比较NB点的C语言书籍:\n");
for(i=0; i<5; i++)
{
//同样,我们需要打印字符串,需要的是地址,那么要对lib2[]解引用一次就刚好是地址。
printf("%s \n",*lib2[i]);
}
printf("Hello world!\n");
return 0;
}
我们使用指向指针的指针指向数组指针有两个优势:
(1)避免重复分配内存(不使用指针数组去存放)
(2)只需要进行一处修改(都存放在一起,只修改一处就可以)
这样,代码的灵活性和安全性都有了显著的提高。
数组指针和二维数组
首先,我们来看下边的程序, 用指针的方式访问以为数组是没有问题的。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
int array[] ={ 0,1,2,3,4,5,6,7,8,9 };
int *p = array;
for(i=0; i<10; i++)
{
printf("%3d",*(p+i));
printf("\n");
}
printf("Hello world!\n");
return 0;
}
再来看看下边的程序,如果我们要访问二维数组呢?是不是可以直接用双重指针就可以了呢?
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i,j;
int array[3][4] =
{
{0,1,2,3},
{4,5,6,7},
{8,9,10,11},
};
int **p= array;
for(i=0; i<3; i++)
{
for(j=0; j<4; j++)
{
printf("%3d",*(*(p+i)+j));//这里的跨度是不一样的。
}
printf("\n");
}
printf("Hello world!\n");
return 0;
}
上边的程序有没有什么问题呢?先思考一下。
首先是不能成功执行的。原因如下:
用程序来演示:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i,j;
int array[3][4] =
{
{0,1,2,3},
{4,5,6,7},
{8,9,10,11},
};
int **p = array;
/*
for(i=0; i<3; i++)
{
for(j=0; j<4; j++)
{
printf("%3d",*(*(p+i)+j));
}
printf("\n");
}
*/
printf("array的地址:%p , p的地址:%p\n",array,p);
printf("array+1的地址:%p p+1的地址:%p\n",array+1,p+1);
printf("Hello world!\n");
return 0;
}
我们能看到:
array+1举例array的跨度是:E0-F0 转换成十进制就是16个字节。
p+1距离p的跨度是:E0-E8转换成十进制是8个字节(这个是指针的长度,与硬件系统平台有关系,我的是win10 64位系统codeblock20.1平台,测试指针是8个字节)。
所以:我们这样访问是不对的。会发生段错误,导致程序奔溃。
我们用数值指针来进行对二维数组的访问。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i,j;
int array[3][4] =
{
{0,1,2,3},//第一行
{4,5,6,7},//第二行
{8,9,10,11},//第三行
};
int (*p)[4] = array;
//这里p是一个指向了4个元素的一维数组,所以这个p的跨度刚好是4*4=16个字节。
//所以p+1刚好就指向了第二行。array是指向二维数组的第一个元素的地址,array+1就是加到第二行了。
for(i=0; i<3; i++)
{
for(j=0; j<4; j++)
{
printf("%3d",*(*(p+i)+j));
}
printf("\n");
}
printf("Hello world!\n");
return 0;
}